fshelp.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /* fshelp.c -- Filesystem helper functions */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2004,2005,2006,2007,2008 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/err.h>
  20. #include <grub/mm.h>
  21. #include <grub/misc.h>
  22. #include <grub/disk.h>
  23. #include <grub/fshelp.h>
  24. GRUB_EXPORT(grub_fshelp_find_file);
  25. GRUB_EXPORT(grub_fshelp_log2blksize);
  26. GRUB_EXPORT(grub_fshelp_read_file);
  27. struct grub_fshelp_find_file_closure
  28. {
  29. grub_fshelp_node_t rootnode;
  30. int (*iterate_dir) (grub_fshelp_node_t dir,
  31. int (*hook)
  32. (const char *filename,
  33. enum grub_fshelp_filetype filetype,
  34. grub_fshelp_node_t node, void *closure),
  35. void *closure);
  36. void *closure;
  37. char *(*read_symlink) (grub_fshelp_node_t node);
  38. int symlinknest;
  39. enum grub_fshelp_filetype foundtype;
  40. grub_fshelp_node_t currroot;
  41. };
  42. static void
  43. free_node (grub_fshelp_node_t node, struct grub_fshelp_find_file_closure *c)
  44. {
  45. if (node != c->rootnode && node != c->currroot)
  46. grub_free (node);
  47. }
  48. struct find_file_closure
  49. {
  50. char *name;
  51. enum grub_fshelp_filetype *type;
  52. grub_fshelp_node_t *oldnode;
  53. grub_fshelp_node_t *currnode;
  54. };
  55. static int
  56. iterate (const char *filename,
  57. enum grub_fshelp_filetype filetype,
  58. grub_fshelp_node_t node,
  59. void *closure)
  60. {
  61. struct find_file_closure *c = closure;
  62. if (filetype == GRUB_FSHELP_UNKNOWN ||
  63. (grub_strcmp (c->name, filename) &&
  64. (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
  65. grub_strncasecmp (c->name, filename, GRUB_LONG_MAX))))
  66. {
  67. grub_free (node);
  68. return 0;
  69. }
  70. /* The node is found, stop iterating over the nodes. */
  71. *(c->type) = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
  72. *(c->oldnode) = *(c->currnode);
  73. *(c->currnode) = node;
  74. return 1;
  75. }
  76. static grub_err_t
  77. find_file (const char *currpath, grub_fshelp_node_t currroot,
  78. grub_fshelp_node_t *currfound,
  79. struct grub_fshelp_find_file_closure *c)
  80. {
  81. char fpath[grub_strlen (currpath) + 1];
  82. char *name = fpath;
  83. char *next;
  84. enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
  85. grub_fshelp_node_t currnode = currroot;
  86. grub_fshelp_node_t oldnode = currroot;
  87. c->currroot = currroot;
  88. grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
  89. /* Remove all leading slashes. */
  90. while (*name == '/')
  91. name++;
  92. if (! *name)
  93. {
  94. *currfound = currnode;
  95. return 0;
  96. }
  97. for (;;)
  98. {
  99. int found;
  100. struct find_file_closure cc;
  101. /* Extract the actual part from the pathname. */
  102. next = grub_strchr (name, '/');
  103. if (next)
  104. {
  105. /* Remove all leading slashes. */
  106. while (*next == '/')
  107. *(next++) = '\0';
  108. }
  109. /* At this point it is expected that the current node is a
  110. directory, check if this is true. */
  111. if (type != GRUB_FSHELP_DIR)
  112. {
  113. free_node (currnode, c);
  114. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
  115. }
  116. cc.name = name;
  117. cc.type = &type;
  118. cc.oldnode = &oldnode;
  119. cc.currnode = &currnode;
  120. /* Iterate over the directory. */
  121. found = c->iterate_dir (currnode, iterate, &cc);
  122. if (! found)
  123. {
  124. if (grub_errno)
  125. return grub_errno;
  126. break;
  127. }
  128. /* Read in the symlink and follow it. */
  129. if (type == GRUB_FSHELP_SYMLINK)
  130. {
  131. char *symlink;
  132. /* Test if the symlink does not loop. */
  133. if (++(c->symlinknest) == 8)
  134. {
  135. free_node (currnode, c);
  136. free_node (oldnode, c);
  137. return grub_error (GRUB_ERR_SYMLINK_LOOP,
  138. "too deep nesting of symlinks");
  139. }
  140. symlink = c->read_symlink (currnode);
  141. free_node (currnode, c);
  142. if (!symlink)
  143. {
  144. free_node (oldnode, c);
  145. return grub_errno;
  146. }
  147. /* The symlink is an absolute path, go back to the root inode. */
  148. if (symlink[0] == '/')
  149. {
  150. free_node (oldnode, c);
  151. oldnode = c->rootnode;
  152. }
  153. /* Lookup the node the symlink points to. */
  154. find_file (symlink, oldnode, &currnode, c);
  155. type = c->foundtype;
  156. grub_free (symlink);
  157. if (grub_errno)
  158. {
  159. free_node (oldnode, c);
  160. return grub_errno;
  161. }
  162. }
  163. free_node (oldnode, c);
  164. /* Found the node! */
  165. if (! next || *next == '\0')
  166. {
  167. *currfound = currnode;
  168. c->foundtype = type;
  169. return 0;
  170. }
  171. name = next;
  172. }
  173. return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
  174. }
  175. /* Lookup the node PATH. The node ROOTNODE describes the root of the
  176. directory tree. The node found is returned in FOUNDNODE, which is
  177. either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to
  178. iterate over all directory entries in the current node.
  179. READ_SYMLINK is used to read the symlink if a node is a symlink.
  180. EXPECTTYPE is the type node that is expected by the called, an
  181. error is generated if the node is not of the expected type. Make
  182. sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
  183. because GCC has a nasty bug when using regparm=3. */
  184. grub_err_t
  185. grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
  186. grub_fshelp_node_t *foundnode,
  187. int (*iterate_dir) (grub_fshelp_node_t dir,
  188. int (*hook)
  189. (const char *filename,
  190. enum grub_fshelp_filetype filetype,
  191. grub_fshelp_node_t node,
  192. void *closure),
  193. void *closure),
  194. void *closure,
  195. char *(*read_symlink) (grub_fshelp_node_t node),
  196. enum grub_fshelp_filetype expecttype)
  197. {
  198. grub_err_t err;
  199. struct grub_fshelp_find_file_closure c;
  200. c.rootnode = rootnode;
  201. c.iterate_dir = iterate_dir;
  202. c.closure = closure;
  203. c.read_symlink = read_symlink;
  204. c.symlinknest = 0;
  205. c.foundtype = GRUB_FSHELP_DIR;
  206. if (!path || path[0] != '/')
  207. {
  208. grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
  209. return grub_errno;
  210. }
  211. err = find_file (path, rootnode, foundnode, &c);
  212. if (err)
  213. return err;
  214. /* Check if the node that was found was of the expected type. */
  215. if (expecttype == GRUB_FSHELP_REG && c.foundtype != expecttype)
  216. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
  217. else if (expecttype == GRUB_FSHELP_DIR && c.foundtype != expecttype)
  218. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
  219. return 0;
  220. }
  221. /* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
  222. beginning with the block POS. READ_HOOK should be set before
  223. reading a block from the file. GET_BLOCK is used to translate file
  224. blocks to disk blocks. The file is FILESIZE bytes big and the
  225. blocks have a size of LOG2BLOCKSIZE (in log2). */
  226. grub_ssize_t
  227. grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
  228. void (*read_hook) (grub_disk_addr_t sector,
  229. unsigned offset,
  230. unsigned length,
  231. void *closure),
  232. void *closure, int flags,
  233. grub_off_t pos, grub_size_t len, char *buf,
  234. grub_disk_addr_t (*get_block) (grub_fshelp_node_t node,
  235. grub_disk_addr_t block),
  236. grub_off_t filesize, int log2blocksize)
  237. {
  238. grub_disk_addr_t i, blockcnt;
  239. int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
  240. /* Adjust LEN so it we can't read past the end of the file. */
  241. if (pos + len > filesize)
  242. len = filesize - pos;
  243. blockcnt = ((len + pos) + blocksize - 1) >>
  244. (log2blocksize + GRUB_DISK_SECTOR_BITS);
  245. for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++)
  246. {
  247. grub_disk_addr_t blknr;
  248. int blockoff = pos & (blocksize - 1);
  249. int blockend = blocksize;
  250. int skipfirst = 0;
  251. blknr = get_block (node, i);
  252. if (grub_errno)
  253. return -1;
  254. blknr = blknr << log2blocksize;
  255. /* Last block. */
  256. if (i == blockcnt - 1)
  257. {
  258. blockend = (len + pos) & (blocksize - 1);
  259. /* The last portion is exactly blocksize. */
  260. if (! blockend)
  261. blockend = blocksize;
  262. }
  263. /* First block. */
  264. if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS)))
  265. {
  266. skipfirst = blockoff;
  267. blockend -= skipfirst;
  268. }
  269. /* If the block number is 0 this block is not stored on disk but
  270. is zero filled instead. */
  271. if (blknr)
  272. {
  273. disk->read_hook = read_hook;
  274. disk->closure = closure;
  275. grub_disk_read_ex (disk, blknr, skipfirst, blockend, buf, flags);
  276. disk->read_hook = 0;
  277. if (grub_errno)
  278. return -1;
  279. }
  280. else if (buf)
  281. grub_memset (buf, 0, blockend);
  282. if (buf)
  283. buf += blocksize - skipfirst;
  284. }
  285. return len;
  286. }
  287. unsigned int
  288. grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow)
  289. {
  290. int mod;
  291. *pow = 0;
  292. while (blksize > 1)
  293. {
  294. mod = blksize - ((blksize >> 1) << 1);
  295. blksize >>= 1;
  296. /* Check if it really is a power of two. */
  297. if (mod)
  298. return grub_error (GRUB_ERR_BAD_NUMBER,
  299. "the blocksize is not a power of two");
  300. (*pow)++;
  301. }
  302. return GRUB_ERR_NONE;
  303. }