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. */
108 static grub_dl_t my_mod
;
112 static grub_disk_addr_t
113 grub_affs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
117 int block
= node
->block
;
118 struct grub_affs_file file
;
119 struct grub_affs_data
*data
= node
->data
;
122 /* Find the block that points to the fileblock we are looking up by
123 following the chain until the right table is reached. */
124 for (links
= grub_divmod64 (fileblock
, data
->htsize
, &mod
); links
; links
--)
126 grub_disk_read (data
->disk
, block
+ data
->blocksize
- 1,
127 data
->blocksize
* (GRUB_DISK_SECTOR_SIZE
128 - GRUB_AFFS_FILE_LOCATION
),
129 sizeof (file
), (char *) &file
);
133 block
= grub_be_to_cpu32 (file
.extension
);
136 /* Translate the fileblock to the block within the right table. */
138 grub_disk_read (data
->disk
, block
,
139 GRUB_AFFS_BLOCKPTR_OFFSET
140 + (data
->htsize
- fileblock
- 1) * sizeof (pos
),
141 sizeof (pos
), (char *) &pos
);
145 return grub_be_to_cpu32 (pos
);
149 /* Read LEN bytes from the file described by DATA starting with byte
150 POS. Return the amount of read bytes in READ. */
152 grub_affs_read_file (grub_fshelp_node_t node
,
153 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
154 unsigned offset
, unsigned length
),
155 int pos
, grub_size_t len
, char *buf
)
157 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
158 pos
, len
, buf
, grub_affs_read_block
,
163 static struct grub_affs_data
*
164 grub_affs_mount (grub_disk_t disk
)
166 struct grub_affs_data
*data
;
167 grub_uint32_t
*rootblock
= 0;
168 struct grub_affs_rblock
*rblock
;
174 data
= grub_malloc (sizeof (struct grub_affs_data
));
178 /* Read the bootblock. */
179 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
180 (char *) &data
->bblock
);
184 /* Make sure this is an affs filesystem. */
185 if (grub_strncmp ((char *) (data
->bblock
.type
), "DOS", 3))
187 grub_error (GRUB_ERR_BAD_FS
, "not an affs filesystem");
191 /* Test if the filesystem is a OFS filesystem. */
192 if (! (data
->bblock
.flags
& GRUB_AFFS_FLAG_FFS
))
194 grub_error (GRUB_ERR_BAD_FS
, "ofs not yet supported");
198 /* Read the bootblock. */
199 grub_disk_read (disk
, 0, 0, sizeof (struct grub_affs_bblock
),
200 (char *) &data
->bblock
);
204 /* No sane person uses more than 8KB for a block. At least I hope
205 for that person because in that case this won't work. */
206 rootblock
= grub_malloc (GRUB_DISK_SECTOR_SIZE
* 16);
210 rblock
= (struct grub_affs_rblock
*) rootblock
;
212 /* Read the rootblock. */
213 grub_disk_read (disk
, (disk
->total_sectors
>> 1) + blocksize
, 0,
214 GRUB_DISK_SECTOR_SIZE
* 16, (char *) rootblock
);
218 /* The filesystem blocksize is not stored anywhere in the filesystem
219 itself. One way to determine it is reading blocks for the
220 rootblock until the checksum is correct. */
221 checksumr
= grub_be_to_cpu32 (rblock
->checksum
);
222 rblock
->checksum
= 0;
223 for (blocksize
= 0; blocksize
< 8; blocksize
++)
225 grub_uint32_t
*currblock
= rootblock
+ GRUB_DISK_SECTOR_SIZE
* blocksize
;
228 for (i
= 0; i
< GRUB_DISK_SECTOR_SIZE
/ sizeof (*currblock
); i
++)
229 checksum
+= grub_be_to_cpu32 (currblock
[i
]);
231 if (checksumr
== -checksum
)
234 if (-checksum
!= checksumr
)
236 grub_error (GRUB_ERR_BAD_FS
, "affs blocksize could not be determined");
241 data
->blocksize
= blocksize
;
243 data
->htsize
= grub_be_to_cpu32 (rblock
->htsize
);
244 data
->diropen
.data
= data
;
245 data
->diropen
.block
= (disk
->total_sectors
>> 1);
247 grub_free (rootblock
);
252 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
253 grub_error (GRUB_ERR_BAD_FS
, "not an affs filesystem");
256 grub_free (rootblock
);
262 grub_affs_read_symlink (grub_fshelp_node_t node
)
264 struct grub_affs_data
*data
= node
->data
;
267 symlink
= grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
));
271 grub_disk_read (data
->disk
, node
->block
, GRUB_AFFS_SYMLINK_OFFSET
,
272 GRUB_AFFS_SYMLINK_SIZE (data
->blocksize
), symlink
);
278 grub_printf ("Symlink: `%s'\n", symlink
);
284 grub_affs_iterate_dir (grub_fshelp_node_t dir
,
286 (*hook
) (const char *filename
,
287 enum grub_fshelp_filetype filetype
,
288 grub_fshelp_node_t node
))
291 struct grub_affs_file file
;
292 struct grub_fshelp_node
*node
= 0;
293 struct grub_affs_data
*data
= dir
->data
;
294 grub_uint32_t
*hashtable
;
296 auto int NESTED_FUNC_ATTR
grub_affs_create_node (const char *name
, int block
,
299 int NESTED_FUNC_ATTR
grub_affs_create_node (const char *name
, int block
,
302 node
= grub_malloc (sizeof (*node
));
305 grub_free (hashtable
);
312 node
->parent
= grub_be_to_cpu32 (file
.parent
);
314 if (hook (name
, type
, node
))
316 grub_free (hashtable
);
322 hashtable
= grub_malloc (data
->htsize
* sizeof (*hashtable
));
326 grub_disk_read (data
->disk
, dir
->block
, GRUB_AFFS_HASHTABLE_OFFSET
,
327 data
->htsize
* sizeof (*hashtable
), (char *) hashtable
);
331 /* Create the directory entries for `.' and `..'. */
332 if (grub_affs_create_node (".", dir
->block
, dir
->size
, GRUB_FSHELP_DIR
))
334 if (grub_affs_create_node ("..", dir
->parent
? dir
->parent
: dir
->block
,
335 dir
->size
, GRUB_FSHELP_DIR
))
338 for (i
= 0; i
< data
->htsize
; i
++)
340 enum grub_fshelp_filetype type
;
346 /* Every entry in the hashtable can be chained. Read the entire
348 next
= grub_be_to_cpu32 (hashtable
[i
]);
352 grub_disk_read (data
->disk
, next
+ data
->blocksize
- 1,
353 data
->blocksize
* GRUB_DISK_SECTOR_SIZE
354 - GRUB_AFFS_FILE_LOCATION
,
355 sizeof (file
), (char *) &file
);
359 file
.name
[file
.namelen
] = '\0';
361 if ((int) grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_DIR
)
362 type
= GRUB_FSHELP_REG
;
363 else if (grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_REG
)
364 type
= GRUB_FSHELP_DIR
;
365 else if (grub_be_to_cpu32 (file
.type
) == GRUB_AFFS_FILETYPE_SYMLINK
)
366 type
= GRUB_FSHELP_SYMLINK
;
368 type
= GRUB_FSHELP_UNKNOWN
;
370 if (grub_affs_create_node ((char *) (file
.name
), next
,
371 grub_be_to_cpu32 (file
.size
), type
))
374 next
= grub_be_to_cpu32 (file
.next
);
378 grub_free (hashtable
);
383 grub_free (hashtable
);
388 /* Open a file named NAME and initialize FILE. */
390 grub_affs_open (struct grub_file
*file
, const char *name
)
392 struct grub_affs_data
*data
;
393 struct grub_fshelp_node
*fdiro
= 0;
396 grub_dl_ref (my_mod
);
399 data
= grub_affs_mount (file
->device
->disk
);
403 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
404 grub_affs_read_symlink
, GRUB_FSHELP_REG
);
408 file
->size
= fdiro
->size
;
409 data
->diropen
= *fdiro
;
418 if (data
&& fdiro
!= &data
->diropen
)
423 grub_dl_unref (my_mod
);
431 grub_affs_close (grub_file_t file
)
433 grub_free (file
->data
);
436 grub_dl_unref (my_mod
);
439 return GRUB_ERR_NONE
;
443 /* Read LEN bytes data from FILE into BUF. */
445 grub_affs_read (grub_file_t file
, char *buf
, grub_size_t len
)
447 struct grub_affs_data
*data
=
448 (struct grub_affs_data
*) file
->data
;
450 int size
= grub_affs_read_file (&data
->diropen
, file
->read_hook
,
451 file
->offset
, len
, buf
);
458 grub_affs_dir (grub_device_t device
, const char *path
,
459 int (*hook
) (const char *filename
, int dir
))
461 struct grub_affs_data
*data
= 0;
462 struct grub_fshelp_node
*fdiro
= 0;
464 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
465 enum grub_fshelp_filetype filetype
,
466 grub_fshelp_node_t node
);
468 int NESTED_FUNC_ATTR
iterate (const char *filename
,
469 enum grub_fshelp_filetype filetype
,
470 grub_fshelp_node_t node
)
474 if (filetype
== GRUB_FSHELP_DIR
)
475 return hook (filename
, 1);
477 return hook (filename
, 0);
483 grub_dl_ref (my_mod
);
486 data
= grub_affs_mount (device
->disk
);
490 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_affs_iterate_dir
,
491 grub_affs_read_symlink
, GRUB_FSHELP_DIR
);
495 grub_affs_iterate_dir (fdiro
, iterate
);
498 if (data
&& fdiro
!= &data
->diropen
)
503 grub_dl_unref (my_mod
);
511 grub_affs_label (grub_device_t device
, char **label
)
513 struct grub_affs_data
*data
;
514 struct grub_affs_file file
;
515 grub_disk_t disk
= device
->disk
;
518 grub_dl_ref (my_mod
);
521 data
= grub_affs_mount (disk
);
524 /* The rootblock maps quite well on a file header block, it's
525 something we can use here. */
526 grub_disk_read (data
->disk
, disk
->total_sectors
>> 1,
527 data
->blocksize
* (GRUB_DISK_SECTOR_SIZE
528 - GRUB_AFFS_FILE_LOCATION
),
529 sizeof (file
), (char *) &file
);
533 *label
= grub_strndup ((char *) (file
.name
), file
.namelen
);
539 grub_dl_unref (my_mod
);
548 static struct grub_fs grub_affs_fs
=
551 .dir
= grub_affs_dir
,
552 .open
= grub_affs_open
,
553 .read
= grub_affs_read
,
554 .close
= grub_affs_close
,
555 .label
= grub_affs_label
,
561 grub_fs_register (&grub_affs_fs
);
569 grub_fs_unregister (&grub_affs_fs
);