1 /* afs.c - The native AtheOS file-system. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 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 #define GRUB_AFS_DIRECT_BLOCK_COUNT 12
30 #define GRUB_AFS_BLOCKS_PER_DI_RUN 4
32 #define GRUB_AFS_SBLOCK_MAGIC1 0x41465331 /* AFS1 */
33 #define GRUB_AFS_SBLOCK_MAGIC2 0xdd121031
34 #define GRUB_AFS_SBLOCK_MAGIC3 0x15b6830e
36 #define GRUB_AFS_INODE_MAGIC 0x64358428
38 #define GRUB_AFS_BTREE_MAGIC 0x65768995
40 #define GRUB_AFS_BNODE_SIZE 1024
42 #define GRUB_AFS_S_IFMT 00170000
43 #define GRUB_AFS_S_IFLNK 0120000
45 #define GRUB_AFS_S_IFREG 0100000
46 #define GRUB_AFS_S_IFDIR 0040000
47 #define GRUB_AFS_S_IFIFO 0010000
49 #define GRUB_AFS_NULL_VAL ((grub_afs_bvalue_t)-1)
51 #define U16(sb, u) (((sb)->byte_order == GRUB_AFS_BO_LITTLE_ENDIAN) ? \
52 grub_le_to_cpu16 (u) : grub_be_to_cpu16 (u))
54 #define U32(sb, u) (((sb)->byte_order == GRUB_AFS_BO_LITTLE_ENDIAN) ? \
55 grub_le_to_cpu32 (u) : grub_be_to_cpu32 (u))
57 #define U64(sb, u) (((sb)->byte_order == GRUB_AFS_BO_LITTLE_ENDIAN) ? \
58 grub_le_to_cpu64 (u) : grub_be_to_cpu64 (u))
60 #define B_KEY_INDEX_OFFSET(node) ((grub_uint16_t *) \
62 sizeof (struct grub_afs_bnode) + \
63 ((node->key_size + 3) & ~3)))
65 #define B_KEY_VALUE_OFFSET(node) ((grub_afs_bvalue_t *) \
66 ((char *) B_KEY_INDEX_OFFSET (node) + \
71 GRUB_AFS_BO_LITTLE_ENDIAN
,
72 GRUB_AFS_BO_BIG_ENDIAN
75 typedef grub_uint64_t grub_afs_off_t
;
76 typedef grub_uint64_t grub_afs_bigtime
;
77 typedef grub_uint64_t grub_afs_bvalue_t
;
79 struct grub_afs_blockrun
86 struct grub_afs_datastream
88 struct grub_afs_blockrun direct
[GRUB_AFS_DIRECT_BLOCK_COUNT
];
89 grub_afs_off_t max_direct_range
;
90 struct grub_afs_blockrun indirect
;
91 grub_afs_off_t max_indirect_range
;
92 struct grub_afs_blockrun double_indirect
;
93 grub_afs_off_t max_double_indirect_range
;
99 grub_afs_bvalue_t left
;
100 grub_afs_bvalue_t right
;
101 grub_afs_bvalue_t overflow
;
102 grub_uint32_t key_count
;
103 grub_uint32_t key_size
;
107 struct grub_afs_btree
110 grub_afs_bvalue_t root
;
111 grub_uint32_t tree_depth
;
112 grub_afs_bvalue_t last_node
;
113 grub_afs_bvalue_t first_free
;
116 struct grub_afs_sblock
118 grub_uint8_t name
[32];
119 grub_uint32_t magic1
;
120 grub_uint32_t byte_order
;
121 grub_uint32_t block_size
;
122 grub_uint32_t block_shift
;
123 grub_afs_off_t num_blocks
;
124 grub_afs_off_t used_blocks
;
125 grub_uint32_t inode_size
;
126 grub_uint32_t magic2
;
127 grub_uint32_t block_per_group
; // Number of blocks per allocation group (Max 65536)
128 grub_uint32_t alloc_group_shift
; // Number of bits to shift a group number to get a byte address.
129 grub_uint32_t alloc_group_count
;
131 struct grub_afs_blockrun log_block
;
132 grub_afs_off_t log_start
;
133 grub_uint32_t valid_log_blocks
;
134 grub_uint32_t log_size
;
135 grub_uint32_t magic3
;
136 struct grub_afs_blockrun root_dir
; // Root dir inode.
137 struct grub_afs_blockrun deleted_files
; // Directory containing files scheduled for deletion.
138 struct grub_afs_blockrun index_dir
; // Directory of index files.
139 grub_uint32_t boot_loader_size
;
140 grub_uint32_t pad
[7];
143 struct grub_afs_inode
145 grub_uint32_t magic1
;
146 struct grub_afs_blockrun inode_num
;
151 grub_uint32_t link_count
;
152 grub_afs_bigtime create_time
;
153 grub_afs_bigtime modified_time
;
154 struct grub_afs_blockrun parent
;
155 struct grub_afs_blockrun attrib_dir
;
156 grub_uint32_t index_type
; /* Key data-key only used for index files */
157 grub_uint32_t inode_size
;
159 struct grub_afs_datastream stream
;
160 grub_uint32_t pad
[4];
161 grub_uint32_t small_data
[1];
164 struct grub_fshelp_node
166 struct grub_afs_data
*data
;
167 struct grub_afs_inode inode
;
173 struct grub_afs_sblock sblock
;
174 struct grub_afs_inode
*inode
;
175 struct grub_fshelp_node diropen
;
178 static grub_dl_t my_mod
;
180 static grub_afs_off_t
181 grub_afs_run_to_num (struct grub_afs_sblock
*sb
,
182 struct grub_afs_blockrun
*run
)
184 return ((grub_afs_off_t
) U32 (sb
, run
->group
) * sb
->block_per_group
+
185 U16 (sb
, run
->start
));
189 grub_afs_read_inode (struct grub_afs_data
*data
,
190 grub_uint32_t ino
, struct grub_afs_inode
*inode
)
192 return grub_disk_read (data
->disk
,
194 (data
->sblock
.block_size
>> GRUB_DISK_SECTOR_BITS
),
195 0, sizeof (struct grub_afs_inode
),
199 static grub_disk_addr_t
200 grub_afs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
202 struct grub_afs_sblock
*sb
= &node
->data
->sblock
;
203 struct grub_afs_datastream
*ds
= &node
->inode
.stream
;
205 if (fileblock
< U64 (sb
, ds
->max_direct_range
))
209 for (i
= 0; i
< GRUB_AFS_DIRECT_BLOCK_COUNT
; i
++)
211 if (fileblock
< U16 (sb
, ds
->direct
[i
].len
))
212 return grub_afs_run_to_num (sb
, &ds
->direct
[i
]) + fileblock
;
213 fileblock
-= U16 (sb
, ds
->direct
[i
].len
);
216 else if (fileblock
< U64 (sb
, ds
->max_indirect_range
))
218 int ptrs_per_blk
= sb
->block_size
/ sizeof (struct grub_afs_blockrun
);
219 struct grub_afs_blockrun indir
[ptrs_per_blk
];
220 grub_afs_off_t blk
= grub_afs_run_to_num (sb
, &ds
->indirect
);
223 fileblock
-= U64 (sb
, ds
->max_direct_range
);
224 for (i
= 0; i
< ds
->indirect
.len
; i
++, blk
++)
228 if (grub_disk_read (node
->data
->disk
,
229 blk
* (sb
->block_size
>> GRUB_DISK_SECTOR_BITS
),
234 for (j
= 0; j
< ptrs_per_blk
; j
++)
236 if (fileblock
< U16 (sb
, indir
[j
].len
))
237 return grub_afs_run_to_num (sb
, &indir
[j
]) + fileblock
;
239 fileblock
-= U16 (sb
, indir
[j
].len
);
245 int ptrs_per_blk
= sb
->block_size
/ sizeof (struct grub_afs_blockrun
);
246 struct grub_afs_blockrun indir
[ptrs_per_blk
];
248 /* ([idblk][idptr]) ([dblk][dptr]) [blk] */
249 int cur_pos
= fileblock
- U64 (sb
, ds
->max_indirect_range
);
251 int dptr_size
= GRUB_AFS_BLOCKS_PER_DI_RUN
;
252 int dblk_size
= dptr_size
* ptrs_per_blk
;
253 int idptr_size
= dblk_size
* GRUB_AFS_BLOCKS_PER_DI_RUN
;
254 int idblk_size
= idptr_size
* ptrs_per_blk
;
256 int off
= cur_pos
% GRUB_AFS_BLOCKS_PER_DI_RUN
;
257 int dptr
= (cur_pos
/ dptr_size
) % ptrs_per_blk
;
258 int dblk
= (cur_pos
/ dblk_size
) % GRUB_AFS_BLOCKS_PER_DI_RUN
;
259 int idptr
= (cur_pos
/ idptr_size
) % ptrs_per_blk
;
260 int idblk
= (cur_pos
/ idblk_size
);
262 if (grub_disk_read (node
->data
->disk
,
263 (grub_afs_run_to_num (sb
, &ds
->double_indirect
)
265 (sb
->block_size
>> GRUB_DISK_SECTOR_BITS
),
270 if (grub_disk_read (node
->data
->disk
,
271 (grub_afs_run_to_num (sb
, &indir
[idptr
]) + dblk
) *
272 (sb
->block_size
>> GRUB_DISK_SECTOR_BITS
),
277 return grub_afs_run_to_num (sb
, &indir
[dptr
]) + off
;
284 grub_afs_read_file (grub_fshelp_node_t node
,
285 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
286 unsigned offset
, unsigned length
),
287 int pos
, grub_size_t len
, char *buf
)
289 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
290 pos
, len
, buf
, grub_afs_read_block
,
291 U64 (&node
->data
->sblock
,
292 node
->inode
.stream
.size
),
293 node
->data
->sblock
.block_shift
294 - GRUB_DISK_SECTOR_BITS
);
298 grub_afs_iterate_dir (grub_fshelp_node_t dir
,
300 (*hook
) (const char *filename
,
301 enum grub_fshelp_filetype filetype
,
302 grub_fshelp_node_t node
))
304 struct grub_afs_btree head
;
305 char node_data
[GRUB_AFS_BNODE_SIZE
];
306 struct grub_afs_bnode
*node
= (struct grub_afs_bnode
*) node_data
;
307 struct grub_afs_sblock
*sb
= &dir
->data
->sblock
;
310 if ((! dir
->inode
.stream
.size
) ||
311 ((U32 (sb
, dir
->inode
.mode
) & GRUB_AFS_S_IFMT
) != GRUB_AFS_S_IFDIR
))
314 grub_afs_read_file (dir
, 0, 0, sizeof (head
), (char *) &head
);
318 grub_afs_read_file (dir
, 0, U64 (sb
, head
.root
),
319 GRUB_AFS_BNODE_SIZE
, (char *) node
);
323 for (i
= 0; i
< (int) U32 (sb
, head
.tree_depth
) - 1; i
++)
325 grub_afs_bvalue_t blk
;
327 blk
= U64(sb
, B_KEY_VALUE_OFFSET (node
) [0]);
328 grub_afs_read_file (dir
, 0, blk
, GRUB_AFS_BNODE_SIZE
, (char *) node
);
335 grub_uint32_t cur_key
= 0;
339 int key_start
, key_size
;
340 grub_uint16_t
*index
;
342 index
= B_KEY_INDEX_OFFSET (node
);
344 key_start
= U16 (sb
, (cur_key
> 0) ? index
[cur_key
- 1] : 0);
345 key_size
= U16 (sb
, index
[cur_key
]) - key_start
;
348 char filename
[key_size
+ 1];
349 struct grub_fshelp_node
*fdiro
;
352 fdiro
= grub_malloc (sizeof (struct grub_fshelp_node
));
356 fdiro
->data
= dir
->data
;
357 if (grub_afs_read_inode (dir
->data
,
358 U64 (sb
, B_KEY_VALUE_OFFSET (node
) [cur_key
]),
362 grub_memcpy (filename
, &node
->key_data
[key_start
], key_size
);
363 filename
[key_size
] = 0;
365 mode
= (U32 (sb
, fdiro
->inode
.mode
) & GRUB_AFS_S_IFMT
);
366 if (mode
== GRUB_AFS_S_IFDIR
)
367 type
= GRUB_FSHELP_DIR
;
368 else if (mode
== GRUB_AFS_S_IFREG
)
369 type
= GRUB_FSHELP_REG
;
371 type
= GRUB_FSHELP_UNKNOWN
;
373 if (hook (filename
, type
, fdiro
))
378 if (cur_key
>= U32 (sb
, node
->key_count
))
380 if (node
->right
== GRUB_AFS_NULL_VAL
)
383 grub_afs_read_file (dir
, 0, U64 (sb
, node
->right
),
384 GRUB_AFS_BNODE_SIZE
, (char *) node
);
397 grub_afs_validate_sblock (struct grub_afs_sblock
*sb
)
399 if (grub_le_to_cpu32 (sb
->magic1
) == GRUB_AFS_SBLOCK_MAGIC1
)
401 if (grub_le_to_cpu32 (sb
->byte_order
) != GRUB_AFS_BO_LITTLE_ENDIAN
)
404 sb
->byte_order
= GRUB_AFS_BO_LITTLE_ENDIAN
;
405 sb
->magic2
= grub_le_to_cpu32 (sb
->magic2
);
406 sb
->magic3
= grub_le_to_cpu32 (sb
->magic3
);
407 sb
->block_shift
= grub_le_to_cpu32 (sb
->block_shift
);
408 sb
->block_size
= grub_le_to_cpu32 (sb
->block_size
);
409 sb
->used_blocks
= grub_le_to_cpu64 (sb
->used_blocks
);
410 sb
->num_blocks
= grub_le_to_cpu64 (sb
->num_blocks
);
411 sb
->inode_size
= grub_le_to_cpu32 (sb
->inode_size
);
412 sb
->alloc_group_count
= grub_le_to_cpu32 (sb
->alloc_group_count
);
413 sb
->alloc_group_shift
= grub_le_to_cpu32 (sb
->alloc_group_shift
);
414 sb
->block_per_group
= grub_le_to_cpu32 (sb
->block_per_group
);
415 sb
->alloc_group_count
= grub_le_to_cpu32 (sb
->alloc_group_count
);
416 sb
->log_size
= grub_le_to_cpu32 (sb
->log_size
);
418 else if (grub_be_to_cpu32 (sb
->magic1
) == GRUB_AFS_SBLOCK_MAGIC1
)
420 if (grub_be_to_cpu32 (sb
->byte_order
) != GRUB_AFS_BO_BIG_ENDIAN
)
423 sb
->byte_order
= GRUB_AFS_BO_BIG_ENDIAN
;
424 sb
->magic2
= grub_be_to_cpu32 (sb
->magic2
);
425 sb
->magic3
= grub_be_to_cpu32 (sb
->magic3
);
426 sb
->block_shift
= grub_be_to_cpu32 (sb
->block_shift
);
427 sb
->block_size
= grub_be_to_cpu32 (sb
->block_size
);
428 sb
->used_blocks
= grub_be_to_cpu64 (sb
->used_blocks
);
429 sb
->num_blocks
= grub_be_to_cpu64 (sb
->num_blocks
);
430 sb
->inode_size
= grub_be_to_cpu32 (sb
->inode_size
);
431 sb
->alloc_group_count
= grub_be_to_cpu32 (sb
->alloc_group_count
);
432 sb
->alloc_group_shift
= grub_be_to_cpu32 (sb
->alloc_group_shift
);
433 sb
->block_per_group
= grub_be_to_cpu32 (sb
->block_per_group
);
434 sb
->alloc_group_count
= grub_be_to_cpu32 (sb
->alloc_group_count
);
435 sb
->log_size
= grub_be_to_cpu32 (sb
->log_size
);
440 if ((sb
->magic2
!= GRUB_AFS_SBLOCK_MAGIC2
) ||
441 (sb
->magic3
!= GRUB_AFS_SBLOCK_MAGIC3
))
444 if (((grub_uint32_t
) (1 << sb
->block_shift
) != sb
->block_size
) ||
445 (sb
->used_blocks
> sb
->num_blocks
) ||
446 (sb
->inode_size
!= sb
->block_size
) ||
447 (0 == sb
->block_size
) ||
448 ((grub_uint32_t
) (1 << sb
->alloc_group_shift
) !=
449 sb
->block_per_group
* sb
->block_size
) ||
450 (sb
->alloc_group_count
* sb
->block_per_group
< sb
->num_blocks
) ||
451 (U16 (sb
, sb
->log_block
.len
) != sb
->log_size
) ||
452 (U32 (sb
, sb
->valid_log_blocks
) > sb
->log_size
))
458 static struct grub_afs_data
*
459 grub_afs_mount (grub_disk_t disk
)
461 struct grub_afs_data
*data
= 0;
463 data
= grub_malloc (sizeof (struct grub_afs_data
));
467 /* Read the superblock. */
468 if (grub_disk_read (disk
, 1 * 2, 0, sizeof (struct grub_afs_sblock
),
469 (char *) &data
->sblock
))
472 if (! grub_afs_validate_sblock (&data
->sblock
))
474 if (grub_disk_read (disk
, 1 * 2, 0, sizeof (struct grub_afs_sblock
),
475 (char *) &data
->sblock
))
478 if (! grub_afs_validate_sblock (&data
->sblock
))
482 data
->diropen
.data
= data
;
483 data
->inode
= &data
->diropen
.inode
;
486 if (grub_afs_read_inode (data
,
487 grub_afs_run_to_num (&data
->sblock
,
488 &data
->sblock
.root_dir
),
495 grub_error (GRUB_ERR_BAD_FS
, "not an afs filesystem");
501 grub_afs_open (struct grub_file
*file
, const char *name
)
503 struct grub_afs_data
*data
;
504 struct grub_fshelp_node
*fdiro
= 0;
506 grub_dl_ref (my_mod
);
508 data
= grub_afs_mount (file
->device
->disk
);
512 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_afs_iterate_dir
,
517 grub_memcpy (data
->inode
, &fdiro
->inode
, sizeof (struct grub_afs_inode
));
520 file
->size
= U64 (&data
->sblock
, data
->inode
->stream
.size
);
527 if (fdiro
!= &data
->diropen
)
531 grub_dl_unref (my_mod
);
537 grub_afs_read (grub_file_t file
, char *buf
, grub_size_t len
)
539 struct grub_afs_data
*data
= (struct grub_afs_data
*) file
->data
;
541 return grub_afs_read_file (&data
->diropen
, file
->read_hook
,
542 file
->offset
, len
, buf
);
546 grub_afs_close (grub_file_t file
)
548 grub_free (file
->data
);
550 grub_dl_unref (my_mod
);
552 return GRUB_ERR_NONE
;
556 grub_afs_dir (grub_device_t device
, const char *path
,
557 int (*hook
) (const char *filename
,
558 const struct grub_dirhook_info
*info
))
560 struct grub_afs_data
*data
= 0;;
561 struct grub_fshelp_node
*fdiro
= 0;
563 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
564 enum grub_fshelp_filetype filetype
,
565 grub_fshelp_node_t node
);
567 int NESTED_FUNC_ATTR
iterate (const char *filename
,
568 enum grub_fshelp_filetype filetype
,
569 grub_fshelp_node_t node
)
571 struct grub_dirhook_info info
;
572 grub_memset (&info
, 0, sizeof (info
));
573 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
575 return hook (filename
, &info
);
578 grub_dl_ref (my_mod
);
580 data
= grub_afs_mount (device
->disk
);
584 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_afs_iterate_dir
,
589 grub_afs_iterate_dir (fdiro
, iterate
);
592 if (fdiro
!= &data
->diropen
)
596 grub_dl_unref (my_mod
);
601 static struct grub_fs grub_afs_fs
= {
604 .open
= grub_afs_open
,
605 .read
= grub_afs_read
,
606 .close
= grub_afs_close
,
613 grub_fs_register (&grub_afs_fs
);
619 grub_fs_unregister (&grub_afs_fs
);