Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / ext2.c
blobc50e379d6ccc152ba973538af36bc8ffdcc1a55a
1 /* ext2.c - Second Extended filesystem */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,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/>.
20 /* Magic value used to identify an ext2 filesystem. */
21 #define EXT2_MAGIC 0xEF53
22 /* Amount of indirect blocks in an inode. */
23 #define INDIRECT_BLOCKS 12
25 /* The good old revision and the default inode size. */
26 #define EXT2_GOOD_OLD_REVISION 0
27 #define EXT2_GOOD_OLD_INODE_SIZE 128
29 /* Filetype used in directory entry. */
30 #define FILETYPE_UNKNOWN 0
31 #define FILETYPE_REG 1
32 #define FILETYPE_DIRECTORY 2
33 #define FILETYPE_SYMLINK 7
35 /* Filetype information as used in inodes. */
36 #define FILETYPE_INO_MASK 0170000
37 #define FILETYPE_INO_REG 0100000
38 #define FILETYPE_INO_DIRECTORY 0040000
39 #define FILETYPE_INO_SYMLINK 0120000
41 #include <grub/err.h>
42 #include <grub/file.h>
43 #include <grub/mm.h>
44 #include <grub/misc.h>
45 #include <grub/disk.h>
46 #include <grub/dl.h>
47 #include <grub/types.h>
48 #include <grub/fshelp.h>
50 GRUB_MOD_LICENSE ("GPLv3+");
52 /* Log2 size of ext2 block in 512 blocks. */
53 #define LOG2_EXT2_BLOCK_SIZE(data) \
54 (grub_le_to_cpu32 (data->sblock.log2_block_size) + 1)
56 /* Log2 size of ext2 block in bytes. */
57 #define LOG2_BLOCK_SIZE(data) \
58 (grub_le_to_cpu32 (data->sblock.log2_block_size) + 10)
60 /* The size of an ext2 block in bytes. */
61 #define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data))
63 /* The revision level. */
64 #define EXT2_REVISION(data) grub_le_to_cpu32 (data->sblock.revision_level)
66 /* The inode size. */
67 #define EXT2_INODE_SIZE(data) \
68 (EXT2_REVISION (data) == EXT2_GOOD_OLD_REVISION \
69 ? EXT2_GOOD_OLD_INODE_SIZE \
70 : grub_le_to_cpu16 (data->sblock.inode_size))
72 /* Superblock filesystem feature flags (RW compatible)
73 * A filesystem with any of these enabled can be read and written by a driver
74 * that does not understand them without causing metadata/data corruption. */
75 #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
76 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
77 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
78 #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
79 #define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
80 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
81 /* Superblock filesystem feature flags (RO compatible)
82 * A filesystem with any of these enabled can be safely read by a driver that
83 * does not understand them, but should not be written to, usually because
84 * additional metadata is required. */
85 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
86 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
87 #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
88 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
89 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
90 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
91 /* Superblock filesystem feature flags (back-incompatible)
92 * A filesystem with any of these enabled should not be attempted to be read
93 * by a driver that does not understand them, since they usually indicate
94 * metadata format changes that might confuse the reader. */
95 #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
96 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
97 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
98 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Volume is journal device */
99 #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
100 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* Extents used */
101 #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
102 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
104 /* The set of back-incompatible features this driver DOES support. Add (OR)
105 * flags here as the related features are implemented into the driver. */
106 #define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \
107 | EXT4_FEATURE_INCOMPAT_EXTENTS \
108 | EXT4_FEATURE_INCOMPAT_FLEX_BG )
109 /* List of rationales for the ignored "incompatible" features:
110 * needs_recovery: Not really back-incompatible - was added as such to forbid
111 * ext2 drivers from mounting an ext3 volume with a dirty
112 * journal because they will ignore the journal, but the next
113 * ext3 driver to mount the volume will find the journal and
114 * replay it, potentially corrupting the metadata written by
115 * the ext2 drivers. Safe to ignore for this RO driver. */
116 #define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER )
119 #define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U
121 #define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1
122 #define EXT3_JOURNAL_COMMIT_BLOCK 2
123 #define EXT3_JOURNAL_SUPERBLOCK_V1 3
124 #define EXT3_JOURNAL_SUPERBLOCK_V2 4
125 #define EXT3_JOURNAL_REVOKE_BLOCK 5
127 #define EXT3_JOURNAL_FLAG_ESCAPE 1
128 #define EXT3_JOURNAL_FLAG_SAME_UUID 2
129 #define EXT3_JOURNAL_FLAG_DELETED 4
130 #define EXT3_JOURNAL_FLAG_LAST_TAG 8
132 #define EXT4_EXTENTS_FLAG 0x80000
134 /* The ext2 superblock. */
135 struct grub_ext2_sblock
137 grub_uint32_t total_inodes;
138 grub_uint32_t total_blocks;
139 grub_uint32_t reserved_blocks;
140 grub_uint32_t free_blocks;
141 grub_uint32_t free_inodes;
142 grub_uint32_t first_data_block;
143 grub_uint32_t log2_block_size;
144 grub_uint32_t log2_fragment_size;
145 grub_uint32_t blocks_per_group;
146 grub_uint32_t fragments_per_group;
147 grub_uint32_t inodes_per_group;
148 grub_uint32_t mtime;
149 grub_uint32_t utime;
150 grub_uint16_t mnt_count;
151 grub_uint16_t max_mnt_count;
152 grub_uint16_t magic;
153 grub_uint16_t fs_state;
154 grub_uint16_t error_handling;
155 grub_uint16_t minor_revision_level;
156 grub_uint32_t lastcheck;
157 grub_uint32_t checkinterval;
158 grub_uint32_t creator_os;
159 grub_uint32_t revision_level;
160 grub_uint16_t uid_reserved;
161 grub_uint16_t gid_reserved;
162 grub_uint32_t first_inode;
163 grub_uint16_t inode_size;
164 grub_uint16_t block_group_number;
165 grub_uint32_t feature_compatibility;
166 grub_uint32_t feature_incompat;
167 grub_uint32_t feature_ro_compat;
168 grub_uint16_t uuid[8];
169 char volume_name[16];
170 char last_mounted_on[64];
171 grub_uint32_t compression_info;
172 grub_uint8_t prealloc_blocks;
173 grub_uint8_t prealloc_dir_blocks;
174 grub_uint16_t reserved_gdt_blocks;
175 grub_uint8_t journal_uuid[16];
176 grub_uint32_t journal_inum;
177 grub_uint32_t journal_dev;
178 grub_uint32_t last_orphan;
179 grub_uint32_t hash_seed[4];
180 grub_uint8_t def_hash_version;
181 grub_uint8_t jnl_backup_type;
182 grub_uint16_t reserved_word_pad;
183 grub_uint32_t default_mount_opts;
184 grub_uint32_t first_meta_bg;
185 grub_uint32_t mkfs_time;
186 grub_uint32_t jnl_blocks[17];
189 /* The ext2 blockgroup. */
190 struct grub_ext2_block_group
192 grub_uint32_t block_id;
193 grub_uint32_t inode_id;
194 grub_uint32_t inode_table_id;
195 grub_uint16_t free_blocks;
196 grub_uint16_t free_inodes;
197 grub_uint16_t used_dirs;
198 grub_uint16_t pad;
199 grub_uint32_t reserved[3];
202 /* The ext2 inode. */
203 struct grub_ext2_inode
205 grub_uint16_t mode;
206 grub_uint16_t uid;
207 grub_uint32_t size;
208 grub_uint32_t atime;
209 grub_uint32_t ctime;
210 grub_uint32_t mtime;
211 grub_uint32_t dtime;
212 grub_uint16_t gid;
213 grub_uint16_t nlinks;
214 grub_uint32_t blockcnt; /* Blocks of 512 bytes!! */
215 grub_uint32_t flags;
216 grub_uint32_t osd1;
217 union
219 struct datablocks
221 grub_uint32_t dir_blocks[INDIRECT_BLOCKS];
222 grub_uint32_t indir_block;
223 grub_uint32_t double_indir_block;
224 grub_uint32_t triple_indir_block;
225 } blocks;
226 char symlink[60];
228 grub_uint32_t version;
229 grub_uint32_t acl;
230 grub_uint32_t size_high;
231 grub_uint32_t fragment_addr;
232 grub_uint32_t osd2[3];
235 /* The header of an ext2 directory entry. */
236 struct ext2_dirent
238 grub_uint32_t inode;
239 grub_uint16_t direntlen;
240 grub_uint8_t namelen;
241 grub_uint8_t filetype;
244 struct grub_ext3_journal_header
246 grub_uint32_t magic;
247 grub_uint32_t block_type;
248 grub_uint32_t sequence;
251 struct grub_ext3_journal_revoke_header
253 struct grub_ext3_journal_header header;
254 grub_uint32_t count;
255 grub_uint32_t data[0];
258 struct grub_ext3_journal_block_tag
260 grub_uint32_t block;
261 grub_uint32_t flags;
264 struct grub_ext3_journal_sblock
266 struct grub_ext3_journal_header header;
267 grub_uint32_t block_size;
268 grub_uint32_t maxlen;
269 grub_uint32_t first;
270 grub_uint32_t sequence;
271 grub_uint32_t start;
274 #define EXT4_EXT_MAGIC 0xf30a
276 struct grub_ext4_extent_header
278 grub_uint16_t magic;
279 grub_uint16_t entries;
280 grub_uint16_t max;
281 grub_uint16_t depth;
282 grub_uint32_t generation;
285 struct grub_ext4_extent
287 grub_uint32_t block;
288 grub_uint16_t len;
289 grub_uint16_t start_hi;
290 grub_uint32_t start;
293 struct grub_ext4_extent_idx
295 grub_uint32_t block;
296 grub_uint32_t leaf;
297 grub_uint16_t leaf_hi;
298 grub_uint16_t unused;
301 struct grub_fshelp_node
303 struct grub_ext2_data *data;
304 struct grub_ext2_inode inode;
305 int ino;
306 int inode_read;
309 /* Information about a "mounted" ext2 filesystem. */
310 struct grub_ext2_data
312 struct grub_ext2_sblock sblock;
313 grub_disk_t disk;
314 struct grub_ext2_inode *inode;
315 struct grub_fshelp_node diropen;
318 static grub_dl_t my_mod;
322 /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
323 the mounted filesystem DATA. */
324 inline static grub_err_t
325 grub_ext2_blockgroup (struct grub_ext2_data *data, int group,
326 struct grub_ext2_block_group *blkgrp)
328 return grub_disk_read (data->disk,
329 ((grub_le_to_cpu32 (data->sblock.first_data_block) + 1)
330 << LOG2_EXT2_BLOCK_SIZE (data)),
331 group * sizeof (struct grub_ext2_block_group),
332 sizeof (struct grub_ext2_block_group), blkgrp);
335 static struct grub_ext4_extent_header *
336 grub_ext4_find_leaf (struct grub_ext2_data *data, grub_properly_aligned_t *buf,
337 struct grub_ext4_extent_header *ext_block,
338 grub_uint32_t fileblock)
340 struct grub_ext4_extent_idx *index;
342 while (1)
344 int i;
345 grub_disk_addr_t block;
347 index = (struct grub_ext4_extent_idx *) (ext_block + 1);
349 if (grub_le_to_cpu16(ext_block->magic) != EXT4_EXT_MAGIC)
350 return 0;
352 if (ext_block->depth == 0)
353 return ext_block;
355 for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++)
357 if (fileblock < grub_le_to_cpu32(index[i].block))
358 break;
361 if (--i < 0)
362 return 0;
364 block = grub_le_to_cpu16 (index[i].leaf_hi);
365 block = (block << 32) + grub_le_to_cpu32 (index[i].leaf);
366 if (grub_disk_read (data->disk,
367 block << LOG2_EXT2_BLOCK_SIZE (data),
368 0, EXT2_BLOCK_SIZE(data), buf))
369 return 0;
371 ext_block = (struct grub_ext4_extent_header *) buf;
375 static grub_disk_addr_t
376 grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
378 struct grub_ext2_data *data = node->data;
379 struct grub_ext2_inode *inode = &node->inode;
380 int blknr = -1;
381 unsigned int blksz = EXT2_BLOCK_SIZE (data);
382 int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
384 if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG)
386 GRUB_PROPERLY_ALIGNED_ARRAY (buf, EXT2_BLOCK_SIZE(data));
387 struct grub_ext4_extent_header *leaf;
388 struct grub_ext4_extent *ext;
389 int i;
391 leaf = grub_ext4_find_leaf (data, buf,
392 (struct grub_ext4_extent_header *) inode->blocks.dir_blocks,
393 fileblock);
394 if (! leaf)
396 grub_error (GRUB_ERR_BAD_FS, "invalid extent");
397 return -1;
400 ext = (struct grub_ext4_extent *) (leaf + 1);
401 for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++)
403 if (fileblock < grub_le_to_cpu32 (ext[i].block))
404 break;
407 if (--i >= 0)
409 fileblock -= grub_le_to_cpu32 (ext[i].block);
410 if (fileblock >= grub_le_to_cpu16 (ext[i].len))
411 return 0;
412 else
414 grub_disk_addr_t start;
416 start = grub_le_to_cpu16 (ext[i].start_hi);
417 start = (start << 32) + grub_le_to_cpu32 (ext[i].start);
419 return fileblock + start;
422 else
424 grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
425 return -1;
428 /* Direct blocks. */
429 if (fileblock < INDIRECT_BLOCKS)
430 blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
431 /* Indirect. */
432 else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
434 grub_uint32_t indir[blksz / 4];
436 if (grub_disk_read (data->disk,
437 ((grub_disk_addr_t)
438 grub_le_to_cpu32 (inode->blocks.indir_block))
439 << log2_blksz,
440 0, blksz, indir))
441 return grub_errno;
443 blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
445 /* Double indirect. */
446 else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1))
448 unsigned int perblock = blksz / 4;
449 unsigned int rblock = fileblock - (INDIRECT_BLOCKS
450 + blksz / 4);
451 grub_uint32_t indir[blksz / 4];
453 if (grub_disk_read (data->disk,
454 ((grub_disk_addr_t)
455 grub_le_to_cpu32 (inode->blocks.double_indir_block))
456 << log2_blksz,
457 0, blksz, indir))
458 return grub_errno;
460 if (grub_disk_read (data->disk,
461 ((grub_disk_addr_t)
462 grub_le_to_cpu32 (indir[rblock / perblock]))
463 << log2_blksz,
464 0, blksz, indir))
465 return grub_errno;
468 blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
470 /* triple indirect. */
471 else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1)
472 + (blksz / 4) * (blksz / 4) * (blksz / 4 + 1))
474 unsigned int perblock = blksz / 4;
475 unsigned int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4
476 * (blksz / 4 + 1));
477 grub_uint32_t indir[blksz / 4];
479 if (grub_disk_read (data->disk,
480 ((grub_disk_addr_t)
481 grub_le_to_cpu32 (inode->blocks.triple_indir_block))
482 << log2_blksz,
483 0, blksz, indir))
484 return grub_errno;
486 if (grub_disk_read (data->disk,
487 ((grub_disk_addr_t)
488 grub_le_to_cpu32 (indir[(rblock / perblock) / perblock]))
489 << log2_blksz,
490 0, blksz, indir))
491 return grub_errno;
493 if (grub_disk_read (data->disk,
494 ((grub_disk_addr_t)
495 grub_le_to_cpu32 (indir[(rblock / perblock) % perblock]))
496 << log2_blksz,
497 0, blksz, indir))
498 return grub_errno;
500 blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
502 else
504 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
505 "ext2fs doesn't support quadruple indirect blocks");
508 return blknr;
511 /* Read LEN bytes from the file described by DATA starting with byte
512 POS. Return the amount of read bytes in READ. */
513 static grub_ssize_t
514 grub_ext2_read_file (grub_fshelp_node_t node,
515 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
516 unsigned offset, unsigned length),
517 grub_off_t pos, grub_size_t len, char *buf)
519 return grub_fshelp_read_file (node->data->disk, node, read_hook,
520 pos, len, buf, grub_ext2_read_block,
521 grub_cpu_to_le32 (node->inode.size)
522 | (((grub_off_t) grub_cpu_to_le32 (node->inode.size_high)) << 32),
523 LOG2_EXT2_BLOCK_SIZE (node->data), 0);
528 /* Read the inode INO for the file described by DATA into INODE. */
529 static grub_err_t
530 grub_ext2_read_inode (struct grub_ext2_data *data,
531 int ino, struct grub_ext2_inode *inode)
533 struct grub_ext2_block_group blkgrp;
534 struct grub_ext2_sblock *sblock = &data->sblock;
535 int inodes_per_block;
536 unsigned int blkno;
537 unsigned int blkoff;
539 /* It is easier to calculate if the first inode is 0. */
540 ino--;
542 grub_ext2_blockgroup (data,
543 ino / grub_le_to_cpu32 (sblock->inodes_per_group),
544 &blkgrp);
545 if (grub_errno)
546 return grub_errno;
548 inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
549 blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
550 / inodes_per_block;
551 blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
552 % inodes_per_block;
554 /* Read the inode. */
555 if (grub_disk_read (data->disk,
556 (((grub_disk_addr_t) grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno)
557 << LOG2_EXT2_BLOCK_SIZE (data)),
558 EXT2_INODE_SIZE (data) * blkoff,
559 sizeof (struct grub_ext2_inode), inode))
560 return grub_errno;
562 return 0;
565 static struct grub_ext2_data *
566 grub_ext2_mount (grub_disk_t disk)
568 struct grub_ext2_data *data;
570 data = grub_malloc (sizeof (struct grub_ext2_data));
571 if (!data)
572 return 0;
574 /* Read the superblock. */
575 grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
576 &data->sblock);
577 if (grub_errno)
578 goto fail;
580 /* Make sure this is an ext2 filesystem. */
581 if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC
582 || grub_le_to_cpu32 (data->sblock.log2_block_size) >= 16)
584 grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
585 goto fail;
588 /* Check the FS doesn't have feature bits enabled that we don't support. */
589 if (grub_le_to_cpu32 (data->sblock.feature_incompat)
590 & ~(EXT2_DRIVER_SUPPORTED_INCOMPAT | EXT2_DRIVER_IGNORED_INCOMPAT))
592 grub_error (GRUB_ERR_BAD_FS, "filesystem has unsupported incompatible features");
593 goto fail;
597 data->disk = disk;
599 data->diropen.data = data;
600 data->diropen.ino = 2;
601 data->diropen.inode_read = 1;
603 data->inode = &data->diropen.inode;
605 grub_ext2_read_inode (data, 2, data->inode);
606 if (grub_errno)
607 goto fail;
609 return data;
611 fail:
612 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
613 grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
615 grub_free (data);
616 return 0;
619 static char *
620 grub_ext2_read_symlink (grub_fshelp_node_t node)
622 char *symlink;
623 struct grub_fshelp_node *diro = node;
625 if (! diro->inode_read)
627 grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
628 if (grub_errno)
629 return 0;
632 symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
633 if (! symlink)
634 return 0;
636 /* If the filesize of the symlink is bigger than
637 60 the symlink is stored in a separate block,
638 otherwise it is stored in the inode. */
639 if (grub_le_to_cpu32 (diro->inode.size) <= 60)
640 grub_strncpy (symlink,
641 diro->inode.symlink,
642 grub_le_to_cpu32 (diro->inode.size));
643 else
645 grub_ext2_read_file (diro, 0, 0,
646 grub_le_to_cpu32 (diro->inode.size),
647 symlink);
648 if (grub_errno)
650 grub_free (symlink);
651 return 0;
655 symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
656 return symlink;
659 static int
660 grub_ext2_iterate_dir (grub_fshelp_node_t dir,
661 int NESTED_FUNC_ATTR
662 (*hook) (const char *filename,
663 enum grub_fshelp_filetype filetype,
664 grub_fshelp_node_t node))
666 unsigned int fpos = 0;
667 struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
669 if (! diro->inode_read)
671 grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
672 if (grub_errno)
673 return 0;
676 /* Search the file. */
677 while (fpos < grub_le_to_cpu32 (diro->inode.size))
679 struct ext2_dirent dirent;
681 grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent),
682 (char *) &dirent);
683 if (grub_errno)
684 return 0;
686 if (dirent.direntlen == 0)
687 return 0;
689 if (dirent.inode != 0 && dirent.namelen != 0)
691 char filename[dirent.namelen + 1];
692 struct grub_fshelp_node *fdiro;
693 enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
695 grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent),
696 dirent.namelen, filename);
697 if (grub_errno)
698 return 0;
700 fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
701 if (! fdiro)
702 return 0;
704 fdiro->data = diro->data;
705 fdiro->ino = grub_le_to_cpu32 (dirent.inode);
707 filename[dirent.namelen] = '\0';
709 if (dirent.filetype != FILETYPE_UNKNOWN)
711 fdiro->inode_read = 0;
713 if (dirent.filetype == FILETYPE_DIRECTORY)
714 type = GRUB_FSHELP_DIR;
715 else if (dirent.filetype == FILETYPE_SYMLINK)
716 type = GRUB_FSHELP_SYMLINK;
717 else if (dirent.filetype == FILETYPE_REG)
718 type = GRUB_FSHELP_REG;
720 else
722 /* The filetype can not be read from the dirent, read
723 the inode to get more information. */
724 grub_ext2_read_inode (diro->data,
725 grub_le_to_cpu32 (dirent.inode),
726 &fdiro->inode);
727 if (grub_errno)
729 grub_free (fdiro);
730 return 0;
733 fdiro->inode_read = 1;
735 if ((grub_le_to_cpu16 (fdiro->inode.mode)
736 & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
737 type = GRUB_FSHELP_DIR;
738 else if ((grub_le_to_cpu16 (fdiro->inode.mode)
739 & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
740 type = GRUB_FSHELP_SYMLINK;
741 else if ((grub_le_to_cpu16 (fdiro->inode.mode)
742 & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
743 type = GRUB_FSHELP_REG;
746 if (hook (filename, type, fdiro))
747 return 1;
750 fpos += grub_le_to_cpu16 (dirent.direntlen);
753 return 0;
756 /* Open a file named NAME and initialize FILE. */
757 static grub_err_t
758 grub_ext2_open (struct grub_file *file, const char *name)
760 struct grub_ext2_data *data;
761 struct grub_fshelp_node *fdiro = 0;
762 grub_err_t err;
764 grub_dl_ref (my_mod);
766 data = grub_ext2_mount (file->device->disk);
767 if (! data)
769 err = grub_errno;
770 goto fail;
773 err = grub_fshelp_find_file (name, &data->diropen, &fdiro,
774 grub_ext2_iterate_dir,
775 grub_ext2_read_symlink, GRUB_FSHELP_REG);
776 if (err)
777 goto fail;
779 if (! fdiro->inode_read)
781 err = grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
782 if (err)
783 goto fail;
786 grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
787 grub_free (fdiro);
789 file->size = grub_le_to_cpu32 (data->inode->size);
790 file->size |= ((grub_off_t) grub_le_to_cpu32 (data->inode->size_high)) << 32;
791 file->data = data;
792 file->offset = 0;
794 return 0;
796 fail:
797 if (fdiro != &data->diropen)
798 grub_free (fdiro);
799 grub_free (data);
801 grub_dl_unref (my_mod);
803 return err;
806 static grub_err_t
807 grub_ext2_close (grub_file_t file)
809 grub_free (file->data);
811 grub_dl_unref (my_mod);
813 return GRUB_ERR_NONE;
816 /* Read LEN bytes data from FILE into BUF. */
817 static grub_ssize_t
818 grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
820 struct grub_ext2_data *data = (struct grub_ext2_data *) file->data;
822 return grub_ext2_read_file (&data->diropen, file->read_hook,
823 file->offset, len, buf);
827 static grub_err_t
828 grub_ext2_dir (grub_device_t device, const char *path,
829 int (*hook) (const char *filename,
830 const struct grub_dirhook_info *info))
832 struct grub_ext2_data *data = 0;
833 struct grub_fshelp_node *fdiro = 0;
835 auto int NESTED_FUNC_ATTR iterate (const char *filename,
836 enum grub_fshelp_filetype filetype,
837 grub_fshelp_node_t node);
839 int NESTED_FUNC_ATTR iterate (const char *filename,
840 enum grub_fshelp_filetype filetype,
841 grub_fshelp_node_t node)
843 struct grub_dirhook_info info;
844 grub_memset (&info, 0, sizeof (info));
845 if (! node->inode_read)
847 grub_ext2_read_inode (data, node->ino, &node->inode);
848 if (!grub_errno)
849 node->inode_read = 1;
850 grub_errno = GRUB_ERR_NONE;
852 if (node->inode_read)
854 info.mtimeset = 1;
855 info.mtime = grub_le_to_cpu32 (node->inode.mtime);
858 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
859 grub_free (node);
860 return hook (filename, &info);
863 grub_dl_ref (my_mod);
865 data = grub_ext2_mount (device->disk);
866 if (! data)
867 goto fail;
869 grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
870 grub_ext2_read_symlink, GRUB_FSHELP_DIR);
871 if (grub_errno)
872 goto fail;
874 grub_ext2_iterate_dir (fdiro, iterate);
876 fail:
877 if (fdiro != &data->diropen)
878 grub_free (fdiro);
879 grub_free (data);
881 grub_dl_unref (my_mod);
883 return grub_errno;
886 static grub_err_t
887 grub_ext2_label (grub_device_t device, char **label)
889 struct grub_ext2_data *data;
890 grub_disk_t disk = device->disk;
892 grub_dl_ref (my_mod);
894 data = grub_ext2_mount (disk);
895 if (data)
896 *label = grub_strndup (data->sblock.volume_name,
897 sizeof (data->sblock.volume_name));
898 else
899 *label = NULL;
901 grub_dl_unref (my_mod);
903 grub_free (data);
905 return grub_errno;
908 static grub_err_t
909 grub_ext2_uuid (grub_device_t device, char **uuid)
911 struct grub_ext2_data *data;
912 grub_disk_t disk = device->disk;
914 grub_dl_ref (my_mod);
916 data = grub_ext2_mount (disk);
917 if (data)
919 *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
920 grub_be_to_cpu16 (data->sblock.uuid[0]),
921 grub_be_to_cpu16 (data->sblock.uuid[1]),
922 grub_be_to_cpu16 (data->sblock.uuid[2]),
923 grub_be_to_cpu16 (data->sblock.uuid[3]),
924 grub_be_to_cpu16 (data->sblock.uuid[4]),
925 grub_be_to_cpu16 (data->sblock.uuid[5]),
926 grub_be_to_cpu16 (data->sblock.uuid[6]),
927 grub_be_to_cpu16 (data->sblock.uuid[7]));
929 else
930 *uuid = NULL;
932 grub_dl_unref (my_mod);
934 grub_free (data);
936 return grub_errno;
939 /* Get mtime. */
940 static grub_err_t
941 grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
943 struct grub_ext2_data *data;
944 grub_disk_t disk = device->disk;
946 grub_dl_ref (my_mod);
948 data = grub_ext2_mount (disk);
949 if (!data)
950 *tm = 0;
951 else
952 *tm = grub_le_to_cpu32 (data->sblock.utime);
954 grub_dl_unref (my_mod);
956 grub_free (data);
958 return grub_errno;
964 static struct grub_fs grub_ext2_fs =
966 .name = "ext2",
967 .dir = grub_ext2_dir,
968 .open = grub_ext2_open,
969 .read = grub_ext2_read,
970 .close = grub_ext2_close,
971 .label = grub_ext2_label,
972 .uuid = grub_ext2_uuid,
973 .mtime = grub_ext2_mtime,
974 #ifdef GRUB_UTIL
975 .reserved_first_sector = 1,
976 .blocklist_install = 1,
977 #endif
978 .next = 0
981 GRUB_MOD_INIT(ext2)
983 grub_fs_register (&grub_ext2_fs);
984 my_mod = mod;
987 GRUB_MOD_FINI(ext2)
989 grub_fs_unregister (&grub_ext2_fs);