1
/* affs.c - Amiga Fast FileSystem. */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2005,2006,2007,2008,2009 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 couldn't 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_dprintf ("affs", "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);