affs.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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 NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
  129. unsigned offset, unsigned length),
  130. int pos, grub_size_t len, char *buf)
  131. {
  132. return grub_fshelp_read_file (node->data->disk, node, read_hook,
  133. pos, len, buf, grub_affs_read_block,
  134. node->size, 0);
  135. }
  136. static struct grub_affs_data *
  137. grub_affs_mount (grub_disk_t disk)
  138. {
  139. struct grub_affs_data *data;
  140. grub_uint32_t *rootblock = 0;
  141. struct grub_affs_rblock *rblock;
  142. int checksum = 0;
  143. int checksumr = 0;
  144. int blocksize = 0;
  145. data = grub_malloc (sizeof (struct grub_affs_data));
  146. if (!data)
  147. return 0;
  148. /* Read the bootblock. */
  149. grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
  150. &data->bblock);
  151. if (grub_errno)
  152. goto fail;
  153. /* Make sure this is an affs filesystem. */
  154. if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3))
  155. {
  156. grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
  157. goto fail;
  158. }
  159. /* Test if the filesystem is a OFS filesystem. */
  160. if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS))
  161. {
  162. grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported");
  163. goto fail;
  164. }
  165. /* Read the bootblock. */
  166. grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
  167. &data->bblock);
  168. if (grub_errno)
  169. goto fail;
  170. /* No sane person uses more than 8KB for a block. At least I hope
  171. for that person because in that case this won't work. */
  172. rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16);
  173. if (!rootblock)
  174. goto fail;
  175. rblock = (struct grub_affs_rblock *) rootblock;
  176. /* Read the rootblock. */
  177. grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0,
  178. GRUB_DISK_SECTOR_SIZE * 16, rootblock);
  179. if (grub_errno)
  180. goto fail;
  181. /* The filesystem blocksize is not stored anywhere in the filesystem
  182. itself. One way to determine it is reading blocks for the
  183. rootblock until the checksum is correct. */
  184. checksumr = grub_be_to_cpu32 (rblock->checksum);
  185. rblock->checksum = 0;
  186. for (blocksize = 0; blocksize < 8; blocksize++)
  187. {
  188. grub_uint32_t *currblock = rootblock + GRUB_DISK_SECTOR_SIZE * blocksize;
  189. unsigned int i;
  190. for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++)
  191. checksum += grub_be_to_cpu32 (currblock[i]);
  192. if (checksumr == -checksum)
  193. break;
  194. }
  195. if (-checksum != checksumr)
  196. {
  197. grub_error (GRUB_ERR_BAD_FS, "AFFS blocksize couldn't be determined");
  198. goto fail;
  199. }
  200. blocksize++;
  201. data->blocksize = blocksize;
  202. data->disk = disk;
  203. data->htsize = grub_be_to_cpu32 (rblock->htsize);
  204. data->diropen.data = data;
  205. data->diropen.block = (disk->total_sectors >> 1);
  206. grub_free (rootblock);
  207. return data;
  208. fail:
  209. if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
  210. grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
  211. grub_free (data);
  212. grub_free (rootblock);
  213. return 0;
  214. }
  215. static char *
  216. grub_affs_read_symlink (grub_fshelp_node_t node)
  217. {
  218. struct grub_affs_data *data = node->data;
  219. char *symlink;
  220. symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize));
  221. if (!symlink)
  222. return 0;
  223. grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET,
  224. GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink);
  225. if (grub_errno)
  226. {
  227. grub_free (symlink);
  228. return 0;
  229. }
  230. grub_dprintf ("affs", "Symlink: `%s'\n", symlink);
  231. return symlink;
  232. }
  233. static int
  234. grub_affs_iterate_dir (grub_fshelp_node_t dir,
  235. int NESTED_FUNC_ATTR
  236. (*hook) (const char *filename,
  237. enum grub_fshelp_filetype filetype,
  238. grub_fshelp_node_t node))
  239. {
  240. int i;
  241. struct grub_affs_file file;
  242. struct grub_fshelp_node *node = 0;
  243. struct grub_affs_data *data = dir->data;
  244. grub_uint32_t *hashtable;
  245. auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
  246. int size, int type);
  247. int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
  248. int size, int type)
  249. {
  250. node = grub_malloc (sizeof (*node));
  251. if (!node)
  252. {
  253. grub_free (hashtable);
  254. return 1;
  255. }
  256. node->data = data;
  257. node->size = size;
  258. node->block = block;
  259. node->parent = grub_be_to_cpu32 (file.parent);
  260. if (hook (name, type, node))
  261. {
  262. grub_free (hashtable);
  263. return 1;
  264. }
  265. return 0;
  266. }
  267. hashtable = grub_malloc (data->htsize * sizeof (*hashtable));
  268. if (!hashtable)
  269. return 1;
  270. grub_disk_read (data->disk, dir->block, GRUB_AFFS_HASHTABLE_OFFSET,
  271. data->htsize * sizeof (*hashtable), (char *) hashtable);
  272. if (grub_errno)
  273. goto fail;
  274. /* Create the directory entries for `.' and `..'. */
  275. if (grub_affs_create_node (".", dir->block, dir->size, GRUB_FSHELP_DIR))
  276. return 1;
  277. if (grub_affs_create_node ("..", dir->parent ? dir->parent : dir->block,
  278. dir->size, GRUB_FSHELP_DIR))
  279. return 1;
  280. for (i = 0; i < data->htsize; i++)
  281. {
  282. enum grub_fshelp_filetype type;
  283. grub_uint64_t next;
  284. if (!hashtable[i])
  285. continue;
  286. /* Every entry in the hashtable can be chained. Read the entire
  287. chain. */
  288. next = grub_be_to_cpu32 (hashtable[i]);
  289. while (next)
  290. {
  291. grub_disk_read (data->disk, next + data->blocksize - 1,
  292. data->blocksize * GRUB_DISK_SECTOR_SIZE
  293. - GRUB_AFFS_FILE_LOCATION,
  294. sizeof (file), (char *) &file);
  295. if (grub_errno)
  296. goto fail;
  297. file.name[file.namelen] = '\0';
  298. if ((int) grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_DIR)
  299. type = GRUB_FSHELP_REG;
  300. else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_REG)
  301. type = GRUB_FSHELP_DIR;
  302. else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_SYMLINK)
  303. type = GRUB_FSHELP_SYMLINK;
  304. else
  305. type = GRUB_FSHELP_UNKNOWN;
  306. if (grub_affs_create_node ((char *) (file.name), next,
  307. grub_be_to_cpu32 (file.size), type))
  308. return 1;
  309. next = grub_be_to_cpu32 (file.next);
  310. }
  311. }
  312. grub_free (hashtable);
  313. return 0;
  314. fail:
  315. grub_free (node);
  316. grub_free (hashtable);
  317. return 0;
  318. }
  319. /* Open a file named NAME and initialize FILE. */
  320. static grub_err_t
  321. grub_affs_open (struct grub_file *file, const char *name)
  322. {
  323. struct grub_affs_data *data;
  324. struct grub_fshelp_node *fdiro = 0;
  325. grub_dl_ref (my_mod);
  326. data = grub_affs_mount (file->device->disk);
  327. if (!data)
  328. goto fail;
  329. grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir,
  330. grub_affs_read_symlink, GRUB_FSHELP_REG);
  331. if (grub_errno)
  332. goto fail;
  333. file->size = fdiro->size;
  334. data->diropen = *fdiro;
  335. grub_free (fdiro);
  336. file->data = data;
  337. file->offset = 0;
  338. return 0;
  339. fail:
  340. if (data && fdiro != &data->diropen)
  341. grub_free (fdiro);
  342. grub_free (data);
  343. grub_dl_unref (my_mod);
  344. return grub_errno;
  345. }
  346. static grub_err_t
  347. grub_affs_close (grub_file_t file)
  348. {
  349. grub_free (file->data);
  350. grub_dl_unref (my_mod);
  351. return GRUB_ERR_NONE;
  352. }
  353. /* Read LEN bytes data from FILE into BUF. */
  354. static grub_ssize_t
  355. grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
  356. {
  357. struct grub_affs_data *data =
  358. (struct grub_affs_data *) file->data;
  359. int size = grub_affs_read_file (&data->diropen, file->read_hook,
  360. file->offset, len, buf);
  361. return size;
  362. }
  363. static grub_err_t
  364. grub_affs_dir (grub_device_t device, const char *path,
  365. int (*hook) (const char *filename,
  366. const struct grub_dirhook_info *info))
  367. {
  368. struct grub_affs_data *data = 0;
  369. struct grub_fshelp_node *fdiro = 0;
  370. auto int NESTED_FUNC_ATTR iterate (const char *filename,
  371. enum grub_fshelp_filetype filetype,
  372. grub_fshelp_node_t node);
  373. int NESTED_FUNC_ATTR iterate (const char *filename,
  374. enum grub_fshelp_filetype filetype,
  375. grub_fshelp_node_t node)
  376. {
  377. struct grub_dirhook_info info;
  378. grub_memset (&info, 0, sizeof (info));
  379. info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
  380. grub_free (node);
  381. return hook (filename, &info);
  382. }
  383. grub_dl_ref (my_mod);
  384. data = grub_affs_mount (device->disk);
  385. if (!data)
  386. goto fail;
  387. grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir,
  388. grub_affs_read_symlink, GRUB_FSHELP_DIR);
  389. if (grub_errno)
  390. goto fail;
  391. grub_affs_iterate_dir (fdiro, iterate);
  392. fail:
  393. if (data && fdiro != &data->diropen)
  394. grub_free (fdiro);
  395. grub_free (data);
  396. grub_dl_unref (my_mod);
  397. return grub_errno;
  398. }
  399. static grub_err_t
  400. grub_affs_label (grub_device_t device, char **label)
  401. {
  402. struct grub_affs_data *data;
  403. struct grub_affs_file file;
  404. grub_disk_t disk = device->disk;
  405. grub_dl_ref (my_mod);
  406. data = grub_affs_mount (disk);
  407. if (data)
  408. {
  409. /* The rootblock maps quite well on a file header block, it's
  410. something we can use here. */
  411. grub_disk_read (data->disk, disk->total_sectors >> 1,
  412. data->blocksize * (GRUB_DISK_SECTOR_SIZE
  413. - GRUB_AFFS_FILE_LOCATION),
  414. sizeof (file), &file);
  415. if (grub_errno)
  416. return 0;
  417. *label = grub_strndup ((char *) (file.name), file.namelen);
  418. }
  419. else
  420. *label = 0;
  421. grub_dl_unref (my_mod);
  422. grub_free (data);
  423. return grub_errno;
  424. }
  425. static struct grub_fs grub_affs_fs =
  426. {
  427. .name = "affs",
  428. .dir = grub_affs_dir,
  429. .open = grub_affs_open,
  430. .read = grub_affs_read,
  431. .close = grub_affs_close,
  432. .label = grub_affs_label,
  433. .next = 0
  434. };
  435. GRUB_MOD_INIT(affs)
  436. {
  437. grub_fs_register (&grub_affs_fs);
  438. my_mod = mod;
  439. }
  440. GRUB_MOD_FINI(affs)
  441. {
  442. grub_fs_unregister (&grub_affs_fs);
  443. }