1 /* ext2.c - Second Extended filesystem */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,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/>.
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
24 /* Maximum length of a pathname. */
25 #define EXT2_PATH_MAX 4096
26 /* Maximum nesting of symlinks, used to prevent a loop. */
27 #define EXT2_MAX_SYMLINKCNT 8
29 /* The good old revision and the default inode size. */
30 #define EXT2_GOOD_OLD_REVISION 0
31 #define EXT2_GOOD_OLD_INODE_SIZE 128
33 /* Filetype used in directory entry. */
34 #define FILETYPE_UNKNOWN 0
35 #define FILETYPE_REG 1
36 #define FILETYPE_DIRECTORY 2
37 #define FILETYPE_SYMLINK 7
39 /* Filetype information as used in inodes. */
40 #define FILETYPE_INO_MASK 0170000
41 #define FILETYPE_INO_REG 0100000
42 #define FILETYPE_INO_DIRECTORY 0040000
43 #define FILETYPE_INO_SYMLINK 0120000
46 #include <grub/file.h>
48 #include <grub/misc.h>
49 #include <grub/disk.h>
51 #include <grub/types.h>
52 #include <grub/fshelp.h>
54 /* Log2 size of ext2 block in 512 blocks. */
55 #define LOG2_EXT2_BLOCK_SIZE(data) \
56 (grub_le_to_cpu32 (data->sblock.log2_block_size) + 1)
58 /* Log2 size of ext2 block in bytes. */
59 #define LOG2_BLOCK_SIZE(data) \
60 (grub_le_to_cpu32 (data->sblock.log2_block_size) + 10)
62 /* The size of an ext2 block in bytes. */
63 #define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data))
65 /* The revision level. */
66 #define EXT2_REVISION(data) grub_le_to_cpu32 (data->sblock.revision_level)
69 #define EXT2_INODE_SIZE(data) \
70 (EXT2_REVISION (data) == EXT2_GOOD_OLD_REVISION \
71 ? EXT2_GOOD_OLD_INODE_SIZE \
72 : grub_le_to_cpu16 (data->sblock.inode_size))
74 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
76 #define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U
78 #define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1
79 #define EXT3_JOURNAL_COMMIT_BLOCK 2
80 #define EXT3_JOURNAL_SUPERBLOCK_V1 3
81 #define EXT3_JOURNAL_SUPERBLOCK_V2 4
82 #define EXT3_JOURNAL_REVOKE_BLOCK 5
84 #define EXT3_JOURNAL_FLAG_ESCAPE 1
85 #define EXT3_JOURNAL_FLAG_SAME_UUID 2
86 #define EXT3_JOURNAL_FLAG_DELETED 4
87 #define EXT3_JOURNAL_FLAG_LAST_TAG 8
89 #define EXT4_EXTENTS_FLAG 0x80000
91 /* The ext2 superblock. */
92 struct grub_ext2_sblock
94 grub_uint32_t total_inodes
;
95 grub_uint32_t total_blocks
;
96 grub_uint32_t reserved_blocks
;
97 grub_uint32_t free_blocks
;
98 grub_uint32_t free_inodes
;
99 grub_uint32_t first_data_block
;
100 grub_uint32_t log2_block_size
;
101 grub_uint32_t log2_fragment_size
;
102 grub_uint32_t blocks_per_group
;
103 grub_uint32_t fragments_per_group
;
104 grub_uint32_t inodes_per_group
;
107 grub_uint16_t mnt_count
;
108 grub_uint16_t max_mnt_count
;
110 grub_uint16_t fs_state
;
111 grub_uint16_t error_handling
;
112 grub_uint16_t minor_revision_level
;
113 grub_uint32_t lastcheck
;
114 grub_uint32_t checkinterval
;
115 grub_uint32_t creator_os
;
116 grub_uint32_t revision_level
;
117 grub_uint16_t uid_reserved
;
118 grub_uint16_t gid_reserved
;
119 grub_uint32_t first_inode
;
120 grub_uint16_t inode_size
;
121 grub_uint16_t block_group_number
;
122 grub_uint32_t feature_compatibility
;
123 grub_uint32_t feature_incompat
;
124 grub_uint32_t feature_ro_compat
;
125 grub_uint16_t uuid
[8];
126 char volume_name
[16];
127 char last_mounted_on
[64];
128 grub_uint32_t compression_info
;
129 grub_uint8_t prealloc_blocks
;
130 grub_uint8_t prealloc_dir_blocks
;
131 grub_uint16_t reserved_gdt_blocks
;
132 grub_uint8_t journal_uuid
[16];
133 grub_uint32_t journal_inum
;
134 grub_uint32_t journal_dev
;
135 grub_uint32_t last_orphan
;
136 grub_uint32_t hash_seed
[4];
137 grub_uint8_t def_hash_version
;
138 grub_uint8_t jnl_backup_type
;
139 grub_uint16_t reserved_word_pad
;
140 grub_uint32_t default_mount_opts
;
141 grub_uint32_t first_meta_bg
;
142 grub_uint32_t mkfs_time
;
143 grub_uint32_t jnl_blocks
[17];
146 /* The ext2 blockgroup. */
147 struct grub_ext2_block_group
149 grub_uint32_t block_id
;
150 grub_uint32_t inode_id
;
151 grub_uint32_t inode_table_id
;
152 grub_uint16_t free_blocks
;
153 grub_uint16_t free_inodes
;
154 grub_uint16_t used_dirs
;
156 grub_uint32_t reserved
[3];
159 /* The ext2 inode. */
160 struct grub_ext2_inode
170 grub_uint16_t nlinks
;
171 grub_uint32_t blockcnt
; /* Blocks of 512 bytes!! */
178 grub_uint32_t dir_blocks
[INDIRECT_BLOCKS
];
179 grub_uint32_t indir_block
;
180 grub_uint32_t double_indir_block
;
181 grub_uint32_t triple_indir_block
;
185 grub_uint32_t version
;
187 grub_uint32_t dir_acl
;
188 grub_uint32_t fragment_addr
;
189 grub_uint32_t osd2
[3];
192 /* The header of an ext2 directory entry. */
196 grub_uint16_t direntlen
;
197 grub_uint8_t namelen
;
198 grub_uint8_t filetype
;
201 struct grub_ext3_journal_header
204 grub_uint32_t block_type
;
205 grub_uint32_t sequence
;
208 struct grub_ext3_journal_revoke_header
210 struct grub_ext3_journal_header header
;
212 grub_uint32_t data
[0];
215 struct grub_ext3_journal_block_tag
221 struct grub_ext3_journal_sblock
223 struct grub_ext3_journal_header header
;
224 grub_uint32_t block_size
;
225 grub_uint32_t maxlen
;
227 grub_uint32_t sequence
;
231 #define EXT4_EXT_MAGIC 0xf30a
233 struct grub_ext4_extent_header
236 grub_uint16_t entries
;
239 grub_uint32_t generation
;
242 struct grub_ext4_extent
246 grub_uint16_t start_hi
;
250 struct grub_ext4_extent_idx
254 grub_uint16_t leaf_hi
;
255 grub_uint16_t unused
;
258 struct grub_fshelp_node
260 struct grub_ext2_data
*data
;
261 struct grub_ext2_inode inode
;
266 /* Information about a "mounted" ext2 filesystem. */
267 struct grub_ext2_data
269 struct grub_ext2_sblock sblock
;
271 struct grub_ext2_inode
*inode
;
272 struct grub_fshelp_node diropen
;
276 static grub_dl_t my_mod
;
281 /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
282 the mounted filesystem DATA. */
283 inline static grub_err_t
284 grub_ext2_blockgroup (struct grub_ext2_data
*data
, int group
,
285 struct grub_ext2_block_group
*blkgrp
)
287 return grub_disk_read (data
->disk
,
288 ((grub_le_to_cpu32 (data
->sblock
.first_data_block
) + 1)
289 << LOG2_EXT2_BLOCK_SIZE (data
)),
290 group
* sizeof (struct grub_ext2_block_group
),
291 sizeof (struct grub_ext2_block_group
), (char *) blkgrp
);
294 static struct grub_ext4_extent_header
*
295 grub_ext4_find_leaf (struct grub_ext2_data
*data
, char *buf
,
296 struct grub_ext4_extent_header
*ext_block
,
297 grub_uint32_t fileblock
)
299 struct grub_ext4_extent_idx
*index
;
304 grub_disk_addr_t block
;
306 index
= (struct grub_ext4_extent_idx
*) (ext_block
+ 1);
308 if (grub_le_to_cpu16(ext_block
->magic
) != EXT4_EXT_MAGIC
)
311 if (ext_block
->depth
== 0)
314 for (i
= 0; i
< grub_le_to_cpu16 (ext_block
->entries
); i
++)
316 if (fileblock
< grub_le_to_cpu32(index
[i
].block
))
323 block
= grub_le_to_cpu16 (index
[i
].leaf_hi
);
324 block
= (block
<< 32) + grub_le_to_cpu32 (index
[i
].leaf
);
325 if (grub_disk_read (data
->disk
,
326 block
<< LOG2_EXT2_BLOCK_SIZE (data
),
327 0, EXT2_BLOCK_SIZE(data
), buf
))
330 ext_block
= (struct grub_ext4_extent_header
*) buf
;
334 static grub_disk_addr_t
335 grub_ext2_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
337 struct grub_ext2_data
*data
= node
->data
;
338 struct grub_ext2_inode
*inode
= &node
->inode
;
340 unsigned int blksz
= EXT2_BLOCK_SIZE (data
);
341 int log2_blksz
= LOG2_EXT2_BLOCK_SIZE (data
);
343 if (inode
->flags
& EXT4_EXTENTS_FLAG
)
345 char buf
[EXT2_BLOCK_SIZE(data
)];
346 struct grub_ext4_extent_header
*leaf
;
347 struct grub_ext4_extent
*ext
;
350 leaf
= grub_ext4_find_leaf (data
, buf
,
351 (struct grub_ext4_extent_header
*) inode
->blocks
.dir_blocks
,
355 grub_error (GRUB_ERR_BAD_FS
, "invalid extent");
359 ext
= (struct grub_ext4_extent
*) (leaf
+ 1);
360 for (i
= 0; i
< grub_le_to_cpu16 (leaf
->entries
); i
++)
362 if (fileblock
< grub_le_to_cpu32 (ext
[i
].block
))
368 fileblock
-= grub_le_to_cpu32 (ext
[i
].block
);
369 if (fileblock
>= grub_le_to_cpu16 (ext
[i
].len
))
373 grub_disk_addr_t start
;
375 start
= grub_le_to_cpu16 (ext
[i
].start_hi
);
376 start
= (start
<< 32) + grub_le_to_cpu32 (ext
[i
].start
);
378 return fileblock
+ start
;
383 grub_error (GRUB_ERR_BAD_FS
, "something wrong with extent");
388 if (fileblock
< INDIRECT_BLOCKS
)
389 blknr
= grub_le_to_cpu32 (inode
->blocks
.dir_blocks
[fileblock
]);
391 else if (fileblock
< INDIRECT_BLOCKS
+ blksz
/ 4)
393 grub_uint32_t indir
[blksz
/ 4];
395 if (grub_disk_read (data
->disk
,
396 grub_le_to_cpu32 (inode
->blocks
.indir_block
)
398 0, blksz
, (char *) indir
))
401 blknr
= grub_le_to_cpu32 (indir
[fileblock
- INDIRECT_BLOCKS
]);
403 /* Double indirect. */
404 else if (fileblock
< INDIRECT_BLOCKS
+ blksz
/ 4 * (blksz
/ 4 + 1))
406 unsigned int perblock
= blksz
/ 4;
407 unsigned int rblock
= fileblock
- (INDIRECT_BLOCKS
409 grub_uint32_t indir
[blksz
/ 4];
411 if (grub_disk_read (data
->disk
,
412 grub_le_to_cpu32 (inode
->blocks
.double_indir_block
)
414 0, blksz
, (char *) indir
))
417 if (grub_disk_read (data
->disk
,
418 grub_le_to_cpu32 (indir
[rblock
/ perblock
])
420 0, blksz
, (char *) indir
))
424 blknr
= grub_le_to_cpu32 (indir
[rblock
% perblock
]);
426 /* triple indirect. */
429 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
430 "ext2fs doesn't support triple indirect blocks");
436 /* Read LEN bytes from the file described by DATA starting with byte
437 POS. Return the amount of read bytes in READ. */
439 grub_ext2_read_file (grub_fshelp_node_t node
,
440 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
441 unsigned offset
, unsigned length
),
442 int pos
, grub_size_t len
, char *buf
)
444 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
445 pos
, len
, buf
, grub_ext2_read_block
,
447 LOG2_EXT2_BLOCK_SIZE (node
->data
));
452 /* Read the inode INO for the file described by DATA into INODE. */
454 grub_ext2_read_inode (struct grub_ext2_data
*data
,
455 int ino
, struct grub_ext2_inode
*inode
)
457 struct grub_ext2_block_group blkgrp
;
458 struct grub_ext2_sblock
*sblock
= &data
->sblock
;
459 int inodes_per_block
;
463 /* It is easier to calculate if the first inode is 0. */
466 grub_ext2_blockgroup (data
,
467 ino
/ grub_le_to_cpu32 (sblock
->inodes_per_group
),
472 inodes_per_block
= EXT2_BLOCK_SIZE (data
) / EXT2_INODE_SIZE (data
);
473 blkno
= (ino
% grub_le_to_cpu32 (sblock
->inodes_per_group
))
475 blkoff
= (ino
% grub_le_to_cpu32 (sblock
->inodes_per_group
))
478 /* Read the inode. */
479 if (grub_disk_read (data
->disk
,
480 ((grub_le_to_cpu32 (blkgrp
.inode_table_id
) + blkno
)
481 << LOG2_EXT2_BLOCK_SIZE (data
)),
482 EXT2_INODE_SIZE (data
) * blkoff
,
483 sizeof (struct grub_ext2_inode
), (char *) inode
))
489 static struct grub_ext2_data
*
490 grub_ext2_mount (grub_disk_t disk
)
492 struct grub_ext2_data
*data
;
494 data
= grub_malloc (sizeof (struct grub_ext2_data
));
498 /* Read the superblock. */
499 grub_disk_read (disk
, 1 * 2, 0, sizeof (struct grub_ext2_sblock
),
500 (char *) &data
->sblock
);
504 /* Make sure this is an ext2 filesystem. */
505 if (grub_le_to_cpu16 (data
->sblock
.magic
) != EXT2_MAGIC
)
510 data
->diropen
.data
= data
;
511 data
->diropen
.ino
= 2;
512 data
->diropen
.inode_read
= 1;
514 data
->inode
= &data
->diropen
.inode
;
516 grub_ext2_read_inode (data
, 2, data
->inode
);
523 grub_error (GRUB_ERR_BAD_FS
, "not an ext2 filesystem");
529 grub_ext2_read_symlink (grub_fshelp_node_t node
)
532 struct grub_fshelp_node
*diro
= node
;
534 if (! diro
->inode_read
)
536 grub_ext2_read_inode (diro
->data
, diro
->ino
, &diro
->inode
);
541 symlink
= grub_malloc (grub_le_to_cpu32 (diro
->inode
.size
) + 1);
545 /* If the filesize of the symlink is bigger than
546 60 the symlink is stored in a separate block,
547 otherwise it is stored in the inode. */
548 if (grub_le_to_cpu32 (diro
->inode
.size
) <= 60)
549 grub_strncpy (symlink
,
551 grub_le_to_cpu32 (diro
->inode
.size
));
554 grub_ext2_read_file (diro
, 0, 0,
555 grub_le_to_cpu32 (diro
->inode
.size
),
564 symlink
[grub_le_to_cpu32 (diro
->inode
.size
)] = '\0';
569 grub_ext2_iterate_dir (grub_fshelp_node_t dir
,
571 (*hook
) (const char *filename
,
572 enum grub_fshelp_filetype filetype
,
573 grub_fshelp_node_t node
))
575 unsigned int fpos
= 0;
576 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
578 if (! diro
->inode_read
)
580 grub_ext2_read_inode (diro
->data
, diro
->ino
, &diro
->inode
);
585 /* Search the file. */
586 while (fpos
< grub_le_to_cpu32 (diro
->inode
.size
))
588 struct ext2_dirent dirent
;
590 grub_ext2_read_file (diro
, 0, fpos
, sizeof (struct ext2_dirent
),
595 if (dirent
.namelen
!= 0)
597 char filename
[dirent
.namelen
+ 1];
598 struct grub_fshelp_node
*fdiro
;
599 enum grub_fshelp_filetype type
= GRUB_FSHELP_UNKNOWN
;
601 grub_ext2_read_file (diro
, 0, fpos
+ sizeof (struct ext2_dirent
),
602 dirent
.namelen
, filename
);
606 fdiro
= grub_malloc (sizeof (struct grub_fshelp_node
));
610 fdiro
->data
= diro
->data
;
611 fdiro
->ino
= grub_le_to_cpu32 (dirent
.inode
);
613 filename
[dirent
.namelen
] = '\0';
615 if (dirent
.filetype
!= FILETYPE_UNKNOWN
)
617 fdiro
->inode_read
= 0;
619 if (dirent
.filetype
== FILETYPE_DIRECTORY
)
620 type
= GRUB_FSHELP_DIR
;
621 else if (dirent
.filetype
== FILETYPE_SYMLINK
)
622 type
= GRUB_FSHELP_SYMLINK
;
623 else if (dirent
.filetype
== FILETYPE_REG
)
624 type
= GRUB_FSHELP_REG
;
628 /* The filetype can not be read from the dirent, read
629 the inode to get more information. */
630 grub_ext2_read_inode (diro
->data
,
631 grub_le_to_cpu32 (dirent
.inode
),
639 fdiro
->inode_read
= 1;
641 if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
642 & FILETYPE_INO_MASK
) == FILETYPE_INO_DIRECTORY
)
643 type
= GRUB_FSHELP_DIR
;
644 else if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
645 & FILETYPE_INO_MASK
) == FILETYPE_INO_SYMLINK
)
646 type
= GRUB_FSHELP_SYMLINK
;
647 else if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
648 & FILETYPE_INO_MASK
) == FILETYPE_INO_REG
)
649 type
= GRUB_FSHELP_REG
;
652 if (hook (filename
, type
, fdiro
))
656 fpos
+= grub_le_to_cpu16 (dirent
.direntlen
);
662 /* Open a file named NAME and initialize FILE. */
664 grub_ext2_open (struct grub_file
*file
, const char *name
)
666 struct grub_ext2_data
*data
;
667 struct grub_fshelp_node
*fdiro
= 0;
670 grub_dl_ref (my_mod
);
673 data
= grub_ext2_mount (file
->device
->disk
);
677 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_ext2_iterate_dir
,
678 grub_ext2_read_symlink
, GRUB_FSHELP_REG
);
682 if (! fdiro
->inode_read
)
684 grub_ext2_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
689 grub_memcpy (data
->inode
, &fdiro
->inode
, sizeof (struct grub_ext2_inode
));
692 file
->size
= grub_le_to_cpu32 (data
->inode
->size
);
699 if (fdiro
!= &data
->diropen
)
704 grub_dl_unref (my_mod
);
711 grub_ext2_close (grub_file_t file
)
713 grub_free (file
->data
);
716 grub_dl_unref (my_mod
);
719 return GRUB_ERR_NONE
;
722 /* Read LEN bytes data from FILE into BUF. */
724 grub_ext2_read (grub_file_t file
, char *buf
, grub_size_t len
)
726 struct grub_ext2_data
*data
= (struct grub_ext2_data
*) file
->data
;
728 return grub_ext2_read_file (&data
->diropen
, file
->read_hook
,
729 file
->offset
, len
, buf
);
734 grub_ext2_dir (grub_device_t device
, const char *path
,
735 int (*hook
) (const char *filename
, int dir
))
737 struct grub_ext2_data
*data
= 0;;
738 struct grub_fshelp_node
*fdiro
= 0;
740 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
741 enum grub_fshelp_filetype filetype
,
742 grub_fshelp_node_t node
);
744 int NESTED_FUNC_ATTR
iterate (const char *filename
,
745 enum grub_fshelp_filetype filetype
,
746 grub_fshelp_node_t node
)
750 if (filetype
== GRUB_FSHELP_DIR
)
751 return hook (filename
, 1);
753 return hook (filename
, 0);
759 grub_dl_ref (my_mod
);
762 data
= grub_ext2_mount (device
->disk
);
766 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_ext2_iterate_dir
,
767 grub_ext2_read_symlink
, GRUB_FSHELP_DIR
);
771 grub_ext2_iterate_dir (fdiro
, iterate
);
774 if (fdiro
!= &data
->diropen
)
779 grub_dl_unref (my_mod
);
786 grub_ext2_label (grub_device_t device
, char **label
)
788 struct grub_ext2_data
*data
;
789 grub_disk_t disk
= device
->disk
;
792 grub_dl_ref (my_mod
);
795 data
= grub_ext2_mount (disk
);
797 *label
= grub_strndup (data
->sblock
.volume_name
, 14);
802 grub_dl_unref (my_mod
);
811 grub_ext2_uuid (grub_device_t device
, char **uuid
)
813 struct grub_ext2_data
*data
;
814 grub_disk_t disk
= device
->disk
;
817 grub_dl_ref (my_mod
);
820 data
= grub_ext2_mount (disk
);
823 *uuid
= grub_malloc (40 + sizeof ('\0'));
824 grub_sprintf (*uuid
, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
825 grub_be_to_cpu16 (data
->sblock
.uuid
[0]), grub_be_to_cpu16 (data
->sblock
.uuid
[1]),
826 grub_be_to_cpu16 (data
->sblock
.uuid
[2]), grub_be_to_cpu16 (data
->sblock
.uuid
[3]),
827 grub_be_to_cpu16 (data
->sblock
.uuid
[4]), grub_be_to_cpu16 (data
->sblock
.uuid
[5]),
828 grub_be_to_cpu16 (data
->sblock
.uuid
[6]), grub_be_to_cpu16 (data
->sblock
.uuid
[7]));
834 grub_dl_unref (my_mod
);
843 static struct grub_fs grub_ext2_fs
=
846 .dir
= grub_ext2_dir
,
847 .open
= grub_ext2_open
,
848 .read
= grub_ext2_read
,
849 .close
= grub_ext2_close
,
850 .label
= grub_ext2_label
,
851 .uuid
= grub_ext2_uuid
,
857 grub_fs_register (&grub_ext2_fs
);
865 grub_fs_unregister (&grub_ext2_fs
);