1 /* ext2.c - Second Extended filesystem */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2007 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 lenght 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 /* The ext2 superblock. */
75 struct grub_ext2_sblock
77 grub_uint32_t total_inodes
;
78 grub_uint32_t total_blocks
;
79 grub_uint32_t reserved_blocks
;
80 grub_uint32_t free_blocks
;
81 grub_uint32_t free_inodes
;
82 grub_uint32_t first_data_block
;
83 grub_uint32_t log2_block_size
;
84 grub_uint32_t log2_fragment_size
;
85 grub_uint32_t blocks_per_group
;
86 grub_uint32_t fragments_per_group
;
87 grub_uint32_t inodes_per_group
;
90 grub_uint16_t mnt_count
;
91 grub_uint16_t max_mnt_count
;
93 grub_uint16_t fs_state
;
94 grub_uint16_t error_handling
;
95 grub_uint16_t minor_revision_level
;
96 grub_uint32_t lastcheck
;
97 grub_uint32_t checkinterval
;
98 grub_uint32_t creator_os
;
99 grub_uint32_t revision_level
;
100 grub_uint16_t uid_reserved
;
101 grub_uint16_t gid_reserved
;
102 grub_uint32_t first_inode
;
103 grub_uint16_t inode_size
;
104 grub_uint16_t block_group_number
;
105 grub_uint32_t feature_compatibility
;
106 grub_uint32_t feature_incompat
;
107 grub_uint32_t feature_ro_compat
;
108 grub_uint32_t unique_id
[4];
109 char volume_name
[16];
110 char last_mounted_on
[64];
111 grub_uint32_t compression_info
;
114 /* The ext2 blockgroup. */
115 struct grub_ext2_block_group
117 grub_uint32_t block_id
;
118 grub_uint32_t inode_id
;
119 grub_uint32_t inode_table_id
;
120 grub_uint16_t free_blocks
;
121 grub_uint16_t free_inodes
;
122 grub_uint16_t used_dirs
;
124 grub_uint32_t reserved
[3];
127 /* The ext2 inode. */
128 struct grub_ext2_inode
138 grub_uint16_t nlinks
;
139 grub_uint32_t blockcnt
; /* Blocks of 512 bytes!! */
146 grub_uint32_t dir_blocks
[INDIRECT_BLOCKS
];
147 grub_uint32_t indir_block
;
148 grub_uint32_t double_indir_block
;
149 grub_uint32_t tripple_indir_block
;
153 grub_uint32_t version
;
155 grub_uint32_t dir_acl
;
156 grub_uint32_t fragment_addr
;
157 grub_uint32_t osd2
[3];
160 /* The header of an ext2 directory entry. */
164 grub_uint16_t direntlen
;
165 grub_uint8_t namelen
;
166 grub_uint8_t filetype
;
169 struct grub_fshelp_node
171 struct grub_ext2_data
*data
;
172 struct grub_ext2_inode inode
;
177 /* Information about a "mounted" ext2 filesystem. */
178 struct grub_ext2_data
180 struct grub_ext2_sblock sblock
;
182 struct grub_ext2_inode
*inode
;
183 struct grub_fshelp_node diropen
;
187 static grub_dl_t my_mod
;
192 /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
193 the mounted filesystem DATA. */
194 inline static grub_err_t
195 grub_ext2_blockgroup (struct grub_ext2_data
*data
, int group
,
196 struct grub_ext2_block_group
*blkgrp
)
198 return grub_disk_read (data
->disk
,
199 ((grub_le_to_cpu32 (data
->sblock
.first_data_block
) + 1)
200 << LOG2_EXT2_BLOCK_SIZE (data
)),
201 group
* sizeof (struct grub_ext2_block_group
),
202 sizeof (struct grub_ext2_block_group
), (char *) blkgrp
);
207 grub_ext2_read_block (grub_fshelp_node_t node
, int fileblock
)
209 struct grub_ext2_data
*data
= node
->data
;
210 struct grub_ext2_inode
*inode
= &node
->inode
;
212 int blksz
= EXT2_BLOCK_SIZE (data
);
213 int log2_blksz
= LOG2_EXT2_BLOCK_SIZE (data
);
216 if (fileblock
< INDIRECT_BLOCKS
)
217 blknr
= grub_le_to_cpu32 (inode
->blocks
.dir_blocks
[fileblock
]);
219 else if (fileblock
< INDIRECT_BLOCKS
+ blksz
/ 4)
221 grub_uint32_t indir
[blksz
/ 4];
223 if (grub_disk_read (data
->disk
,
224 grub_le_to_cpu32 (inode
->blocks
.indir_block
)
226 0, blksz
, (char *) indir
))
229 blknr
= grub_le_to_cpu32 (indir
[fileblock
- INDIRECT_BLOCKS
]);
231 /* Double indirect. */
232 else if (fileblock
< INDIRECT_BLOCKS
+ blksz
/ 4 * (blksz
/ 4 + 1))
234 unsigned int perblock
= blksz
/ 4;
235 unsigned int rblock
= fileblock
- (INDIRECT_BLOCKS
237 grub_uint32_t indir
[blksz
/ 4];
239 if (grub_disk_read (data
->disk
,
240 grub_le_to_cpu32 (inode
->blocks
.double_indir_block
)
242 0, blksz
, (char *) indir
))
245 if (grub_disk_read (data
->disk
,
246 grub_le_to_cpu32 (indir
[rblock
/ perblock
])
248 0, blksz
, (char *) indir
))
252 blknr
= grub_le_to_cpu32 (indir
[rblock
% perblock
]);
254 /* Tripple indirect. */
257 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
258 "ext2fs doesn't support tripple indirect blocks");
266 /* Read LEN bytes from the file described by DATA starting with byte
267 POS. Return the amount of read bytes in READ. */
269 grub_ext2_read_file (grub_fshelp_node_t node
,
270 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
271 unsigned offset
, unsigned length
),
272 int pos
, grub_size_t len
, char *buf
)
274 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
275 pos
, len
, buf
, grub_ext2_read_block
,
277 LOG2_EXT2_BLOCK_SIZE (node
->data
));
282 /* Read the inode INO for the file described by DATA into INODE. */
284 grub_ext2_read_inode (struct grub_ext2_data
*data
,
285 int ino
, struct grub_ext2_inode
*inode
)
287 struct grub_ext2_block_group blkgrp
;
288 struct grub_ext2_sblock
*sblock
= &data
->sblock
;
289 int inodes_per_block
;
294 /* It is easier to calculate if the first inode is 0. */
297 grub_ext2_blockgroup (data
,
298 ino
/ grub_le_to_cpu32 (sblock
->inodes_per_group
),
303 inodes_per_block
= EXT2_BLOCK_SIZE (data
) / EXT2_INODE_SIZE (data
);
304 blkno
= (ino
% grub_le_to_cpu32 (sblock
->inodes_per_group
))
306 blkoff
= (ino
% grub_le_to_cpu32 (sblock
->inodes_per_group
))
309 /* Read the inode. */
310 if (grub_disk_read (data
->disk
,
311 ((grub_le_to_cpu32 (blkgrp
.inode_table_id
) + blkno
)
312 << LOG2_EXT2_BLOCK_SIZE (data
)),
313 EXT2_INODE_SIZE (data
) * blkoff
,
314 sizeof (struct grub_ext2_inode
), (char *) inode
))
320 static struct grub_ext2_data
*
321 grub_ext2_mount (grub_disk_t disk
)
323 struct grub_ext2_data
*data
;
325 data
= grub_malloc (sizeof (struct grub_ext2_data
));
329 /* Read the superblock. */
330 grub_disk_read (disk
, 1 * 2, 0, sizeof (struct grub_ext2_sblock
),
331 (char *) &data
->sblock
);
335 /* Make sure this is an ext2 filesystem. */
336 if (grub_le_to_cpu16 (data
->sblock
.magic
) != EXT2_MAGIC
)
339 data
->diropen
.data
= data
;
340 data
->diropen
.ino
= 2;
341 data
->diropen
.inode_read
= 1;
343 data
->inode
= &data
->diropen
.inode
;
346 grub_ext2_read_inode (data
, 2, data
->inode
);
353 grub_error (GRUB_ERR_BAD_FS
, "not an ext2 filesystem");
359 grub_ext2_read_symlink (grub_fshelp_node_t node
)
362 struct grub_fshelp_node
*diro
= node
;
364 if (! diro
->inode_read
)
366 grub_ext2_read_inode (diro
->data
, diro
->ino
, &diro
->inode
);
371 symlink
= grub_malloc (grub_le_to_cpu32 (diro
->inode
.size
) + 1);
375 /* If the filesize of the symlink is bigger than
376 60 the symlink is stored in a separate block,
377 otherwise it is stored in the inode. */
378 if (grub_le_to_cpu32 (diro
->inode
.size
) <= 60)
379 grub_strncpy (symlink
,
381 grub_le_to_cpu32 (diro
->inode
.size
));
384 grub_ext2_read_file (diro
, 0, 0,
385 grub_le_to_cpu32 (diro
->inode
.size
),
394 symlink
[grub_le_to_cpu32 (diro
->inode
.size
)] = '\0';
399 grub_ext2_iterate_dir (grub_fshelp_node_t dir
,
401 (*hook
) (const char *filename
,
402 enum grub_fshelp_filetype filetype
,
403 grub_fshelp_node_t node
))
405 unsigned int fpos
= 0;
406 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
408 if (! diro
->inode_read
)
410 grub_ext2_read_inode (diro
->data
, diro
->ino
, &diro
->inode
);
415 /* Search the file. */
416 while (fpos
< grub_le_to_cpu32 (diro
->inode
.size
))
418 struct ext2_dirent dirent
;
420 grub_ext2_read_file (diro
, 0, fpos
, sizeof (struct ext2_dirent
),
425 if (dirent
.namelen
!= 0)
427 char filename
[dirent
.namelen
+ 1];
428 struct grub_fshelp_node
*fdiro
;
429 enum grub_fshelp_filetype type
= GRUB_FSHELP_UNKNOWN
;
431 grub_ext2_read_file (diro
, 0, fpos
+ sizeof (struct ext2_dirent
),
432 dirent
.namelen
, filename
);
436 fdiro
= grub_malloc (sizeof (struct grub_fshelp_node
));
440 fdiro
->data
= diro
->data
;
441 fdiro
->ino
= grub_le_to_cpu32 (dirent
.inode
);
443 filename
[dirent
.namelen
] = '\0';
445 if (dirent
.filetype
!= FILETYPE_UNKNOWN
)
447 fdiro
->inode_read
= 0;
449 if (dirent
.filetype
== FILETYPE_DIRECTORY
)
450 type
= GRUB_FSHELP_DIR
;
451 else if (dirent
.filetype
== FILETYPE_SYMLINK
)
452 type
= GRUB_FSHELP_SYMLINK
;
453 else if (dirent
.filetype
== FILETYPE_REG
)
454 type
= GRUB_FSHELP_REG
;
458 /* The filetype can not be read from the dirent, read
459 the inode to get more information. */
460 grub_ext2_read_inode (diro
->data
,
461 grub_le_to_cpu32 (dirent
.inode
),
469 fdiro
->inode_read
= 1;
471 if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
472 & FILETYPE_INO_MASK
) == FILETYPE_INO_DIRECTORY
)
473 type
= GRUB_FSHELP_DIR
;
474 else if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
475 & FILETYPE_INO_MASK
) == FILETYPE_INO_SYMLINK
)
476 type
= GRUB_FSHELP_SYMLINK
;
477 else if ((grub_le_to_cpu16 (fdiro
->inode
.mode
)
478 & FILETYPE_INO_MASK
) == FILETYPE_INO_REG
)
479 type
= GRUB_FSHELP_REG
;
482 if (hook (filename
, type
, fdiro
))
486 fpos
+= grub_le_to_cpu16 (dirent
.direntlen
);
492 /* Open a file named NAME and initialize FILE. */
494 grub_ext2_open (struct grub_file
*file
, const char *name
)
496 struct grub_ext2_data
*data
;
497 struct grub_fshelp_node
*fdiro
= 0;
500 grub_dl_ref (my_mod
);
503 data
= grub_ext2_mount (file
->device
->disk
);
507 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_ext2_iterate_dir
,
508 grub_ext2_read_symlink
, GRUB_FSHELP_REG
);
512 if (! fdiro
->inode_read
)
514 grub_ext2_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
519 grub_memcpy (data
->inode
, &fdiro
->inode
, sizeof (struct grub_ext2_inode
));
522 file
->size
= grub_le_to_cpu32 (data
->inode
->size
);
529 if (fdiro
!= &data
->diropen
)
534 grub_dl_unref (my_mod
);
541 grub_ext2_close (grub_file_t file
)
543 grub_free (file
->data
);
546 grub_dl_unref (my_mod
);
549 return GRUB_ERR_NONE
;
552 /* Read LEN bytes data from FILE into BUF. */
554 grub_ext2_read (grub_file_t file
, char *buf
, grub_size_t len
)
556 struct grub_ext2_data
*data
= (struct grub_ext2_data
*) file
->data
;
558 return grub_ext2_read_file (&data
->diropen
, file
->read_hook
,
559 file
->offset
, len
, buf
);
564 grub_ext2_dir (grub_device_t device
, const char *path
,
565 int (*hook
) (const char *filename
, int dir
))
567 struct grub_ext2_data
*data
= 0;;
568 struct grub_fshelp_node
*fdiro
= 0;
570 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
571 enum grub_fshelp_filetype filetype
,
572 grub_fshelp_node_t node
);
574 int NESTED_FUNC_ATTR
iterate (const char *filename
,
575 enum grub_fshelp_filetype filetype
,
576 grub_fshelp_node_t node
)
580 if (filetype
== GRUB_FSHELP_DIR
)
581 return hook (filename
, 1);
583 return hook (filename
, 0);
589 grub_dl_ref (my_mod
);
592 data
= grub_ext2_mount (device
->disk
);
596 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_ext2_iterate_dir
,
597 grub_ext2_read_symlink
, GRUB_FSHELP_DIR
);
601 grub_ext2_iterate_dir (fdiro
, iterate
);
604 if (fdiro
!= &data
->diropen
)
609 grub_dl_unref (my_mod
);
616 grub_ext2_label (grub_device_t device
, char **label
)
618 struct grub_ext2_data
*data
;
619 grub_disk_t disk
= device
->disk
;
622 grub_dl_ref (my_mod
);
625 data
= grub_ext2_mount (disk
);
627 *label
= grub_strndup (data
->sblock
.volume_name
, 14);
632 grub_dl_unref (my_mod
);
641 static struct grub_fs grub_ext2_fs
=
644 .dir
= grub_ext2_dir
,
645 .open
= grub_ext2_open
,
646 .read
= grub_ext2_read
,
647 .close
= grub_ext2_close
,
648 .label
= grub_ext2_label
,
654 grub_fs_register (&grub_ext2_fs
);
662 grub_fs_unregister (&grub_ext2_fs
);