make the linux-ppc packags be in synch with other platforms
[tangerine.git] / arch / common / boot / grub2 / fs / affs.c
blob2f87e0a1ef3b4f6ff7630eff69b55ba5762bebec
1 /* affs.c - Amiga Fast FileSystem. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
28 #ifdef __AROS__
29 #include <grub/partition.h>
30 #endif
32 /* The affs bootblock. */
33 struct grub_affs_bblock
35 grub_uint8_t type[3];
36 grub_uint8_t flags;
37 grub_uint32_t checksum;
38 grub_uint32_t rootblock;
39 } __attribute__ ((packed));
41 /* Set if the filesystem is a AFFS filesystem. Otherwise this is an
42 OFS filesystem. */
43 #define GRUB_AFFS_FLAG_FFS 1
45 /* The affs rootblock. */
46 struct grub_affs_rblock
48 grub_uint8_t type[4];
49 grub_uint8_t unused1[8];
50 grub_uint32_t htsize;
51 grub_uint32_t unused2;
52 grub_uint32_t checksum;
53 grub_uint32_t hashtable[1];
54 } __attribute__ ((packed));
56 /* The second part of a file header block. */
57 struct grub_affs_file
59 grub_uint8_t unused1[12];
60 grub_uint32_t size;
61 grub_uint8_t unused2[104];
62 grub_uint8_t namelen;
63 grub_uint8_t name[30];
64 grub_uint8_t unused3[33];
65 grub_uint32_t next;
66 grub_uint32_t parent;
67 grub_uint32_t extension;
68 grub_int32_t type;
69 } __attribute__ ((packed));
71 /* The location of `struct grub_affs_file' relative to the end of a
72 file header block. */
73 #define GRUB_AFFS_FILE_LOCATION 200
75 /* The offset in both the rootblock and the file header block for the
76 hashtable, symlink and block pointers (all synonyms). */
77 #define GRUB_AFFS_HASHTABLE_OFFSET 24
78 #define GRUB_AFFS_BLOCKPTR_OFFSET 24
79 #define GRUB_AFFS_SYMLINK_OFFSET 24
81 #define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225)
83 #define GRUB_AFFS_FILETYPE_DIR -3
84 #define GRUB_AFFS_FILETYPE_REG 2
85 #define GRUB_AFFS_FILETYPE_SYMLINK 3
88 struct grub_fshelp_node
90 struct grub_affs_data *data;
91 int block;
92 int size;
93 int parent;
96 /* Information about a "mounted" affs filesystem. */
97 struct grub_affs_data
99 struct grub_affs_bblock bblock;
100 struct grub_fshelp_node diropen;
101 grub_disk_t disk;
103 /* Blocksize in sectors. */
104 int blocksize;
106 /* The number of entries in the hashtable. */
107 int htsize;
110 #ifndef GRUB_UTIL
111 static grub_dl_t my_mod;
112 #endif
115 static int
116 grub_affs_read_block (grub_fshelp_node_t node, int fileblock)
118 int links;
119 grub_uint32_t pos;
120 int block = node->block;
121 struct grub_affs_file file;
122 struct grub_affs_data *data = node->data;
124 /* Find the block that points to the fileblock we are looking up by
125 following the chain until the right table is reached. */
126 for (links = fileblock / (data->htsize); links; links--)
128 grub_disk_read (data->disk, block + data->blocksize - 1,
129 data->blocksize * (GRUB_DISK_SECTOR_SIZE
130 - GRUB_AFFS_FILE_LOCATION),
131 sizeof (file), (char *) &file);
132 if (grub_errno)
133 return 0;
135 block = grub_be_to_cpu32 (file.extension);
138 /* Translate the fileblock to the block within the right table. */
139 fileblock = fileblock % (data->htsize);
140 grub_disk_read (data->disk, block,
141 GRUB_AFFS_BLOCKPTR_OFFSET
142 + (data->htsize - fileblock - 1) * sizeof (pos),
143 sizeof (pos), (char *) &pos);
144 if (grub_errno)
145 return 0;
147 return grub_be_to_cpu32 (pos);
151 /* Read LEN bytes from the file described by DATA starting with byte
152 POS. Return the amount of read bytes in READ. */
153 static grub_ssize_t
154 grub_affs_read_file (grub_fshelp_node_t node,
155 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
156 unsigned offset, unsigned length),
157 int pos, grub_size_t len, char *buf)
159 return grub_fshelp_read_file (node->data->disk, node, read_hook,
160 pos, len, buf, grub_affs_read_block,
161 node->size, 0);
165 static struct grub_affs_data *
166 grub_affs_mount (grub_disk_t disk)
168 struct grub_affs_data *data;
169 grub_uint32_t *rootblock = 0;
170 struct grub_affs_rblock *rblock;
172 int checksum = 0;
173 int checksumr = 0;
174 int blocksize = 0;
176 data = grub_malloc (sizeof (struct grub_affs_data));
177 if (!data)
178 return 0;
180 /* Read the bootblock. */
181 grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
182 (char *) &data->bblock);
183 if (grub_errno)
184 goto fail;
186 /* Make sure this is an affs filesystem. */
187 if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3))
189 grub_error (GRUB_ERR_BAD_FS, "not an affs filesystem");
190 goto fail;
193 /* Test if the filesystem is a OFS filesystem. */
194 if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS))
196 grub_error (GRUB_ERR_BAD_FS, "ofs not yet supported");
197 goto fail;
200 /* Read the bootblock. */
201 grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
202 (char *) &data->bblock);
203 if (grub_errno)
204 goto fail;
206 /* No sane person uses more than 8KB for a block. At least I hope
207 for that person because in that case this won't work. */
208 rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16);
209 if (!rootblock)
210 goto fail;
212 rblock = (struct grub_affs_rblock *) rootblock;
214 /* Read the rootblock. */
215 #ifdef __AROS__
216 grub_uint64_t reservedblocks = 2;
217 grub_uint64_t countblocks;
218 if (disk->partition)
219 countblocks = disk->partition->len;
220 else
221 countblocks = disk->total_sectors;
223 grub_disk_addr_t rblknum = (countblocks - 1 + reservedblocks) / 2;
225 grub_disk_read (disk, rblknum, 0,
226 GRUB_DISK_SECTOR_SIZE * 16, (char *) rootblock);
227 #else
228 grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0,
229 GRUB_DISK_SECTOR_SIZE * 16, (char *) rootblock);
230 #endif
231 if (grub_errno)
232 goto fail;
234 /* The filesystem blocksize is not stored anywhere in the filesystem
235 itself. One way to determine it is reading blocks for the
236 rootblock until the checksum is correct. */
237 checksumr = grub_be_to_cpu32 (rblock->checksum);
238 rblock->checksum = 0;
239 for (blocksize = 0; blocksize < 8; blocksize++)
241 grub_uint32_t *currblock = rootblock + GRUB_DISK_SECTOR_SIZE * blocksize;
242 unsigned int i;
244 for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++)
245 checksum += grub_be_to_cpu32 (currblock[i]);
247 if (checksumr == -checksum)
248 break;
250 if (-checksum != checksumr)
252 grub_error (GRUB_ERR_BAD_FS, "affs blocksize could not be determined");
253 goto fail;
255 blocksize++;
257 data->blocksize = blocksize;
258 data->disk = disk;
259 data->htsize = grub_be_to_cpu32 (rblock->htsize);
260 data->diropen.data = data;
261 #ifdef __AROS__
262 data->diropen.block = rblknum;
263 #else
264 data->diropen.block = (disk->total_sectors >> 1);
265 #endif
267 grub_free (rootblock);
269 return data;
271 fail:
272 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
273 grub_error (GRUB_ERR_BAD_FS, "not an affs filesystem");
275 grub_free (data);
276 grub_free (rootblock);
277 return 0;
281 static char *
282 grub_affs_read_symlink (grub_fshelp_node_t node)
284 struct grub_affs_data *data = node->data;
285 char *symlink;
287 symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize));
288 if (!symlink)
289 return 0;
291 grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET,
292 GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink);
293 if (grub_errno)
295 grub_free (symlink);
296 return 0;
298 grub_printf ("Symlink: `%s'\n", symlink);
299 return symlink;
303 static int
304 grub_affs_iterate_dir (grub_fshelp_node_t dir,
305 int NESTED_FUNC_ATTR
306 (*hook) (const char *filename,
307 enum grub_fshelp_filetype filetype,
308 grub_fshelp_node_t node))
310 int i;
311 struct grub_affs_file file;
312 struct grub_fshelp_node *node = 0;
313 struct grub_affs_data *data = dir->data;
314 grub_uint32_t *hashtable;
316 auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
317 int size, int type);
319 int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
320 int size, int type)
322 node = grub_malloc (sizeof (*node));
323 if (!node)
325 grub_free (hashtable);
326 return 1;
329 node->data = data;
330 node->size = size;
331 node->block = block;
332 node->parent = grub_be_to_cpu32 (file.parent);
334 if (hook (name, type, node))
336 grub_free (hashtable);
337 return 1;
339 return 0;
342 hashtable = grub_malloc (data->htsize * sizeof (*hashtable));
343 if (!hashtable)
344 return 1;
346 grub_disk_read (data->disk, dir->block, GRUB_AFFS_HASHTABLE_OFFSET,
347 data->htsize * sizeof (*hashtable), (char *) hashtable);
348 if (grub_errno)
349 goto fail;
351 /* Create the directory entries for `.' and `..'. */
352 if (grub_affs_create_node (".", dir->block, dir->size, GRUB_FSHELP_DIR))
353 return 1;
354 if (grub_affs_create_node ("..", dir->parent ? dir->parent : dir->block,
355 dir->size, GRUB_FSHELP_DIR))
356 return 1;
358 for (i = 0; i < data->htsize; i++)
360 enum grub_fshelp_filetype type;
361 grub_uint64_t next;
363 if (!hashtable[i])
364 continue;
366 /* Every entry in the hashtable can be chained. Read the entire
367 chain. */
368 next = grub_be_to_cpu32 (hashtable[i]);
370 while (next)
372 grub_disk_read (data->disk, next + data->blocksize - 1,
373 data->blocksize * GRUB_DISK_SECTOR_SIZE
374 - GRUB_AFFS_FILE_LOCATION,
375 sizeof (file), (char *) &file);
376 if (grub_errno)
377 goto fail;
379 file.name[file.namelen] = '\0';
381 if ((int) grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_DIR)
382 type = GRUB_FSHELP_REG;
383 else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_REG)
384 type = GRUB_FSHELP_DIR;
385 else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_SYMLINK)
386 type = GRUB_FSHELP_SYMLINK;
387 else
388 type = GRUB_FSHELP_UNKNOWN;
390 if (grub_affs_create_node ((char *) (file.name), next,
391 grub_be_to_cpu32 (file.size), type))
392 return 1;
394 next = grub_be_to_cpu32 (file.next);
398 grub_free (hashtable);
399 return 0;
401 fail:
402 grub_free (node);
403 grub_free (hashtable);
404 return 1;
408 /* Open a file named NAME and initialize FILE. */
409 static grub_err_t
410 grub_affs_open (struct grub_file *file, const char *name)
412 struct grub_affs_data *data;
413 struct grub_fshelp_node *fdiro = 0;
415 #ifndef GRUB_UTIL
416 grub_dl_ref (my_mod);
417 #endif
419 data = grub_affs_mount (file->device->disk);
420 if (!data)
421 goto fail;
423 grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir,
424 grub_affs_read_symlink, GRUB_FSHELP_REG);
425 if (grub_errno)
426 goto fail;
428 file->size = fdiro->size;
429 data->diropen = *fdiro;
430 grub_free (fdiro);
432 file->data = data;
433 file->offset = 0;
435 return 0;
437 fail:
438 if (data && fdiro != &data->diropen)
439 grub_free (fdiro);
440 grub_free (data);
442 #ifndef GRUB_UTIL
443 grub_dl_unref (my_mod);
444 #endif
446 return grub_errno;
450 static grub_err_t
451 grub_affs_close (grub_file_t file)
453 grub_free (file->data);
455 #ifndef GRUB_UTIL
456 grub_dl_unref (my_mod);
457 #endif
459 return GRUB_ERR_NONE;
463 /* Read LEN bytes data from FILE into BUF. */
464 static grub_ssize_t
465 grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
467 struct grub_affs_data *data =
468 (struct grub_affs_data *) file->data;
470 int size = grub_affs_read_file (&data->diropen, file->read_hook,
471 file->offset, len, buf);
473 return size;
477 static grub_err_t
478 grub_affs_dir (grub_device_t device, const char *path,
479 int (*hook) (const char *filename, int dir))
481 struct grub_affs_data *data = 0;
482 struct grub_fshelp_node *fdiro = 0;
484 auto int NESTED_FUNC_ATTR iterate (const char *filename,
485 enum grub_fshelp_filetype filetype,
486 grub_fshelp_node_t node);
488 int NESTED_FUNC_ATTR iterate (const char *filename,
489 enum grub_fshelp_filetype filetype,
490 grub_fshelp_node_t node)
492 grub_free (node);
494 if (filetype == GRUB_FSHELP_DIR)
495 return hook (filename, 1);
496 else
497 return hook (filename, 0);
499 return 0;
502 #ifndef GRUB_UTIL
503 grub_dl_ref (my_mod);
504 #endif
506 data = grub_affs_mount (device->disk);
507 if (!data)
508 goto fail;
510 grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir,
511 grub_affs_read_symlink, GRUB_FSHELP_DIR);
512 if (grub_errno)
513 goto fail;
515 grub_affs_iterate_dir (fdiro, iterate);
517 fail:
518 if (data && fdiro != &data->diropen)
519 grub_free (fdiro);
520 grub_free (data);
522 #ifndef GRUB_UTIL
523 grub_dl_unref (my_mod);
524 #endif
526 return grub_errno;
530 static grub_err_t
531 grub_affs_label (grub_device_t device, char **label)
533 struct grub_affs_data *data;
534 struct grub_affs_file file;
535 grub_disk_t disk = device->disk;
537 #ifndef GRUB_UTIL
538 grub_dl_ref (my_mod);
539 #endif
541 data = grub_affs_mount (disk);
542 if (data)
544 /* The rootblock maps quite well on a file header block, it's
545 something we can use here. */
546 grub_disk_read (data->disk, disk->total_sectors >> 1,
547 data->blocksize * (GRUB_DISK_SECTOR_SIZE
548 - GRUB_AFFS_FILE_LOCATION),
549 sizeof (file), (char *) &file);
550 if (grub_errno)
551 return 0;
553 *label = grub_strndup ((char *) (file.name), file.namelen);
555 else
556 *label = 0;
558 #ifndef GRUB_UTIL
559 grub_dl_unref (my_mod);
560 #endif
562 grub_free (data);
564 return grub_errno;
568 static struct grub_fs grub_affs_fs =
570 .name = "affs",
571 .dir = grub_affs_dir,
572 .open = grub_affs_open,
573 .read = grub_affs_read,
574 .close = grub_affs_close,
575 .label = grub_affs_label,
576 .next = 0
579 GRUB_MOD_INIT(affs)
581 grub_fs_register (&grub_affs_fs);
582 #ifndef GRUB_UTIL
583 my_mod = mod;
584 #endif
587 GRUB_MOD_FINI(affs)
589 grub_fs_unregister (&grub_affs_fs);