3 * esd gmbh <www.esd-electronics.com>
4 * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
6 * based on code from grub2 fs/ext2.c and fs/fshelp.c by
8 * GRUB -- GRand Unified Bootloader
9 * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #if defined(CONFIG_CMD_EXT2)
31 #include <asm/byteorder.h>
33 extern int ext2fs_devread (int sector
, int byte_offset
, int byte_len
,
36 /* Magic value used to identify an ext2 filesystem. */
37 #define EXT2_MAGIC 0xEF53
38 /* Amount of indirect blocks in an inode. */
39 #define INDIRECT_BLOCKS 12
40 /* Maximum lenght of a pathname. */
41 #define EXT2_PATH_MAX 4096
42 /* Maximum nesting of symlinks, used to prevent a loop. */
43 #define EXT2_MAX_SYMLINKCNT 8
45 /* Filetype used in directory entry. */
46 #define FILETYPE_UNKNOWN 0
47 #define FILETYPE_REG 1
48 #define FILETYPE_DIRECTORY 2
49 #define FILETYPE_SYMLINK 7
51 /* Filetype information as used in inodes. */
52 #define FILETYPE_INO_MASK 0170000
53 #define FILETYPE_INO_REG 0100000
54 #define FILETYPE_INO_DIRECTORY 0040000
55 #define FILETYPE_INO_SYMLINK 0120000
57 /* Bits used as offset in sector */
58 #define DISK_SECTOR_BITS 9
60 /* Log2 size of ext2 block in 512 blocks. */
61 #define LOG2_EXT2_BLOCK_SIZE(data) (__le32_to_cpu (data->sblock.log2_block_size) + 1)
63 /* Log2 size of ext2 block in bytes. */
64 #define LOG2_BLOCK_SIZE(data) (__le32_to_cpu (data->sblock.log2_block_size) + 10)
66 /* The size of an ext2 block in bytes. */
67 #define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE(data))
69 /* The ext2 superblock. */
71 uint32_t total_inodes
;
72 uint32_t total_blocks
;
73 uint32_t reserved_blocks
;
76 uint32_t first_data_block
;
77 uint32_t log2_block_size
;
78 uint32_t log2_fragment_size
;
79 uint32_t blocks_per_group
;
80 uint32_t fragments_per_group
;
81 uint32_t inodes_per_group
;
85 uint16_t max_mnt_count
;
88 uint16_t error_handling
;
89 uint16_t minor_revision_level
;
91 uint32_t checkinterval
;
93 uint32_t revision_level
;
94 uint16_t uid_reserved
;
95 uint16_t gid_reserved
;
98 uint16_t block_group_number
;
99 uint32_t feature_compatibility
;
100 uint32_t feature_incompat
;
101 uint32_t feature_ro_compat
;
102 uint32_t unique_id
[4];
103 char volume_name
[16];
104 char last_mounted_on
[64];
105 uint32_t compression_info
;
108 /* The ext2 blockgroup. */
109 struct ext2_block_group
{
112 uint32_t inode_table_id
;
113 uint16_t free_blocks
;
114 uint16_t free_inodes
;
116 uint32_t reserved
[3];
119 /* The ext2 inode. */
130 uint32_t blockcnt
; /* Blocks of 512 bytes!! */
135 uint32_t dir_blocks
[INDIRECT_BLOCKS
];
136 uint32_t indir_block
;
137 uint32_t double_indir_block
;
138 uint32_t tripple_indir_block
;
145 uint32_t fragment_addr
;
149 /* The header of an ext2 directory entry. */
158 struct ext2_data
*data
;
159 struct ext2_inode inode
;
164 /* Information about a "mounted" ext2 filesystem. */
166 struct ext2_sblock sblock
;
167 struct ext2_inode
*inode
;
168 struct ext2fs_node diropen
;
172 typedef struct ext2fs_node
*ext2fs_node_t
;
174 struct ext2_data
*ext2fs_root
= NULL
;
175 ext2fs_node_t ext2fs_file
= NULL
;
177 uint32_t *indir1_block
= NULL
;
179 int indir1_blkno
= -1;
180 uint32_t *indir2_block
= NULL
;
182 int indir2_blkno
= -1;
185 static int ext2fs_blockgroup
186 (struct ext2_data
*data
, int group
, struct ext2_block_group
*blkgrp
) {
188 printf ("ext2fs read blockgroup\n");
190 return (ext2fs_devread
191 (((__le32_to_cpu (data
->sblock
.first_data_block
) +
192 1) << LOG2_EXT2_BLOCK_SIZE (data
)),
193 group
* sizeof (struct ext2_block_group
),
194 sizeof (struct ext2_block_group
), (char *) blkgrp
));
198 static int ext2fs_read_inode
199 (struct ext2_data
*data
, int ino
, struct ext2_inode
*inode
) {
200 struct ext2_block_group blkgrp
;
201 struct ext2_sblock
*sblock
= &data
->sblock
;
202 int inodes_per_block
;
208 /* It is easier to calculate if the first inode is 0. */
211 printf ("ext2fs read inode %d\n", ino
);
213 status
= ext2fs_blockgroup (data
,
215 __le32_to_cpu (sblock
->inodes_per_group
),
220 inodes_per_block
= EXT2_BLOCK_SIZE (data
) / 128;
221 blkno
= (ino
% __le32_to_cpu (sblock
->inodes_per_group
)) /
223 blkoff
= (ino
% __le32_to_cpu (sblock
->inodes_per_group
)) %
226 printf ("ext2fs read inode blkno %d blkoff %d\n", blkno
, blkoff
);
228 /* Read the inode. */
229 status
= ext2fs_devread (((__le32_to_cpu (blkgrp
.inode_table_id
) +
230 blkno
) << LOG2_EXT2_BLOCK_SIZE (data
)),
231 sizeof (struct ext2_inode
) * blkoff
,
232 sizeof (struct ext2_inode
), (char *) inode
);
240 void ext2fs_free_node (ext2fs_node_t node
, ext2fs_node_t currroot
) {
241 if ((node
!= &ext2fs_root
->diropen
) && (node
!= currroot
)) {
247 static int ext2fs_read_block (ext2fs_node_t node
, int fileblock
) {
248 struct ext2_data
*data
= node
->data
;
249 struct ext2_inode
*inode
= &node
->inode
;
251 int blksz
= EXT2_BLOCK_SIZE (data
);
252 int log2_blksz
= LOG2_EXT2_BLOCK_SIZE (data
);
256 if (fileblock
< INDIRECT_BLOCKS
) {
257 blknr
= __le32_to_cpu (inode
->b
.blocks
.dir_blocks
[fileblock
]);
260 else if (fileblock
< (INDIRECT_BLOCKS
+ (blksz
/ 4))) {
261 if (indir1_block
== NULL
) {
262 indir1_block
= (uint32_t *) malloc (blksz
);
263 if (indir1_block
== NULL
) {
264 printf ("** ext2fs read block (indir 1) malloc failed. **\n");
270 if (blksz
!= indir1_size
) {
275 indir1_block
= (uint32_t *) malloc (blksz
);
276 if (indir1_block
== NULL
) {
277 printf ("** ext2fs read block (indir 1) malloc failed. **\n");
282 if ((__le32_to_cpu (inode
->b
.blocks
.indir_block
) <<
283 log2_blksz
) != indir1_blkno
) {
284 status
= ext2fs_devread (__le32_to_cpu(inode
->b
.blocks
.indir_block
) << log2_blksz
,
286 (char *) indir1_block
);
288 printf ("** ext2fs read block (indir 1) failed. **\n");
292 __le32_to_cpu (inode
->b
.blocks
.
293 indir_block
) << log2_blksz
;
295 blknr
= __le32_to_cpu (indir1_block
296 [fileblock
- INDIRECT_BLOCKS
]);
298 /* Double indirect. */
300 (INDIRECT_BLOCKS
+ (blksz
/ 4 * (blksz
/ 4 + 1)))) {
301 unsigned int perblock
= blksz
/ 4;
302 unsigned int rblock
= fileblock
- (INDIRECT_BLOCKS
305 if (indir1_block
== NULL
) {
306 indir1_block
= (uint32_t *) malloc (blksz
);
307 if (indir1_block
== NULL
) {
308 printf ("** ext2fs read block (indir 2 1) malloc failed. **\n");
314 if (blksz
!= indir1_size
) {
319 indir1_block
= (uint32_t *) malloc (blksz
);
320 if (indir1_block
== NULL
) {
321 printf ("** ext2fs read block (indir 2 1) malloc failed. **\n");
326 if ((__le32_to_cpu (inode
->b
.blocks
.double_indir_block
) <<
327 log2_blksz
) != indir1_blkno
) {
328 status
= ext2fs_devread (__le32_to_cpu(inode
->b
.blocks
.double_indir_block
) << log2_blksz
,
330 (char *) indir1_block
);
332 printf ("** ext2fs read block (indir 2 1) failed. **\n");
336 __le32_to_cpu (inode
->b
.blocks
.double_indir_block
) << log2_blksz
;
339 if (indir2_block
== NULL
) {
340 indir2_block
= (uint32_t *) malloc (blksz
);
341 if (indir2_block
== NULL
) {
342 printf ("** ext2fs read block (indir 2 2) malloc failed. **\n");
348 if (blksz
!= indir2_size
) {
353 indir2_block
= (uint32_t *) malloc (blksz
);
354 if (indir2_block
== NULL
) {
355 printf ("** ext2fs read block (indir 2 2) malloc failed. **\n");
360 if ((__le32_to_cpu (indir1_block
[rblock
/ perblock
]) <<
361 log2_blksz
) != indir1_blkno
) {
362 status
= ext2fs_devread (__le32_to_cpu(indir1_block
[rblock
/ perblock
]) << log2_blksz
,
364 (char *) indir2_block
);
366 printf ("** ext2fs read block (indir 2 2) failed. **\n");
370 __le32_to_cpu (indir1_block
[rblock
/ perblock
]) << log2_blksz
;
372 blknr
= __le32_to_cpu (indir2_block
[rblock
% perblock
]);
374 /* Tripple indirect. */
376 printf ("** ext2fs doesn't support tripple indirect blocks. **\n");
380 printf ("ext2fs_read_block %08x\n", blknr
);
387 (ext2fs_node_t node
, int pos
, unsigned int len
, char *buf
) {
390 int log2blocksize
= LOG2_EXT2_BLOCK_SIZE (node
->data
);
391 int blocksize
= 1 << (log2blocksize
+ DISK_SECTOR_BITS
);
392 unsigned int filesize
= __le32_to_cpu(node
->inode
.size
);
394 /* Adjust len so it we can't read past the end of the file. */
395 if (len
> filesize
) {
398 blockcnt
= ((len
+ pos
) + blocksize
- 1) / blocksize
;
400 for (i
= pos
/ blocksize
; i
< blockcnt
; i
++) {
402 int blockoff
= pos
% blocksize
;
403 int blockend
= blocksize
;
407 blknr
= ext2fs_read_block (node
, i
);
411 blknr
= blknr
<< log2blocksize
;
414 if (i
== blockcnt
- 1) {
415 blockend
= (len
+ pos
) % blocksize
;
417 /* The last portion is exactly blocksize. */
419 blockend
= blocksize
;
424 if (i
== pos
/ blocksize
) {
425 skipfirst
= blockoff
;
426 blockend
-= skipfirst
;
429 /* If the block number is 0 this block is not stored on disk but
430 is zero filled instead. */
434 status
= ext2fs_devread (blknr
, skipfirst
, blockend
, buf
);
439 memset (buf
, 0, blocksize
- skipfirst
);
441 buf
+= blocksize
- skipfirst
;
447 static int ext2fs_iterate_dir (ext2fs_node_t dir
, char *name
, ext2fs_node_t
* fnode
, int *ftype
)
449 unsigned int fpos
= 0;
451 struct ext2fs_node
*diro
= (struct ext2fs_node
*) dir
;
455 printf ("Iterate dir %s\n", name
);
456 #endif /* of DEBUG */
457 if (!diro
->inode_read
) {
458 status
= ext2fs_read_inode (diro
->data
, diro
->ino
,
464 /* Search the file. */
465 while (fpos
< __le32_to_cpu (diro
->inode
.size
)) {
466 struct ext2_dirent dirent
;
468 status
= ext2fs_read_file (diro
, fpos
,
469 sizeof (struct ext2_dirent
),
474 if (dirent
.namelen
!= 0) {
475 char filename
[dirent
.namelen
+ 1];
477 int type
= FILETYPE_UNKNOWN
;
479 status
= ext2fs_read_file (diro
,
480 fpos
+ sizeof (struct ext2_dirent
),
481 dirent
.namelen
, filename
);
485 fdiro
= malloc (sizeof (struct ext2fs_node
));
490 fdiro
->data
= diro
->data
;
491 fdiro
->ino
= __le32_to_cpu (dirent
.inode
);
493 filename
[dirent
.namelen
] = '\0';
495 if (dirent
.filetype
!= FILETYPE_UNKNOWN
) {
496 fdiro
->inode_read
= 0;
498 if (dirent
.filetype
== FILETYPE_DIRECTORY
) {
499 type
= FILETYPE_DIRECTORY
;
500 } else if (dirent
.filetype
==
502 type
= FILETYPE_SYMLINK
;
503 } else if (dirent
.filetype
== FILETYPE_REG
) {
507 /* The filetype can not be read from the dirent, get it from inode */
509 status
= ext2fs_read_inode (diro
->data
,
510 __le32_to_cpu(dirent
.inode
),
516 fdiro
->inode_read
= 1;
518 if ((__le16_to_cpu (fdiro
->inode
.mode
) &
519 FILETYPE_INO_MASK
) ==
520 FILETYPE_INO_DIRECTORY
) {
521 type
= FILETYPE_DIRECTORY
;
522 } else if ((__le16_to_cpu (fdiro
->inode
.mode
)
523 & FILETYPE_INO_MASK
) ==
524 FILETYPE_INO_SYMLINK
) {
525 type
= FILETYPE_SYMLINK
;
526 } else if ((__le16_to_cpu (fdiro
->inode
.mode
)
527 & FILETYPE_INO_MASK
) ==
533 printf ("iterate >%s<\n", filename
);
534 #endif /* of DEBUG */
535 if ((name
!= NULL
) && (fnode
!= NULL
)
536 && (ftype
!= NULL
)) {
537 if (strcmp (filename
, name
) == 0) {
543 if (fdiro
->inode_read
== 0) {
544 status
= ext2fs_read_inode (diro
->data
,
545 __le32_to_cpu (dirent
.inode
),
551 fdiro
->inode_read
= 1;
554 case FILETYPE_DIRECTORY
:
557 case FILETYPE_SYMLINK
:
568 __le32_to_cpu (fdiro
->inode
.size
),
573 fpos
+= __le16_to_cpu (dirent
.direntlen
);
579 static char *ext2fs_read_symlink (ext2fs_node_t node
) {
581 struct ext2fs_node
*diro
= node
;
584 if (!diro
->inode_read
) {
585 status
= ext2fs_read_inode (diro
->data
, diro
->ino
,
591 symlink
= malloc (__le32_to_cpu (diro
->inode
.size
) + 1);
595 /* If the filesize of the symlink is bigger than
596 60 the symlink is stored in a separate block,
597 otherwise it is stored in the inode. */
598 if (__le32_to_cpu (diro
->inode
.size
) <= 60) {
599 strncpy (symlink
, diro
->inode
.b
.symlink
,
600 __le32_to_cpu (diro
->inode
.size
));
602 status
= ext2fs_read_file (diro
, 0,
603 __le32_to_cpu (diro
->inode
.size
),
610 symlink
[__le32_to_cpu (diro
->inode
.size
)] = '\0';
615 int ext2fs_find_file1
616 (const char *currpath
,
617 ext2fs_node_t currroot
, ext2fs_node_t
* currfound
, int *foundtype
) {
618 char fpath
[strlen (currpath
) + 1];
622 int type
= FILETYPE_DIRECTORY
;
623 ext2fs_node_t currnode
= currroot
;
624 ext2fs_node_t oldnode
= currroot
;
626 strncpy (fpath
, currpath
, strlen (currpath
) + 1);
628 /* Remove all leading slashes. */
629 while (*name
== '/') {
633 *currfound
= currnode
;
640 /* Extract the actual part from the pathname. */
641 next
= strchr (name
, '/');
643 /* Remove all leading slashes. */
644 while (*next
== '/') {
649 /* At this point it is expected that the current node is a directory, check if this is true. */
650 if (type
!= FILETYPE_DIRECTORY
) {
651 ext2fs_free_node (currnode
, currroot
);
657 /* Iterate over the directory. */
658 found
= ext2fs_iterate_dir (currnode
, name
, &currnode
, &type
);
666 /* Read in the symlink and follow it. */
667 if (type
== FILETYPE_SYMLINK
) {
670 /* Test if the symlink does not loop. */
671 if (++symlinknest
== 8) {
672 ext2fs_free_node (currnode
, currroot
);
673 ext2fs_free_node (oldnode
, currroot
);
677 symlink
= ext2fs_read_symlink (currnode
);
678 ext2fs_free_node (currnode
, currroot
);
681 ext2fs_free_node (oldnode
, currroot
);
685 printf ("Got symlink >%s<\n", symlink
);
686 #endif /* of DEBUG */
687 /* The symlink is an absolute path, go back to the root inode. */
688 if (symlink
[0] == '/') {
689 ext2fs_free_node (oldnode
, currroot
);
690 oldnode
= &ext2fs_root
->diropen
;
693 /* Lookup the node the symlink points to. */
694 status
= ext2fs_find_file1 (symlink
, oldnode
,
700 ext2fs_free_node (oldnode
, currroot
);
705 ext2fs_free_node (oldnode
, currroot
);
707 /* Found the node! */
708 if (!next
|| *next
== '\0') {
709 *currfound
= currnode
;
721 ext2fs_node_t rootnode
, ext2fs_node_t
* foundnode
, int expecttype
) {
723 int foundtype
= FILETYPE_DIRECTORY
;
731 status
= ext2fs_find_file1 (path
, rootnode
, foundnode
, &foundtype
);
735 /* Check if the node that was found was of the expected type. */
736 if ((expecttype
== FILETYPE_REG
) && (foundtype
!= expecttype
)) {
738 } else if ((expecttype
== FILETYPE_DIRECTORY
)
739 && (foundtype
!= expecttype
)) {
746 int ext2fs_ls (char *dirname
) {
747 ext2fs_node_t dirnode
;
750 if (ext2fs_root
== NULL
) {
754 status
= ext2fs_find_file (dirname
, &ext2fs_root
->diropen
, &dirnode
,
757 printf ("** Can not find directory. **\n");
760 ext2fs_iterate_dir (dirnode
, NULL
, NULL
, NULL
);
761 ext2fs_free_node (dirnode
, &ext2fs_root
->diropen
);
766 int ext2fs_open (char *filename
) {
767 ext2fs_node_t fdiro
= NULL
;
771 if (ext2fs_root
== NULL
) {
775 status
= ext2fs_find_file (filename
, &ext2fs_root
->diropen
, &fdiro
,
780 if (!fdiro
->inode_read
) {
781 status
= ext2fs_read_inode (fdiro
->data
, fdiro
->ino
,
787 len
= __le32_to_cpu (fdiro
->inode
.size
);
792 ext2fs_free_node (fdiro
, &ext2fs_root
->diropen
);
797 int ext2fs_close (void
799 if ((ext2fs_file
!= NULL
) && (ext2fs_root
!= NULL
)) {
800 ext2fs_free_node (ext2fs_file
, &ext2fs_root
->diropen
);
803 if (ext2fs_root
!= NULL
) {
807 if (indir1_block
!= NULL
) {
813 if (indir2_block
!= NULL
) {
823 int ext2fs_read (char *buf
, unsigned len
) {
826 if (ext2fs_root
== NULL
) {
830 if (ext2fs_file
== NULL
) {
834 status
= ext2fs_read_file (ext2fs_file
, 0, len
, buf
);
839 int ext2fs_mount (unsigned part_length
) {
840 struct ext2_data
*data
;
843 data
= malloc (sizeof (struct ext2_data
));
847 /* Read the superblock. */
848 status
= ext2fs_devread (1 * 2, 0, sizeof (struct ext2_sblock
),
849 (char *) &data
->sblock
);
853 /* Make sure this is an ext2 filesystem. */
854 if (__le16_to_cpu (data
->sblock
.magic
) != EXT2_MAGIC
) {
857 data
->diropen
.data
= data
;
858 data
->diropen
.ino
= 2;
859 data
->diropen
.inode_read
= 1;
860 data
->inode
= &data
->diropen
.inode
;
862 status
= ext2fs_read_inode (data
, 2, data
->inode
);
872 printf ("Failed to mount ext2 filesystem...\n");