affs.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /* affs.c - Amiga Fast 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. /* The affs bootblock. */
  28. struct grub_affs_bblock
  29. {
  30. grub_uint8_t type[3];
  31. grub_uint8_t flags;
  32. grub_uint32_t checksum;
  33. grub_uint32_t rootblock;
  34. } __attribute__ ((packed));
  35. /* Set if the filesystem is a AFFS filesystem. Otherwise this is an
  36. OFS filesystem. */
  37. #define GRUB_AFFS_FLAG_FFS 1
  38. /* The affs rootblock. */
  39. struct grub_affs_rblock
  40. {
  41. grub_uint8_t type[4];
  42. grub_uint8_t unused1[8];
  43. grub_uint32_t htsize;
  44. grub_uint32_t unused2;
  45. grub_uint32_t checksum;
  46. grub_uint32_t hashtable[1];
  47. } __attribute__ ((packed));
  48. /* The second part of a file header block. */
  49. struct grub_affs_file
  50. {
  51. grub_uint8_t unused1[12];
  52. grub_uint32_t size;
  53. grub_uint8_t unused2[104];
  54. grub_uint8_t namelen;
  55. grub_uint8_t name[30];
  56. grub_uint8_t unused3[33];
  57. grub_uint32_t next;
  58. grub_uint32_t parent;
  59. grub_uint32_t extension;
  60. grub_int32_t type;
  61. } __attribute__ ((packed));
  62. /* The location of `struct grub_affs_file' relative to the end of a
  63. file header block. */
  64. #define GRUB_AFFS_FILE_LOCATION 200
  65. /* The offset in both the rootblock and the file header block for the
  66. hashtable, symlink and block pointers (all synonyms). */
  67. #define GRUB_AFFS_HASHTABLE_OFFSET 24
  68. #define GRUB_AFFS_BLOCKPTR_OFFSET 24
  69. #define GRUB_AFFS_SYMLINK_OFFSET 24
  70. #define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225)
  71. #define GRUB_AFFS_FILETYPE_DIR -3
  72. #define GRUB_AFFS_FILETYPE_REG 2
  73. #define GRUB_AFFS_FILETYPE_SYMLINK 3
  74. struct grub_fshelp_node
  75. {
  76. struct grub_affs_data *data;
  77. int block;
  78. int size;
  79. int parent;
  80. };
  81. /* Information about a "mounted" affs filesystem. */
  82. struct grub_affs_data
  83. {
  84. struct grub_affs_bblock bblock;
  85. struct grub_fshelp_node diropen;
  86. grub_disk_t disk;
  87. /* Blocksize in sectors. */
  88. int blocksize;
  89. /* The number of entries in the hashtable. */
  90. int htsize;
  91. };
  92. static grub_dl_t my_mod;
  93. static grub_disk_addr_t
  94. grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
  95. {
  96. int links;
  97. grub_uint32_t pos;
  98. int block = node->block;
  99. struct grub_affs_file file;
  100. struct grub_affs_data *data = node->data;
  101. grub_uint32_t mod;
  102. /* Find the block that points to the fileblock we are looking up by
  103. following the chain until the right table is reached. */
  104. for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--)
  105. {
  106. grub_disk_read (data->disk, block + data->blocksize - 1,
  107. data->blocksize * (GRUB_DISK_SECTOR_SIZE
  108. - GRUB_AFFS_FILE_LOCATION),
  109. sizeof (file), &file);
  110. if (grub_errno)
  111. return 0;
  112. block = grub_be_to_cpu32 (file.extension);
  113. }
  114. /* Translate the fileblock to the block within the right table. */
  115. fileblock = mod;
  116. grub_disk_read (data->disk, block,
  117. GRUB_AFFS_BLOCKPTR_OFFSET
  118. + (data->htsize - fileblock - 1) * sizeof (pos),
  119. sizeof (pos), &pos);
  120. if (grub_errno)
  121. return 0;
  122. return grub_be_to_cpu32 (pos);
  123. }
  124. /* Read LEN bytes from the file described by DATA starting with byte
  125. POS. Return the amount of read bytes in READ. */
  126. static grub_ssize_t
  127. grub_affs_read_file (grub_fshelp_node_t node,
  128. void (*read_hook) (grub_disk_addr_t sector,
  129. unsigned offset, unsigned length,
  130. void *closure),
  131. void *closure, int flags,
  132. int pos, grub_size_t len, char *buf)
  133. {
  134. return grub_fshelp_read_file (node->data->disk, node, read_hook, closure,
  135. flags, pos, len, buf, grub_affs_read_block,
  136. node->size, 0);
  137. }
  138. static struct grub_affs_data *
  139. grub_affs_mount (grub_disk_t disk)
  140. {
  141. struct grub_affs_data *data;
  142. grub_uint32_t *rootblock = 0;
  143. struct grub_affs_rblock *rblock;
  144. int checksum = 0;
  145. int checksumr = 0;
  146. int blocksize = 0;
  147. data = grub_malloc (sizeof (struct grub_affs_data));
  148. if (!data)
  149. return 0;
  150. /* Read the bootblock. */
  151. grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
  152. &data->bblock);
  153. if (grub_errno)
  154. goto fail;
  155. /* Make sure this is an affs filesystem. */
  156. if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3))
  157. {
  158. grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
  159. goto fail;
  160. }
  161. /* Test if the filesystem is a OFS filesystem. */
  162. if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS))
  163. {
  164. grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported");
  165. goto fail;
  166. }
  167. /* Read the bootblock. */
  168. grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
  169. &data->bblock);
  170. if (grub_errno)
  171. goto fail;
  172. /* No sane person uses more than 8KB for a block. At least I hope
  173. for that person because in that case this won't work. */
  174. rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16);
  175. if (!rootblock)
  176. goto fail;
  177. rblock = (struct grub_affs_rblock *) rootblock;
  178. /* Read the rootblock. */
  179. grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0,
  180. GRUB_DISK_SECTOR_SIZE * 16, rootblock);
  181. if (grub_errno)
  182. goto fail;
  183. /* The filesystem blocksize is not stored anywhere in the filesystem
  184. itself. One way to determine it is reading blocks for the
  185. rootblock until the checksum is correct. */
  186. checksumr = grub_be_to_cpu32 (rblock->checksum);
  187. rblock->checksum = 0;
  188. for (blocksize = 0; blocksize < 8; blocksize++)
  189. {
  190. grub_uint32_t *currblock = rootblock + GRUB_DISK_SECTOR_SIZE * blocksize;
  191. unsigned int i;
  192. for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++)
  193. checksum += grub_be_to_cpu32 (currblock[i]);
  194. if (checksumr == -checksum)
  195. break;
  196. }
  197. if (-checksum != checksumr)
  198. {
  199. grub_error (GRUB_ERR_BAD_FS, "AFFS blocksize couldn't be determined");
  200. goto fail;
  201. }
  202. blocksize++;
  203. data->blocksize = blocksize;
  204. data->disk = disk;
  205. data->htsize = grub_be_to_cpu32 (rblock->htsize);
  206. data->diropen.data = data;
  207. data->diropen.block = (disk->total_sectors >> 1);
  208. grub_free (rootblock);
  209. return data;
  210. fail:
  211. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  212. grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
  213. grub_free (data);
  214. grub_free (rootblock);
  215. return 0;
  216. }
  217. static char *
  218. grub_affs_read_symlink (grub_fshelp_node_t node)
  219. {
  220. struct grub_affs_data *data = node->data;
  221. char *symlink;
  222. symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize));
  223. if (!symlink)
  224. return 0;
  225. grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET,
  226. GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink);
  227. if (grub_errno)
  228. {
  229. grub_free (symlink);
  230. return 0;
  231. }
  232. grub_dprintf ("affs", "Symlink: `%s'\n", symlink);
  233. return symlink;
  234. }
  235. struct grub_affs_iterate_dir_closure
  236. {
  237. int (*hook) (const char *filename,
  238. enum grub_fshelp_filetype filetype,
  239. grub_fshelp_node_t node, void *closure);
  240. void *closure;
  241. struct grub_fshelp_node *node;
  242. struct grub_affs_data *data;
  243. struct grub_affs_file *file;
  244. grub_uint32_t *hashtable;
  245. };
  246. static int
  247. grub_affs_create_node (const char *name, int block,
  248. int size, int type,
  249. struct grub_affs_iterate_dir_closure *c)
  250. {
  251. struct grub_fshelp_node *node;
  252. node = grub_malloc (sizeof (*node));
  253. c->node = node;
  254. if (!node)
  255. {
  256. grub_free (c->hashtable);
  257. return 1;
  258. }
  259. node->data = c->data;
  260. node->size = size;
  261. node->block = block;
  262. node->parent = grub_be_to_cpu32 (c->file->parent);
  263. if (c->hook (name, type, node, c->closure))
  264. {
  265. grub_free (c->hashtable);
  266. return 1;
  267. }
  268. return 0;
  269. }
  270. static int
  271. grub_affs_iterate_dir (grub_fshelp_node_t dir,
  272. int (*hook) (const char *filename,
  273. enum grub_fshelp_filetype filetype,
  274. grub_fshelp_node_t node, void *closure),
  275. void *closure)
  276. {
  277. int i;
  278. struct grub_affs_file file;
  279. struct grub_affs_data *data = dir->data;
  280. grub_uint32_t *hashtable;
  281. struct grub_affs_iterate_dir_closure c;
  282. c.hook = hook;
  283. c.closure = closure;
  284. c.node = 0;
  285. c.data = data;
  286. c.file = &file;
  287. hashtable = grub_malloc (data->htsize * sizeof (*hashtable));
  288. c.hashtable = hashtable;
  289. if (!hashtable)
  290. return 1;
  291. grub_disk_read (data->disk, dir->block, GRUB_AFFS_HASHTABLE_OFFSET,
  292. data->htsize * sizeof (*hashtable), (char *) hashtable);
  293. if (grub_errno)
  294. goto fail;
  295. /* Create the directory entries for `.' and `..'. */
  296. if (grub_affs_create_node (".", dir->block, dir->size, GRUB_FSHELP_DIR, &c))
  297. return 1;
  298. if (grub_affs_create_node ("..", dir->parent ? dir->parent : dir->block,
  299. dir->size, GRUB_FSHELP_DIR, &c))
  300. return 1;
  301. for (i = 0; i < data->htsize; i++)
  302. {
  303. enum grub_fshelp_filetype type;
  304. grub_uint64_t next;
  305. if (!hashtable[i])
  306. continue;
  307. /* Every entry in the hashtable can be chained. Read the entire
  308. chain. */
  309. next = grub_be_to_cpu32 (hashtable[i]);
  310. while (next)
  311. {
  312. grub_disk_read (data->disk, next + data->blocksize - 1,
  313. data->blocksize * GRUB_DISK_SECTOR_SIZE
  314. - GRUB_AFFS_FILE_LOCATION,
  315. sizeof (file), (char *) &file);
  316. if (grub_errno)
  317. goto fail;
  318. file.name[file.namelen] = '\0';
  319. if ((int) grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_DIR)
  320. type = GRUB_FSHELP_REG;
  321. else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_REG)
  322. type = GRUB_FSHELP_DIR;
  323. else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_SYMLINK)
  324. type = GRUB_FSHELP_SYMLINK;
  325. else
  326. type = GRUB_FSHELP_UNKNOWN;
  327. if (grub_affs_create_node ((char *) (file.name), next,
  328. grub_be_to_cpu32 (file.size), type, &c))
  329. return 1;
  330. next = grub_be_to_cpu32 (file.next);
  331. }
  332. }
  333. grub_free (hashtable);
  334. return 0;
  335. fail:
  336. grub_free (c.node);
  337. grub_free (hashtable);
  338. return 0;
  339. }
  340. /* Open a file named NAME and initialize FILE. */
  341. static grub_err_t
  342. grub_affs_open (struct grub_file *file, const char *name)
  343. {
  344. struct grub_affs_data *data;
  345. struct grub_fshelp_node *fdiro = 0;
  346. grub_dl_ref (my_mod);
  347. data = grub_affs_mount (file->device->disk);
  348. if (!data)
  349. goto fail;
  350. grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir, 0,
  351. grub_affs_read_symlink, GRUB_FSHELP_REG);
  352. if (grub_errno)
  353. goto fail;
  354. file->size = fdiro->size;
  355. data->diropen = *fdiro;
  356. grub_free (fdiro);
  357. file->data = data;
  358. file->offset = 0;
  359. return 0;
  360. fail:
  361. if (data && fdiro != &data->diropen)
  362. grub_free (fdiro);
  363. grub_free (data);
  364. grub_dl_unref (my_mod);
  365. return grub_errno;
  366. }
  367. static grub_err_t
  368. grub_affs_close (grub_file_t file)
  369. {
  370. grub_free (file->data);
  371. grub_dl_unref (my_mod);
  372. return GRUB_ERR_NONE;
  373. }
  374. /* Read LEN bytes data from FILE into BUF. */
  375. static grub_ssize_t
  376. grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
  377. {
  378. struct grub_affs_data *data =
  379. (struct grub_affs_data *) file->data;
  380. int size = grub_affs_read_file (&data->diropen, file->read_hook,
  381. file->closure, file->flags,
  382. file->offset, len, buf);
  383. return size;
  384. }
  385. struct grub_affs_dir_closure
  386. {
  387. int (*hook) (const char *filename,
  388. const struct grub_dirhook_info *info, void *closure);
  389. void *closure;
  390. };
  391. static int
  392. iterate (const char *filename,
  393. enum grub_fshelp_filetype filetype,
  394. grub_fshelp_node_t node,
  395. void *closure)
  396. {
  397. struct grub_affs_dir_closure *c = closure;
  398. struct grub_dirhook_info info;
  399. grub_memset (&info, 0, sizeof (info));
  400. info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  401. grub_free (node);
  402. return c->hook (filename, &info, c->closure);
  403. }
  404. static grub_err_t
  405. grub_affs_dir (grub_device_t device, const char *path,
  406. int (*hook) (const char *filename,
  407. const struct grub_dirhook_info *info, void *closure),
  408. void *closure)
  409. {
  410. struct grub_affs_data *data = 0;
  411. struct grub_fshelp_node *fdiro = 0;
  412. struct grub_affs_dir_closure c;
  413. grub_dl_ref (my_mod);
  414. data = grub_affs_mount (device->disk);
  415. if (!data)
  416. goto fail;
  417. grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir, 0,
  418. grub_affs_read_symlink, GRUB_FSHELP_DIR);
  419. if (grub_errno)
  420. goto fail;
  421. c.hook = hook;
  422. c.closure = closure;
  423. grub_affs_iterate_dir (fdiro, iterate, &c);
  424. fail:
  425. if (data && fdiro != &data->diropen)
  426. grub_free (fdiro);
  427. grub_free (data);
  428. grub_dl_unref (my_mod);
  429. return grub_errno;
  430. }
  431. static grub_err_t
  432. grub_affs_label (grub_device_t device, char **label)
  433. {
  434. struct grub_affs_data *data;
  435. struct grub_affs_file file;
  436. grub_disk_t disk = device->disk;
  437. grub_dl_ref (my_mod);
  438. data = grub_affs_mount (disk);
  439. if (data)
  440. {
  441. /* The rootblock maps quite well on a file header block, it's
  442. something we can use here. */
  443. grub_disk_read (data->disk, disk->total_sectors >> 1,
  444. data->blocksize * (GRUB_DISK_SECTOR_SIZE
  445. - GRUB_AFFS_FILE_LOCATION),
  446. sizeof (file), &file);
  447. if (grub_errno)
  448. return 0;
  449. *label = grub_strndup ((char *) (file.name), file.namelen);
  450. }
  451. else
  452. *label = 0;
  453. grub_dl_unref (my_mod);
  454. grub_free (data);
  455. return grub_errno;
  456. }
  457. static struct grub_fs grub_affs_fs =
  458. {
  459. .name = "affs",
  460. .dir = grub_affs_dir,
  461. .open = grub_affs_open,
  462. .read = grub_affs_read,
  463. .close = grub_affs_close,
  464. .label = grub_affs_label,
  465. .next = 0
  466. };
  467. GRUB_MOD_INIT(affs)
  468. {
  469. grub_fs_register (&grub_affs_fs);
  470. my_mod = mod;
  471. }
  472. GRUB_MOD_FINI(affs)
  473. {
  474. grub_fs_unregister (&grub_affs_fs);
  475. }