1 /* affs.c - Amiga Fast FileSystem. */
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/>.
21 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
29 #include <grub/partition.h>
32 /* The affs bootblock. */
33 struct grub_affs_bblock
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
43 #define GRUB_AFFS_FLAG_FFS 1
45 /* The affs rootblock. */
46 struct grub_affs_rblock
49 grub_uint8_t unused1
[8];
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. */
59 grub_uint8_t unused1
[12];
61 grub_uint8_t unused2
[104];
63 grub_uint8_t name
[30];
64 grub_uint8_t unused3
[33];
67 grub_uint32_t extension
;
69 } __attribute__ ((packed
));
71 /* The location of `struct grub_affs_file' relative to the end of a
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
;
96 /* Information about a "mounted" affs filesystem. */
99 struct grub_affs_bblock bblock
;
100 struct grub_fshelp_node diropen
;
103 /* Blocksize in sectors. */
106 /* The number of entries in the hashtable. */
111 static grub_dl_t my_mod
;
116 grub_affs_read_block (grub_fshelp_node_t node
, int fileblock
)
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
);
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
);
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. */
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
,
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
;
176 data
= grub_malloc (sizeof (struct grub_affs_data
));
180 /* Read the bootblock. */
181 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
182 (char *) &data
->bblock
);
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");
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");
200 /* Read the bootblock. */
201 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
202 (char *) &data
->bblock
);
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);
212 rblock
= (struct grub_affs_rblock
*) rootblock
;
214 /* Read the rootblock. */
216 grub_uint64_t reservedblocks
= 2;
217 grub_uint64_t countblocks
;
219 countblocks
= disk
->partition
->len
;
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
);
228 grub_disk_read (disk
, (disk
->total_sectors
>> 1) + blocksize
, 0,
229 GRUB_DISK_SECTOR_SIZE
* 16, (char *) rootblock
);
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
;
244 for (i
= 0; i
< GRUB_DISK_SECTOR_SIZE
/ sizeof (*currblock
); i
++)
245 checksum
+= grub_be_to_cpu32 (currblock
[i
]);
247 if (checksumr
== -checksum
)
250 if (-checksum
!= checksumr
)
252 grub_error (GRUB_ERR_BAD_FS
, "affs blocksize could not be determined");
257 data
->blocksize
= blocksize
;
259 data
->htsize
= grub_be_to_cpu32 (rblock
->htsize
);
260 data
->diropen
.data
= data
;
262 data
->diropen
.block
= rblknum
;
264 data
->diropen
.block
= (disk
->total_sectors
>> 1);
267 grub_free (rootblock
);
272 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
273 grub_error (GRUB_ERR_BAD_FS
, "not an affs filesystem");
276 grub_free (rootblock
);
282 grub_affs_read_symlink (grub_fshelp_node_t node
)
284 struct grub_affs_data
*data
= node
->data
;
287 symlink
= grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
));
291 grub_disk_read (data
->disk
, node
->block
, GRUB_AFFS_SYMLINK_OFFSET
,
292 GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
), symlink
);
298 grub_printf ("Symlink: `%s'\n", symlink
);
304 grub_affs_iterate_dir (grub_fshelp_node_t dir
,
306 (*hook
) (const char *filename
,
307 enum grub_fshelp_filetype filetype
,
308 grub_fshelp_node_t node
))
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
,
319 int NESTED_FUNC_ATTR
grub_affs_create_node (const char *name
, int block
,
322 node
= grub_malloc (sizeof (*node
));
325 grub_free (hashtable
);
332 node
->parent
= grub_be_to_cpu32 (file
.parent
);
334 if (hook (name
, type
, node
))
336 grub_free (hashtable
);
342 hashtable
= grub_malloc (data
->htsize
* sizeof (*hashtable
));
346 grub_disk_read (data
->disk
, dir
->block
, GRUB_AFFS_HASHTABLE_OFFSET
,
347 data
->htsize
* sizeof (*hashtable
), (char *) hashtable
);
351 /* Create the directory entries for `.' and `..'. */
352 if (grub_affs_create_node (".", dir
->block
, dir
->size
, GRUB_FSHELP_DIR
))
354 if (grub_affs_create_node ("..", dir
->parent
? dir
->parent
: dir
->block
,
355 dir
->size
, GRUB_FSHELP_DIR
))
358 for (i
= 0; i
< data
->htsize
; i
++)
360 enum grub_fshelp_filetype type
;
366 /* Every entry in the hashtable can be chained. Read the entire
368 next
= grub_be_to_cpu32 (hashtable
[i
]);
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
);
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
;
388 type
= GRUB_FSHELP_UNKNOWN
;
390 if (grub_affs_create_node ((char *) (file
.name
), next
,
391 grub_be_to_cpu32 (file
.size
), type
))
394 next
= grub_be_to_cpu32 (file
.next
);
398 grub_free (hashtable
);
403 grub_free (hashtable
);
408 /* Open a file named NAME and initialize FILE. */
410 grub_affs_open (struct grub_file
*file
, const char *name
)
412 struct grub_affs_data
*data
;
413 struct grub_fshelp_node
*fdiro
= 0;
416 grub_dl_ref (my_mod
);
419 data
= grub_affs_mount (file
->device
->disk
);
423 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
424 grub_affs_read_symlink
, GRUB_FSHELP_REG
);
428 file
->size
= fdiro
->size
;
429 data
->diropen
= *fdiro
;
438 if (data
&& fdiro
!= &data
->diropen
)
443 grub_dl_unref (my_mod
);
451 grub_affs_close (grub_file_t file
)
453 grub_free (file
->data
);
456 grub_dl_unref (my_mod
);
459 return GRUB_ERR_NONE
;
463 /* Read LEN bytes data from FILE into BUF. */
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
);
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
)
494 if (filetype
== GRUB_FSHELP_DIR
)
495 return hook (filename
, 1);
497 return hook (filename
, 0);
503 grub_dl_ref (my_mod
);
506 data
= grub_affs_mount (device
->disk
);
510 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
511 grub_affs_read_symlink
, GRUB_FSHELP_DIR
);
515 grub_affs_iterate_dir (fdiro
, iterate
);
518 if (data
&& fdiro
!= &data
->diropen
)
523 grub_dl_unref (my_mod
);
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
;
538 grub_dl_ref (my_mod
);
541 data
= grub_affs_mount (disk
);
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
);
553 *label
= grub_strndup ((char *) (file
.name
), file
.namelen
);
559 grub_dl_unref (my_mod
);
568 static struct grub_fs grub_affs_fs
=
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
,
581 grub_fs_register (&grub_affs_fs
);
589 grub_fs_unregister (&grub_affs_fs
);