1 /* affs.c - Amiga Fast FileSystem. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008 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 /* The affs bootblock. */
30 struct grub_affs_bblock
34 grub_uint32_t checksum
;
35 grub_uint32_t rootblock
;
36 } __attribute__ ((packed
));
38 /* Set if the filesystem is a AFFS filesystem. Otherwise this is an
40 #define GRUB_AFFS_FLAG_FFS 1
42 /* The affs rootblock. */
43 struct grub_affs_rblock
46 grub_uint8_t unused1
[8];
48 grub_uint32_t unused2
;
49 grub_uint32_t checksum
;
50 grub_uint32_t hashtable
[1];
51 } __attribute__ ((packed
));
53 /* The second part of a file header block. */
56 grub_uint8_t unused1
[12];
58 grub_uint8_t unused2
[104];
60 grub_uint8_t name
[30];
61 grub_uint8_t unused3
[33];
64 grub_uint32_t extension
;
66 } __attribute__ ((packed
));
68 /* The location of `struct grub_affs_file' relative to the end of a
70 #define GRUB_AFFS_FILE_LOCATION 200
72 /* The offset in both the rootblock and the file header block for the
73 hashtable, symlink and block pointers (all synonyms). */
74 #define GRUB_AFFS_HASHTABLE_OFFSET 24
75 #define GRUB_AFFS_BLOCKPTR_OFFSET 24
76 #define GRUB_AFFS_SYMLINK_OFFSET 24
78 #define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225)
80 #define GRUB_AFFS_FILETYPE_DIR -3
81 #define GRUB_AFFS_FILETYPE_REG 2
82 #define GRUB_AFFS_FILETYPE_SYMLINK 3
85 struct grub_fshelp_node
87 struct grub_affs_data
*data
;
93 /* Information about a "mounted" affs filesystem. */
96 struct grub_affs_bblock bblock
;
97 struct grub_fshelp_node diropen
;
100 /* Blocksize in sectors. */
103 /* The number of entries in the hashtable. */
107 static grub_dl_t my_mod
;
110 static grub_disk_addr_t
111 grub_affs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
115 int block
= node
->block
;
116 struct grub_affs_file file
;
117 struct grub_affs_data
*data
= node
->data
;
120 /* Find the block that points to the fileblock we are looking up by
121 following the chain until the right table is reached. */
122 for (links
= grub_divmod64 (fileblock
, data
->htsize
, &mod
); links
; links
--)
124 grub_disk_read (data
->disk
, block
+ data
->blocksize
- 1,
125 data
->blocksize
* (GRUB_DISK_SECTOR_SIZE
126 - GRUB_AFFS_FILE_LOCATION
),
127 sizeof (file
), &file
);
131 block
= grub_be_to_cpu32 (file
.extension
);
134 /* Translate the fileblock to the block within the right table. */
136 grub_disk_read (data
->disk
, block
,
137 GRUB_AFFS_BLOCKPTR_OFFSET
138 + (data
->htsize
- fileblock
- 1) * sizeof (pos
),
143 return grub_be_to_cpu32 (pos
);
147 /* Read LEN bytes from the file described by DATA starting with byte
148 POS. Return the amount of read bytes in READ. */
150 grub_affs_read_file (grub_fshelp_node_t node
,
151 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
152 unsigned offset
, unsigned length
),
153 int pos
, grub_size_t len
, char *buf
)
155 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
156 pos
, len
, buf
, grub_affs_read_block
,
161 static struct grub_affs_data
*
162 grub_affs_mount (grub_disk_t disk
)
164 struct grub_affs_data
*data
;
165 grub_uint32_t
*rootblock
= 0;
166 struct grub_affs_rblock
*rblock
;
172 data
= grub_malloc (sizeof (struct grub_affs_data
));
176 /* Read the bootblock. */
177 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
182 /* Make sure this is an affs filesystem. */
183 if (grub_strncmp ((char *) (data
->bblock
.type
), "DOS", 3))
185 grub_error (GRUB_ERR_BAD_FS
, "not an affs filesystem");
189 /* Test if the filesystem is a OFS filesystem. */
190 if (! (data
->bblock
.flags
& GRUB_AFFS_FLAG_FFS
))
192 grub_error (GRUB_ERR_BAD_FS
, "ofs not yet supported");
196 /* Read the bootblock. */
197 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
202 /* No sane person uses more than 8KB for a block. At least I hope
203 for that person because in that case this won't work. */
204 rootblock
= grub_malloc (GRUB_DISK_SECTOR_SIZE
* 16);
208 rblock
= (struct grub_affs_rblock
*) rootblock
;
210 /* Read the rootblock. */
211 grub_disk_read (disk
, (disk
->total_sectors
>> 1) + blocksize
, 0,
212 GRUB_DISK_SECTOR_SIZE
* 16, rootblock
);
216 /* The filesystem blocksize is not stored anywhere in the filesystem
217 itself. One way to determine it is reading blocks for the
218 rootblock until the checksum is correct. */
219 checksumr
= grub_be_to_cpu32 (rblock
->checksum
);
220 rblock
->checksum
= 0;
221 for (blocksize
= 0; blocksize
< 8; blocksize
++)
223 grub_uint32_t
*currblock
= rootblock
+ GRUB_DISK_SECTOR_SIZE
* blocksize
;
226 for (i
= 0; i
< GRUB_DISK_SECTOR_SIZE
/ sizeof (*currblock
); i
++)
227 checksum
+= grub_be_to_cpu32 (currblock
[i
]);
229 if (checksumr
== -checksum
)
232 if (-checksum
!= checksumr
)
234 grub_error (GRUB_ERR_BAD_FS
, "affs blocksize could not be determined");
239 data
->blocksize
= blocksize
;
241 data
->htsize
= grub_be_to_cpu32 (rblock
->htsize
);
242 data
->diropen
.data
= data
;
243 data
->diropen
.block
= (disk
->total_sectors
>> 1);
245 grub_free (rootblock
);
250 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
251 grub_error (GRUB_ERR_BAD_FS
, "not an affs filesystem");
254 grub_free (rootblock
);
260 grub_affs_read_symlink (grub_fshelp_node_t node
)
262 struct grub_affs_data
*data
= node
->data
;
265 symlink
= grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
));
269 grub_disk_read (data
->disk
, node
->block
, GRUB_AFFS_SYMLINK_OFFSET
,
270 GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
), symlink
);
276 grub_printf ("Symlink: `%s'\n", symlink
);
282 grub_affs_iterate_dir (grub_fshelp_node_t dir
,
284 (*hook
) (const char *filename
,
285 enum grub_fshelp_filetype filetype
,
286 grub_fshelp_node_t node
))
289 struct grub_affs_file file
;
290 struct grub_fshelp_node
*node
= 0;
291 struct grub_affs_data
*data
= dir
->data
;
292 grub_uint32_t
*hashtable
;
294 auto int NESTED_FUNC_ATTR
grub_affs_create_node (const char *name
, int block
,
297 int NESTED_FUNC_ATTR
grub_affs_create_node (const char *name
, int block
,
300 node
= grub_malloc (sizeof (*node
));
303 grub_free (hashtable
);
310 node
->parent
= grub_be_to_cpu32 (file
.parent
);
312 if (hook (name
, type
, node
))
314 grub_free (hashtable
);
320 hashtable
= grub_malloc (data
->htsize
* sizeof (*hashtable
));
324 grub_disk_read (data
->disk
, dir
->block
, GRUB_AFFS_HASHTABLE_OFFSET
,
325 data
->htsize
* sizeof (*hashtable
), (char *) hashtable
);
329 /* Create the directory entries for `.' and `..'. */
330 if (grub_affs_create_node (".", dir
->block
, dir
->size
, GRUB_FSHELP_DIR
))
332 if (grub_affs_create_node ("..", dir
->parent
? dir
->parent
: dir
->block
,
333 dir
->size
, GRUB_FSHELP_DIR
))
336 for (i
= 0; i
< data
->htsize
; i
++)
338 enum grub_fshelp_filetype type
;
344 /* Every entry in the hashtable can be chained. Read the entire
346 next
= grub_be_to_cpu32 (hashtable
[i
]);
350 grub_disk_read (data
->disk
, next
+ data
->blocksize
- 1,
351 data
->blocksize
* GRUB_DISK_SECTOR_SIZE
352 - GRUB_AFFS_FILE_LOCATION
,
353 sizeof (file
), (char *) &file
);
357 file
.name
[file
.namelen
] = '\0';
359 if ((int) grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_DIR
)
360 type
= GRUB_FSHELP_REG
;
361 else if (grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_REG
)
362 type
= GRUB_FSHELP_DIR
;
363 else if (grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_SYMLINK
)
364 type
= GRUB_FSHELP_SYMLINK
;
366 type
= GRUB_FSHELP_UNKNOWN
;
368 if (grub_affs_create_node ((char *) (file
.name
), next
,
369 grub_be_to_cpu32 (file
.size
), type
))
372 next
= grub_be_to_cpu32 (file
.next
);
376 grub_free (hashtable
);
381 grub_free (hashtable
);
386 /* Open a file named NAME and initialize FILE. */
388 grub_affs_open (struct grub_file
*file
, const char *name
)
390 struct grub_affs_data
*data
;
391 struct grub_fshelp_node
*fdiro
= 0;
393 grub_dl_ref (my_mod
);
395 data
= grub_affs_mount (file
->device
->disk
);
399 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
400 grub_affs_read_symlink
, GRUB_FSHELP_REG
);
404 file
->size
= fdiro
->size
;
405 data
->diropen
= *fdiro
;
414 if (data
&& fdiro
!= &data
->diropen
)
418 grub_dl_unref (my_mod
);
425 grub_affs_close (grub_file_t file
)
427 grub_free (file
->data
);
429 grub_dl_unref (my_mod
);
431 return GRUB_ERR_NONE
;
435 /* Read LEN bytes data from FILE into BUF. */
437 grub_affs_read (grub_file_t file
, char *buf
, grub_size_t len
)
439 struct grub_affs_data
*data
=
440 (struct grub_affs_data
*) file
->data
;
442 int size
= grub_affs_read_file (&data
->diropen
, file
->read_hook
,
443 file
->offset
, len
, buf
);
450 grub_affs_dir (grub_device_t device
, const char *path
,
451 int (*hook
) (const char *filename
,
452 const struct grub_dirhook_info
*info
))
454 struct grub_affs_data
*data
= 0;
455 struct grub_fshelp_node
*fdiro
= 0;
457 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
458 enum grub_fshelp_filetype filetype
,
459 grub_fshelp_node_t node
);
461 int NESTED_FUNC_ATTR
iterate (const char *filename
,
462 enum grub_fshelp_filetype filetype
,
463 grub_fshelp_node_t node
)
465 struct grub_dirhook_info info
;
466 grub_memset (&info
, 0, sizeof (info
));
467 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
469 return hook (filename
, &info
);
472 grub_dl_ref (my_mod
);
474 data
= grub_affs_mount (device
->disk
);
478 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
479 grub_affs_read_symlink
, GRUB_FSHELP_DIR
);
483 grub_affs_iterate_dir (fdiro
, iterate
);
486 if (data
&& fdiro
!= &data
->diropen
)
490 grub_dl_unref (my_mod
);
497 grub_affs_label (grub_device_t device
, char **label
)
499 struct grub_affs_data
*data
;
500 struct grub_affs_file file
;
501 grub_disk_t disk
= device
->disk
;
503 grub_dl_ref (my_mod
);
505 data
= grub_affs_mount (disk
);
508 /* The rootblock maps quite well on a file header block, it's
509 something we can use here. */
510 grub_disk_read (data
->disk
, disk
->total_sectors
>> 1,
511 data
->blocksize
* (GRUB_DISK_SECTOR_SIZE
512 - GRUB_AFFS_FILE_LOCATION
),
513 sizeof (file
), &file
);
517 *label
= grub_strndup ((char *) (file
.name
), file
.namelen
);
522 grub_dl_unref (my_mod
);
530 static struct grub_fs grub_affs_fs
=
533 .dir
= grub_affs_dir
,
534 .open
= grub_affs_open
,
535 .read
= grub_affs_read
,
536 .close
= grub_affs_close
,
537 .label
= grub_affs_label
,
543 grub_fs_register (&grub_affs_fs
);
549 grub_fs_unregister (&grub_affs_fs
);