sfs.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. /* sfs.c - Amiga Smart FileSystem. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2005,2006,2007,2008,2009 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/file.h>
  21. #include <grub/mm.h>
  22. #include <grub/misc.h>
  23. #include <grub/disk.h>
  24. #include <grub/dl.h>
  25. #include <grub/types.h>
  26. #include <grub/fshelp.h>
  27. #include <grub/charset.h>
  28. #include <grub/lockdown.h>
  29. #include <grub/safemath.h>
  30. GRUB_MOD_LICENSE ("GPLv3+");
  31. /* The common header for a block. */
  32. struct grub_sfs_bheader
  33. {
  34. grub_uint8_t magic[4];
  35. grub_uint32_t chksum;
  36. grub_uint32_t ipointtomyself;
  37. } GRUB_PACKED;
  38. /* The sfs rootblock. */
  39. struct grub_sfs_rblock
  40. {
  41. struct grub_sfs_bheader header;
  42. grub_uint32_t version;
  43. grub_uint32_t createtime;
  44. grub_uint8_t flags;
  45. grub_uint8_t unused1[31];
  46. grub_uint32_t blocksize;
  47. grub_uint8_t unused2[40];
  48. grub_uint8_t unused3[8];
  49. grub_uint32_t rootobject;
  50. grub_uint32_t btree;
  51. } GRUB_PACKED;
  52. enum
  53. {
  54. FLAGS_CASE_SENSITIVE = 0x80
  55. };
  56. /* A SFS object container. */
  57. struct grub_sfs_obj
  58. {
  59. grub_uint8_t unused1[4];
  60. grub_uint32_t nodeid;
  61. grub_uint8_t unused2[4];
  62. union
  63. {
  64. struct
  65. {
  66. grub_uint32_t first_block;
  67. grub_uint32_t size;
  68. } GRUB_PACKED file;
  69. struct
  70. {
  71. grub_uint32_t hashtable;
  72. grub_uint32_t dir_objc;
  73. } GRUB_PACKED dir;
  74. } file_dir;
  75. grub_uint32_t mtime;
  76. grub_uint8_t type;
  77. grub_uint8_t filename[1];
  78. grub_uint8_t comment[1];
  79. } GRUB_PACKED;
  80. #define GRUB_SFS_TYPE_DELETED 32
  81. #define GRUB_SFS_TYPE_SYMLINK 64
  82. #define GRUB_SFS_TYPE_DIR 128
  83. /* A SFS object container. */
  84. struct grub_sfs_objc
  85. {
  86. struct grub_sfs_bheader header;
  87. grub_uint32_t parent;
  88. grub_uint32_t next;
  89. grub_uint32_t prev;
  90. /* The amount of objects depends on the blocksize. */
  91. struct grub_sfs_obj objects[1];
  92. } GRUB_PACKED;
  93. struct grub_sfs_btree_node
  94. {
  95. grub_uint32_t key;
  96. grub_uint32_t data;
  97. } GRUB_PACKED;
  98. struct grub_sfs_btree_extent
  99. {
  100. grub_uint32_t key;
  101. grub_uint32_t next;
  102. grub_uint32_t prev;
  103. grub_uint16_t size;
  104. } GRUB_PACKED;
  105. struct grub_sfs_btree
  106. {
  107. struct grub_sfs_bheader header;
  108. grub_uint16_t nodes;
  109. grub_uint8_t leaf;
  110. grub_uint8_t nodesize;
  111. /* Normally this can be kind of node, but just extents are
  112. supported. */
  113. struct grub_sfs_btree_node node[1];
  114. } GRUB_PACKED;
  115. struct cache_entry
  116. {
  117. grub_uint32_t off;
  118. grub_uint32_t block;
  119. };
  120. struct grub_fshelp_node
  121. {
  122. struct grub_sfs_data *data;
  123. grub_uint32_t block;
  124. grub_uint32_t size;
  125. grub_uint32_t mtime;
  126. grub_uint32_t cache_off;
  127. grub_uint32_t next_extent;
  128. grub_size_t cache_allocated;
  129. grub_size_t cache_size;
  130. struct cache_entry *cache;
  131. };
  132. /* Information about a "mounted" sfs filesystem. */
  133. struct grub_sfs_data
  134. {
  135. struct grub_sfs_rblock rblock;
  136. struct grub_fshelp_node diropen;
  137. grub_disk_t disk;
  138. /* Log of blocksize in sectors. */
  139. int log_blocksize;
  140. int fshelp_flags;
  141. /* Label of the filesystem. */
  142. char *label;
  143. };
  144. static grub_dl_t my_mod;
  145. /* Lookup the extent starting with BLOCK in the filesystem described
  146. by DATA. Return the extent size in SIZE and the following extent
  147. in NEXTEXT. */
  148. static grub_err_t
  149. grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
  150. grub_uint32_t *size, grub_uint32_t *nextext)
  151. {
  152. char *treeblock;
  153. struct grub_sfs_btree *tree;
  154. int i;
  155. grub_uint32_t next;
  156. grub_size_t blocksize = GRUB_DISK_SECTOR_SIZE << data->log_blocksize;
  157. treeblock = grub_malloc (blocksize);
  158. if (!treeblock)
  159. return grub_errno;
  160. next = grub_be_to_cpu32 (data->rblock.btree);
  161. tree = (struct grub_sfs_btree *) treeblock;
  162. /* Handle this level in the btree. */
  163. do
  164. {
  165. grub_uint16_t nnodes;
  166. grub_disk_read (data->disk,
  167. ((grub_disk_addr_t) next) << data->log_blocksize,
  168. 0, blocksize, treeblock);
  169. if (grub_errno)
  170. {
  171. grub_free (treeblock);
  172. return grub_errno;
  173. }
  174. nnodes = grub_be_to_cpu16 (tree->nodes);
  175. if (nnodes * (grub_uint32_t) (tree)->nodesize > blocksize)
  176. break;
  177. for (i = (int) nnodes - 1; i >= 0; i--)
  178. {
  179. #define EXTNODE(tree, index) \
  180. ((struct grub_sfs_btree_node *) (((char *) &(tree)->node[0]) \
  181. + (index) * (tree)->nodesize))
  182. /* Follow the tree down to the leaf level. */
  183. if ((grub_be_to_cpu32 (EXTNODE(tree, i)->key) <= block)
  184. && !tree->leaf)
  185. {
  186. next = grub_be_to_cpu32 (EXTNODE (tree, i)->data);
  187. break;
  188. }
  189. /* If the leaf level is reached, just find the correct extent. */
  190. if (grub_be_to_cpu32 (EXTNODE (tree, i)->key) == block && tree->leaf)
  191. {
  192. struct grub_sfs_btree_extent *extent;
  193. extent = (struct grub_sfs_btree_extent *) EXTNODE (tree, i);
  194. /* We found a correct leaf. */
  195. *size = grub_be_to_cpu16 (extent->size);
  196. *nextext = grub_be_to_cpu32 (extent->next);
  197. grub_free (treeblock);
  198. return 0;
  199. }
  200. #undef EXTNODE
  201. }
  202. } while (!tree->leaf);
  203. grub_free (treeblock);
  204. return grub_error (GRUB_ERR_FILE_READ_ERROR, "SFS extent not found");
  205. }
  206. static grub_disk_addr_t
  207. grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
  208. {
  209. grub_uint32_t blk;
  210. grub_uint32_t size = 0;
  211. grub_uint32_t next = 0;
  212. grub_disk_addr_t off;
  213. struct grub_sfs_data *data = node->data;
  214. /* In case of the first block we don't have to lookup the
  215. extent, the minimum size is always 1. */
  216. if (fileblock == 0)
  217. return node->block;
  218. if (!node->cache)
  219. {
  220. grub_size_t cache_size;
  221. /* Assume half-max extents (32768 sectors). */
  222. cache_size = ((node->size >> (data->log_blocksize + GRUB_DISK_SECTOR_BITS
  223. + 15))
  224. + 3);
  225. if (cache_size < 8)
  226. cache_size = 8;
  227. node->cache_off = 0;
  228. node->next_extent = node->block;
  229. node->cache_size = 0;
  230. node->cache = grub_calloc (cache_size, sizeof (node->cache[0]));
  231. if (!node->cache)
  232. {
  233. grub_errno = 0;
  234. node->cache_allocated = 0;
  235. }
  236. else
  237. {
  238. node->cache_allocated = cache_size;
  239. node->cache[0].off = 0;
  240. node->cache[0].block = node->block;
  241. }
  242. }
  243. if (fileblock < node->cache_off)
  244. {
  245. unsigned int i = 0;
  246. int j, lg;
  247. for (lg = 0; node->cache_size >> lg; lg++);
  248. for (j = lg - 1; j >= 0; j--)
  249. if ((i | (1 << j)) < node->cache_size
  250. && node->cache[(i | (1 << j))].off <= fileblock)
  251. i |= (1 << j);
  252. return node->cache[i].block + fileblock - node->cache[i].off;
  253. }
  254. off = node->cache_off;
  255. blk = node->next_extent;
  256. while (blk)
  257. {
  258. grub_err_t err;
  259. err = grub_sfs_read_extent (node->data, blk, &size, &next);
  260. if (err)
  261. return 0;
  262. if (node->cache && node->cache_size >= node->cache_allocated)
  263. {
  264. struct cache_entry *e = node->cache;
  265. grub_size_t sz;
  266. if (grub_mul (node->cache_allocated, 2 * sizeof (e[0]), &sz))
  267. goto fail;
  268. e = grub_realloc (node->cache, sz);
  269. if (!e)
  270. {
  271. fail:
  272. grub_errno = 0;
  273. grub_free (node->cache);
  274. node->cache = 0;
  275. }
  276. else
  277. {
  278. node->cache_allocated *= 2;
  279. node->cache = e;
  280. }
  281. }
  282. if (node->cache)
  283. {
  284. node->cache_off = off + size;
  285. node->next_extent = next;
  286. node->cache[node->cache_size].off = off;
  287. node->cache[node->cache_size].block = blk;
  288. node->cache_size++;
  289. }
  290. if (fileblock - off < size)
  291. return fileblock - off + blk;
  292. off += size;
  293. blk = next;
  294. }
  295. grub_error (GRUB_ERR_FILE_READ_ERROR,
  296. "reading a SFS block outside the extent");
  297. return 0;
  298. }
  299. /* Read LEN bytes from the file described by DATA starting with byte
  300. POS. Return the amount of read bytes in READ. */
  301. static grub_ssize_t
  302. grub_sfs_read_file (grub_fshelp_node_t node,
  303. grub_disk_read_hook_t read_hook, void *read_hook_data,
  304. grub_off_t pos, grub_size_t len, char *buf)
  305. {
  306. return grub_fshelp_read_file (node->data->disk, node,
  307. read_hook, read_hook_data,
  308. pos, len, buf, grub_sfs_read_block,
  309. node->size, node->data->log_blocksize, 0);
  310. }
  311. static struct grub_sfs_data *
  312. grub_sfs_mount (grub_disk_t disk)
  313. {
  314. struct grub_sfs_data *data;
  315. struct grub_sfs_objc *rootobjc;
  316. char *rootobjc_data = 0;
  317. grub_uint32_t blk;
  318. unsigned int max_len;
  319. data = grub_malloc (sizeof (*data));
  320. if (!data)
  321. return 0;
  322. /* Read the rootblock. */
  323. grub_disk_read (disk, 0, 0, sizeof (struct grub_sfs_rblock),
  324. &data->rblock);
  325. if (grub_errno)
  326. goto fail;
  327. /* Make sure this is a sfs filesystem. */
  328. if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4)
  329. || data->rblock.blocksize == 0
  330. || (data->rblock.blocksize & (data->rblock.blocksize - 1)) != 0
  331. || (data->rblock.blocksize & grub_cpu_to_be32_compile_time (0xf00001ff)))
  332. {
  333. grub_error (GRUB_ERR_BAD_FS, "not a SFS filesystem");
  334. goto fail;
  335. }
  336. for (data->log_blocksize = 9;
  337. (1U << data->log_blocksize) < grub_be_to_cpu32 (data->rblock.blocksize);
  338. data->log_blocksize++);
  339. data->log_blocksize -= GRUB_DISK_SECTOR_BITS;
  340. if (data->rblock.flags & FLAGS_CASE_SENSITIVE)
  341. data->fshelp_flags = 0;
  342. else
  343. data->fshelp_flags = GRUB_FSHELP_CASE_INSENSITIVE;
  344. rootobjc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
  345. if (! rootobjc_data)
  346. goto fail;
  347. /* Read the root object container. */
  348. grub_disk_read (disk, ((grub_disk_addr_t) grub_be_to_cpu32 (data->rblock.rootobject))
  349. << data->log_blocksize, 0,
  350. GRUB_DISK_SECTOR_SIZE << data->log_blocksize, rootobjc_data);
  351. if (grub_errno)
  352. goto fail;
  353. rootobjc = (struct grub_sfs_objc *) rootobjc_data;
  354. blk = grub_be_to_cpu32 (rootobjc->objects[0].file_dir.dir.dir_objc);
  355. data->diropen.size = 0;
  356. data->diropen.block = blk;
  357. data->diropen.data = data;
  358. data->diropen.cache = 0;
  359. data->disk = disk;
  360. /* We only read 1 block of data, so truncate the name if needed. */
  361. max_len = ((GRUB_DISK_SECTOR_SIZE << data->log_blocksize)
  362. - 24 /* offsetof (struct grub_sfs_objc, objects) */
  363. - 25); /* offsetof (struct grub_sfs_obj, filename) */
  364. data->label = grub_zalloc (max_len + 1);
  365. if (data->label == NULL)
  366. goto fail;
  367. grub_strncpy (data->label, (char *) rootobjc->objects[0].filename, max_len);
  368. grub_free (rootobjc_data);
  369. return data;
  370. fail:
  371. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  372. grub_error (GRUB_ERR_BAD_FS, "not an SFS filesystem");
  373. grub_free (data);
  374. grub_free (rootobjc_data);
  375. return 0;
  376. }
  377. static char *
  378. grub_sfs_read_symlink (grub_fshelp_node_t node)
  379. {
  380. struct grub_sfs_data *data = node->data;
  381. char *symlink;
  382. char *block;
  383. block = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
  384. if (!block)
  385. return 0;
  386. grub_disk_read (data->disk, ((grub_disk_addr_t) node->block)
  387. << data->log_blocksize,
  388. 0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize, block);
  389. if (grub_errno)
  390. {
  391. grub_free (block);
  392. return 0;
  393. }
  394. /* This is just a wild guess, but it always worked for me. How the
  395. SLNK block looks like is not documented in the SFS docs. */
  396. symlink = grub_malloc (((GRUB_DISK_SECTOR_SIZE << data->log_blocksize)
  397. - 24) * GRUB_MAX_UTF8_PER_LATIN1 + 1);
  398. if (!symlink)
  399. {
  400. grub_free (block);
  401. return 0;
  402. }
  403. *grub_latin1_to_utf8 ((grub_uint8_t *) symlink, (grub_uint8_t *) &block[24],
  404. (GRUB_DISK_SECTOR_SIZE << data->log_blocksize) - 24) = '\0';
  405. grub_free (block);
  406. return symlink;
  407. }
  408. /* Helper for grub_sfs_iterate_dir. */
  409. static int
  410. grub_sfs_create_node (struct grub_fshelp_node **node,
  411. struct grub_sfs_data *data,
  412. const char *name,
  413. grub_uint32_t block, grub_uint32_t size, int type,
  414. grub_uint32_t mtime,
  415. grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
  416. {
  417. grub_size_t len = grub_strlen (name);
  418. grub_uint8_t *name_u8;
  419. int ret;
  420. grub_size_t sz;
  421. if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) ||
  422. grub_add (sz, 1, &sz))
  423. return 1;
  424. *node = grub_malloc (sizeof (**node));
  425. if (!*node)
  426. return 1;
  427. name_u8 = grub_malloc (sz);
  428. if (!name_u8)
  429. {
  430. grub_free (*node);
  431. return 1;
  432. }
  433. (*node)->data = data;
  434. (*node)->size = size;
  435. (*node)->block = block;
  436. (*node)->mtime = mtime;
  437. (*node)->cache = 0;
  438. (*node)->cache_off = 0;
  439. (*node)->next_extent = block;
  440. (*node)->cache_size = 0;
  441. (*node)->cache_allocated = 0;
  442. *grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0';
  443. ret = hook ((char *) name_u8, type | data->fshelp_flags, *node, hook_data);
  444. grub_free (name_u8);
  445. return ret;
  446. }
  447. static int
  448. grub_sfs_iterate_dir (grub_fshelp_node_t dir,
  449. grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
  450. {
  451. struct grub_fshelp_node *node = 0;
  452. struct grub_sfs_data *data = dir->data;
  453. char *objc_data;
  454. struct grub_sfs_objc *objc;
  455. unsigned int next = dir->block;
  456. grub_uint32_t pos;
  457. objc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
  458. if (!objc_data)
  459. goto fail;
  460. /* The Object container can consist of multiple blocks, iterate over
  461. every block. */
  462. while (next)
  463. {
  464. grub_disk_read (data->disk, ((grub_disk_addr_t) next)
  465. << data->log_blocksize, 0,
  466. GRUB_DISK_SECTOR_SIZE << data->log_blocksize, objc_data);
  467. if (grub_errno)
  468. goto fail;
  469. objc = (struct grub_sfs_objc *) objc_data;
  470. pos = (char *) &objc->objects[0] - (char *) objc;
  471. /* Iterate over all entries in this block. */
  472. while (pos + sizeof (struct grub_sfs_obj)
  473. < (1U << (GRUB_DISK_SECTOR_BITS + data->log_blocksize)))
  474. {
  475. struct grub_sfs_obj *obj;
  476. obj = (struct grub_sfs_obj *) ((char *) objc + pos);
  477. const char *filename = (const char *) obj->filename;
  478. grub_size_t len;
  479. enum grub_fshelp_filetype type;
  480. grub_uint32_t block;
  481. /* The filename and comment dynamically increase the size of
  482. the object. */
  483. len = grub_strlen (filename);
  484. len += grub_strlen (filename + len + 1);
  485. pos += sizeof (*obj) + len;
  486. /* Round up to a multiple of two bytes. */
  487. pos = ((pos + 1) >> 1) << 1;
  488. if (filename[0] == 0)
  489. continue;
  490. /* First check if the file was not deleted. */
  491. if (obj->type & GRUB_SFS_TYPE_DELETED)
  492. continue;
  493. else if (obj->type & GRUB_SFS_TYPE_SYMLINK)
  494. type = GRUB_FSHELP_SYMLINK;
  495. else if (obj->type & GRUB_SFS_TYPE_DIR)
  496. type = GRUB_FSHELP_DIR;
  497. else
  498. type = GRUB_FSHELP_REG;
  499. if (type == GRUB_FSHELP_DIR)
  500. block = grub_be_to_cpu32 (obj->file_dir.dir.dir_objc);
  501. else
  502. block = grub_be_to_cpu32 (obj->file_dir.file.first_block);
  503. if (grub_sfs_create_node (&node, data, filename, block,
  504. grub_be_to_cpu32 (obj->file_dir.file.size),
  505. type, grub_be_to_cpu32 (obj->mtime),
  506. hook, hook_data))
  507. {
  508. grub_free (objc_data);
  509. return 1;
  510. }
  511. }
  512. next = grub_be_to_cpu32 (objc->next);
  513. }
  514. fail:
  515. grub_free (objc_data);
  516. return 0;
  517. }
  518. /* Open a file named NAME and initialize FILE. */
  519. static grub_err_t
  520. grub_sfs_open (struct grub_file *file, const char *name)
  521. {
  522. struct grub_sfs_data *data;
  523. struct grub_fshelp_node *fdiro = 0;
  524. grub_dl_ref (my_mod);
  525. data = grub_sfs_mount (file->device->disk);
  526. if (!data)
  527. goto fail;
  528. grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_sfs_iterate_dir,
  529. grub_sfs_read_symlink, GRUB_FSHELP_REG);
  530. if (grub_errno)
  531. goto fail;
  532. file->size = fdiro->size;
  533. data->diropen = *fdiro;
  534. grub_free (fdiro);
  535. file->data = data;
  536. file->offset = 0;
  537. return 0;
  538. fail:
  539. if (data && fdiro != &data->diropen)
  540. grub_free (fdiro);
  541. if (data)
  542. grub_free (data->label);
  543. grub_free (data);
  544. grub_dl_unref (my_mod);
  545. return grub_errno;
  546. }
  547. static grub_err_t
  548. grub_sfs_close (grub_file_t file)
  549. {
  550. struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
  551. grub_free (data->diropen.cache);
  552. grub_free (data->label);
  553. grub_free (data);
  554. grub_dl_unref (my_mod);
  555. return GRUB_ERR_NONE;
  556. }
  557. /* Read LEN bytes data from FILE into BUF. */
  558. static grub_ssize_t
  559. grub_sfs_read (grub_file_t file, char *buf, grub_size_t len)
  560. {
  561. struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
  562. return grub_sfs_read_file (&data->diropen,
  563. file->read_hook, file->read_hook_data,
  564. file->offset, len, buf);
  565. }
  566. /* Context for grub_sfs_dir. */
  567. struct grub_sfs_dir_ctx
  568. {
  569. grub_fs_dir_hook_t hook;
  570. void *hook_data;
  571. };
  572. /* Helper for grub_sfs_dir. */
  573. static int
  574. grub_sfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
  575. grub_fshelp_node_t node, void *data)
  576. {
  577. struct grub_sfs_dir_ctx *ctx = data;
  578. struct grub_dirhook_info info;
  579. grub_memset (&info, 0, sizeof (info));
  580. info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  581. info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
  582. info.mtimeset = 1;
  583. grub_free (node->cache);
  584. grub_free (node);
  585. return ctx->hook (filename, &info, ctx->hook_data);
  586. }
  587. static grub_err_t
  588. grub_sfs_dir (grub_device_t device, const char *path,
  589. grub_fs_dir_hook_t hook, void *hook_data)
  590. {
  591. struct grub_sfs_dir_ctx ctx = { hook, hook_data };
  592. struct grub_sfs_data *data = 0;
  593. struct grub_fshelp_node *fdiro = 0;
  594. grub_dl_ref (my_mod);
  595. data = grub_sfs_mount (device->disk);
  596. if (!data)
  597. goto fail;
  598. grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_sfs_iterate_dir,
  599. grub_sfs_read_symlink, GRUB_FSHELP_DIR);
  600. if (grub_errno)
  601. goto fail;
  602. grub_sfs_iterate_dir (fdiro, grub_sfs_dir_iter, &ctx);
  603. fail:
  604. if (data && fdiro != &data->diropen)
  605. grub_free (fdiro);
  606. if (data)
  607. grub_free (data->label);
  608. grub_free (data);
  609. grub_dl_unref (my_mod);
  610. return grub_errno;
  611. }
  612. static grub_err_t
  613. grub_sfs_label (grub_device_t device, char **label)
  614. {
  615. struct grub_sfs_data *data;
  616. grub_disk_t disk = device->disk;
  617. data = grub_sfs_mount (disk);
  618. if (data)
  619. {
  620. grub_size_t sz, len = grub_strlen (data->label);
  621. if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) ||
  622. grub_add (sz, 1, &sz))
  623. return GRUB_ERR_OUT_OF_RANGE;
  624. *label = grub_malloc (sz);
  625. if (*label)
  626. *grub_latin1_to_utf8 ((grub_uint8_t *) *label,
  627. (const grub_uint8_t *) data->label,
  628. len) = '\0';
  629. grub_free (data->label);
  630. }
  631. grub_free (data);
  632. return grub_errno;
  633. }
  634. static struct grub_fs grub_sfs_fs =
  635. {
  636. .name = "sfs",
  637. .fs_dir = grub_sfs_dir,
  638. .fs_open = grub_sfs_open,
  639. .fs_read = grub_sfs_read,
  640. .fs_close = grub_sfs_close,
  641. .fs_label = grub_sfs_label,
  642. #ifdef GRUB_UTIL
  643. .reserved_first_sector = 0,
  644. .blocklist_install = 1,
  645. #endif
  646. .next = 0
  647. };
  648. GRUB_MOD_INIT(sfs)
  649. {
  650. if (!grub_is_lockdown ())
  651. {
  652. grub_sfs_fs.mod = mod;
  653. grub_fs_register (&grub_sfs_fs);
  654. }
  655. my_mod = mod;
  656. }
  657. GRUB_MOD_FINI(sfs)
  658. {
  659. if (!grub_is_lockdown ())
  660. grub_fs_unregister (&grub_sfs_fs);
  661. }