0% found this document useful (0 votes)
11 views

ext2-walkthrough

Uploaded by

Irfan Memon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

ext2-walkthrough

Uploaded by

Irfan Memon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

ext2 File System Walkthrough

• The main thing to remember about file systems is that


they are ultimately data structures — knowing a file
system is a matter of knowing how to interpret the
sequence of bytes/blocks that it writes onto a disk

• Implementing a file system is a matter of designing a


scheme for how the bytes/blocks in a volume can be
organized into files, directories, and other constructs,
then implementing this scheme in code

• To drive these points home, we’ll walk through a raw


image of a particular file system: Linux’s ext2

Preliminaries and Tools


• The information in this walkthrough was produced
through a 2-megabyte disk image, on which an “empty”
ext2 file system was installed
• The disk image was then mounted and modified:
Two files, hello.txt and goodbye.txt were placed in the root directory
A subdirectory, mydir, was also placed at the root

Two links were placed in mydir: a symbolic link to hello.txt, and a hard link to goodbye.txt

• Two utilities help in the walkthrough: dumpe2fs displays


the superblock in a more readable form, and hexdump
displays the raw bytes on the disk image
Filesystem volume name:
Last mounted on:
Filesystem UUID:
<none>
<not available>
ab1e9464-f4a2-480d-81c0-e7f05df99988
Superblock: dumpe2fs helps
us get our bearings, but in the
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal filetype sparse_super
Default mount options: (none)
Filesystem state:
Errors behavior:
Filesystem OS type:
clean
Continue
Linux
end it’s just a helper — in
Inode count:
Block count:
Reserved block count:
256
2048
102
ext2, the superblock C
structure maps directly onto
Free blocks: 965
Free inodes: 241
First block: 1

what’s written to the disk


Block size: 1024
Fragment size: 1024
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 256

• 2048 blocks * 1024 bytes per block = 2


Inode blocks per group: 32
Filesystem created: Tue Apr 4 17:28:08 2006
Last mount time: Tue Apr 11 14:38:24 2006
Last write time: Tue Apr 11 14:39:05 2006 megabytes (“mebibytes” by today’s latest
Mount count: 7
Maximum mount count: 23 terminology) — take note, 1024 is 400 hex
Last checked: Tue Apr 4 17:28:08 2006
Check interval: 15552000 (6 months)
Next check after:
Reserved blocks uid:
Sun Oct 1 17:28:08 2006
0 (user root) • 128 bytes per inode: 80 hex
Reserved blocks gid: 0 (group root)

• Groups form an intermediate structure


First inode: 11
Inode size: 128
Journal inode: 8
Default directory hash: tea within an ext2 volume; the superblock and
Directory Hash Seed: 8d91c7c3-5116-4b71-b7ac-255d6907d8a1
Journal backup: inode blocks group descriptors are copied within each
Group 0: (Blocks 1-2047) group in case of corruption
Primary superblock at 1, Group descriptors at 2-2
Block bitmap at 3 (+2), Inode bitmap at 4 (+3)
Inode table at 5-36 (+4)
965 free blocks, 241 free inodes, 3 directories
Free blocks: 1082-1088, 1090-2047
Free inodes: 16-256

00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| struct ext2_super_block {


* __le32 s_inodes_count; /* Inodes count */
00000400 00 01 00 00 00 08 00 00 66 00 00 00 c5 03 00 00 |........f.......| __le32 s_blocks_count; /* Blocks count */
00000410 f1 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| __le32 s_r_blocks_count; /* Reserved blocks count */
00000420 00 20 00 00 00 20 00 00 00 01 00 00 d0 21 3c 44 |. ... .......!<D| __le32 s_free_blocks_count; /* Free blocks count */
00000430 f9 21 3c 44 07 00 17 00 53 ef 01 00 01 00 00 00 |.!<D....S.......| __le32 s_free_inodes_count; /* Free inodes count */
00000440 18 0f 33 44 00 4e ed 00 00 00 00 00 01 00 00 00 |..3D.N..........| __le32 s_first_data_block; /* First Data Block */
00000450 00 00 00 00 0b 00 00 00 80 00 00 00 04 00 00 00 |................| __le32 s_log_block_size; /* Block size */
00000460 02 00 00 00 01 00 00 00 ab 1e 94 64 f4 a2 48 0d |...........d..H.| __le32 s_log_frag_size; /* Fragment size */
00000470 81 c0 e7 f0 5d f9 99 88 00 00 00 00 00 00 00 00 |....]...........| __le32 s_blocks_per_group; /* # Blocks per group */
00000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| __le32 s_frags_per_group; /* # Fragments per group */
* __le32 s_inodes_per_group; /* # Inodes per group */
000004e0 08 00 00 00 00 00 00 00 00 00 00 00 8d 91 c7 c3 |................| __le32 s_mtime; /* Mount time */
000004f0 51 16 4b 71 b7 ac 25 5d 69 07 d8 a1 02 01 00 00 |Q.Kq..%]i.......| __le32 s_wtime; /* Write time */
00000500 00 00 00 00 00 00 00 00 18 0f 33 44 32 00 00 00 |..........3D2...| __le16 s_mnt_count; /* Mount count */
00000510 33 00 00 00 34 00 00 00 35 00 00 00 36 00 00 00 |3...4...5...6...| __le16 s_max_mnt_count; /* Maximal mount count */
00000520 37 00 00 00 38 00 00 00 39 00 00 00 3a 00 00 00 |7...8...9...:...| __le16 s_magic; /* Magic signature */
00000530 3b 00 00 00 3c 00 00 00 3d 00 00 00 3e 00 00 00 |;...<...=...>...| __le16 s_state; /* File system state */
00000540 3f 01 00 00 00 00 00 00 00 00 00 00 00 00 10 00 |?...............| __le16 s_errors; /* Behaviour when detecting errors */
00000550 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| __le16 s_minor_rev_level; /* minor revision level */
* __le32 s_lastcheck; /* time of last check */
__le32 s_checkinterval; /* max. time between checks */
__le32 s_creator_os; /* OS */

• Note how “reading” the superblock is a __le32 s_rev_level;


__le16 s_def_resuid;
/* Revision level */
/* Default uid for reserved blocks */

matter of following its corresponding C __le16 s_def_resgid;


__le32 s_first_ino;
/* Default gid for reserved blocks */
/* First non-reserved inode */

structure from the ext2 source code __le16 s_inode_size;


__le16 s_block_group_nr;
/* size of inode structure */
/* block group # of this superblock */
__le32 s_feature_compat; /* compatible feature set */

• Other file systems might not be quite so


__le32 s_feature_incompat; /* incompatible feature set */
__le32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
direct, requiring additional decoding char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__le32 s_algorithm_usage_bitmap; /* For compression */

• For conciseness, hexdump skips sequences of __u8


__u8
s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
s_prealloc_dir_blocks; /* Nr to preallocate for dirs */

00 bytes, and marks them with a “*” __u16


__u8
s_padding1;
s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */

• hexdump can also place ASCII on the right;


__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
__u32 s_hash_seed[4]; /* HTREE hash seed */
these settings are activated with the -C __u8 s_def_hash_version; /* Default hash version to use */
__u8 s_reserved_char_pad;
(“canonical”) switch __u16 s_reserved_word_pad;
__le32 s_default_mount_opts;
__le32 s_first_meta_bg; /* First metablock block group */
__u32 s_reserved[190]; /* Padding to the end of the block */
};
Group descriptor: Again,
as long as you have the C struct ext2_group_desc
{

structure, it’s fairly easy to


__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap block */
__le32 bg_inode_table; /* Inodes table block */

read in its raw form


__le16 bg_free_blocks_count; /* Free blocks count */
__le16 bg_free_inodes_count; /* Free inodes count */
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
00000800 03 00 00 00 04 00 00 00 05 00 00 00 c5 03 f1 00 |................| __le32 bg_reserved[3];
00000810 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| };
00000820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

• At this point, you can start “doing the math”


• Since the inode table starts at block 5, and
blocks are 1024 bytes long, then you can
expect to see the inodes at the linear hex
location 5 * 400 = 1400

• Data blocks 3 and 4 (locations c000 and 00000c00


*
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|

1000, respectively) are straightforward bit 00000c80


00000c90
ff ff ff ff ff ff ff 01
00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
|................|
|................|

fields indicating what’s available (if the bit is *


00000cf0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 |................|

set, then the corresponding entity has been 00000d00


*
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|

allocated for use)

00001000 ff 7f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001020 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*

Inodes: Based on the


00001400 00 00 00 00 00 00 00 00 18 0f 33 44 18 0f 33 44 |..........3D..3D|
00001410 18 0f 33 44 00 00 00 00 00 00 00 00 00 00 00 00 |..3D............|
00001420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001480
00001490
000014a0
ed
9d
00
41
21
00
f4
3c
00
03
44
00
00
00
00
04
00
00
00
00
00
00
00
00
ef
f4
25
21
03
00
3c
04
00
44
00
00
9d
02
00
21
00
00
3c
00
00
44
00
00
|.A.......!<D.!<D|
|.!<D............|
|........%.......|
information in the superblock
and group descriptor, we
000014b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001780 80 81 00 00 00 00 10 00 00 00 00 00 18 0f 33 44 |..............3D|

expect the inodes to show up


00001790 18 0f 33 44 00 00 00 00 00 00 01 00 0c 08 00 00 |..3D............|
000017a0 00 00 00 00 00 00 00 00 32 00 00 00 33 00 00 00 |........2...3...|
000017b0 34 00 00 00 35 00 00 00 36 00 00 00 37 00 00 00 |4...5...6...7...|
000017c0 38 00 00 00 39 00 00 00 3a 00 00 00 3b 00 00 00 |8...9...:...;...|
000017d0
000017e0
*
3c
00
00
00
00
00
00
00
3d
00
00
00
00
00
00
00
3e
00
00
00
00
00
00
00
3f
00
01
00
00
00
00
00
|<...=...>...?...|
|................| at hex location 1400
• And indeed, they’re there; at 128 bytes per
00001900 c0 41 00 00 00 30 00 00 ec 21 3c 44 18 0f 33 44 |.A...0...!<D..3D|
00001910 18 0f 33 44 00 00 00 00 00 00 02 00 18 00 00 00 |..3D............|
00001920 00 00 00 00 00 00 00 00 26 00 00 00 27 00 00 00 |........&...'...|
00001930 28 00 00 00 29 00 00 00 2a 00 00 00 2b 00 00 00 |(...)...*...+...| inode, it’s easy to jump from one inode to
00001940 2c 00 00 00 2d 00 00 00 2e 00 00 00 2f 00 00 00 |,...-......./...|
00001950 30 00 00 00 31 00 00 00 00 00 00 00 00 00 00 00 |0...1...........| another — 128 is 80 hex, so we’ll find inodes
00001960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* at 1400, 1480, 1500, 1580, etc.
00001980 b0 81 f4 03 0d 00 00 00 6e 0f 33 44 71 0f 33 44 |........n.3Dq.3D|
00001990 71 0f 33 44 00 00 00 00 f4 03 01 00 02 00 00 00 |q.3D............|
000019a0
000019b0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
38
00
04
00
00
00
00
00
00
00
00
00
00
00
00
00
|........8.......|
|................|
• The first few inodes are reserved for system
*
000019e0 00 00 00 00 34 3e 1d 2c 00 00 00 00 00 00 00 00 |....4>.,........|
use, as indicated in the source code:
000019f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
/*
00001a00 f8 41 f4 03 00 04 00 00 d7 21 3c 44 83 21 3c 44 |.A.......!<D.!<D|
* Special inode numbers
00001a10 83 21 3c 44 00 00 00 00 f4 03 02 00 02 00 00 00 |.!<D............|
*/
00001a20 00 00 00 00 00 00 00 00 39 04 00 00 00 00 00 00 |........9.......|
#define EXT2_BAD_INO 1 /* Bad blocks inode */
00001a30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
#define EXT2_ROOT_INO 2 /* Root inode */
*
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
00001a60 00 00 00 00 0b a7 c8 39 00 00 00 00 00 00 00 00 |.......9........|
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
00001a70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001a80 ff a1 f4 03 0c 00 00 00 d7 21 3c 44 83 21 3c 44 |.........!<D.!<D|
/* First non-reserved inode for old ext2 filesystems */
00001a90 83 21 3c 44 00 00 00 00 f4 03 01 00 00 00 00 00 |.!<D............|
#define EXT2_GOOD_OLD_FIRST_INO 11
00001aa0 00 00 00 00 00 00 00 00 2e 2e 2f 68 65 6c 6c 6f |........../hello|
00001ab0
00001ac0
2e
00
74
00
78
00
74
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
|.txt............|
|................|
Inode 11 (b hex) = 1400 + (80 * (b – 1)) = 1900
*
00001ae0 00 00 00 00 0c a7 c8 39 00 00 00 00 00 00 00 00 |.......9........|
00001af0
00001b00
00
b0
00
81
00
f4
00
03
00
0d
00
00
00
00
00
00
00
28
00
48
00
33
00
44
00
9d
00
21
00
3c
00
44
|................|
|........(H3D.!<D| • To go on with reading the volume, we focus
00001b10
00001b20
28
00
48
00
33
00
44
00
00
00
00
00
00
00
00
00
f4
41
03
04
02
00
00
00
02
00
00
00
00
00
00
00
|(H3D............|
|........A.......| on inode 2, which is the root directory’s
00001b30
*
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
inode; since inode 1 is in 1400, we expect
00001b60
00001b70
00 00 00 00 e6 32 42 c9
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
|.....2B.........|
|................| inode 2 in 1480
Inode Structure: As
struct ext2_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size; /* Size in bytes */

you’ve probably guessed by __le32 i_atime;


__le32 i_ctime;
__le32 i_mtime;
/* Access time */
/* Creation time */
/* Modification time */

now, an ext2 inode is mapped __le32 i_dtime;


__le16 i_gid;
/* Deletion Time */
/* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */

directly from its C structure


__le32 i_blocks; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
__le32 l_i_reserved1;

• Let’s start with the inode for the root } linux1;


struct {

directory — the key information here, for __le32 h_i_translator;


} hurd1;

getting to the rest of the volume, is to locate struct {


__le32 m_i_reserved1;

its first data block; in this case, it is also the } masix1;


} osd1; /* OS dependent 1 */

only data block, which is 25 (hex, of course) __le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl; /* File ACL */
__le32 i_dir_acl; /* Directory ACL */
00001480 ed 41 f4 03 00 04 00 00 ef 21 3c 44 9d 21 3c 44 |.A.......!<D.!<D| __le32 i_faddr; /* Fragment address */
00001490 9d 21 3c 44 00 00 00 00 f4 03 04 00 02 00 00 00 |.!<D............| union {
000014a0 00 00 00 00 00 00 00 00 25 00 00 00 00 00 00 00 |........%.......| struct {
000014b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| __u8 l_i_frag; /* Fragment number */
* __u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__le16 l_i_uid_high; /* these 2 fields */

• The rest of the fields should be easy to parse __le16 l_i_gid_high;


__u32 l_i_reserved2;
/* were reserved2[0] */

out now; for instance, the root directory’s mode } linux2;


struct {
is 41 ed or binary 0100 0001 1110 1101 __u8
__u8
h_i_frag; /* Fragment number */
h_i_fsize; /* Fragment size */
__le16 h_i_mode_high;

• The 9 low-order bits correspond to the __le16 h_i_uid_high;


__le16 h_i_gid_high;

traditional Unix permissions, rwxrwxrwx; this


__le32 h_i_author;
} hurd2;

directories permissions are thus rwxr-xr-x


struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;

• The 0100 on the high end indicates that this __u32


} masix2;
m_i_reserved2[2];

inode represents a directory (S_IFDIR in stat.h) };


} osd2; /* OS dependent 2 */

Directories: A directory’s data block is an array of


directory entries; here’s the one for the root directory,
located at data block 25 or offset 9400
• As should be obvious at this point, we use the
directory entry’s C structure in the source
code to read the directory:
struct ext2_dir_entry_2 { So the file called hello.txt is in the
__le32 inode;
__le16 rec_len;
/* Inode number */
/* Directory entry length */ twelfth inode, and its directory
__u8
__u8
name_len;
file_type;
/* Name length */
entry is 20 bytes long
char name[EXT2_NAME_LEN]; /* File name */
}; 00009400 02 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................|
00009410 0c 00 02 02 2e 2e 00 00 0b 00 00 00 14 00 0a 02 |................|
00009420 6c 6f 73 74 2b 66 6f 75 6e 64 00 00 0c 00 00 00 |lost+found......|
00009430 14 00 09 01 68 65 6c 6c 6f 2e 74 78 74 00 00 00 |....hello.txt...|
00009440 0f 00 00 00 14 00 0b 01 67 6f 6f 64 62 79 65 2e |........goodbye.|
• Note how the current directory (“.”) 00009450
00009460
74
72
78
62
74
79
00
65
0d
2e
00
74
00
78
00
74
ac
2e
03
73
05
77
02
70
6d
00
79
00
64
00
69
00
|txt.........mydi|
|rbye.txt.swp....|
and parent directory (“..”) are stored 00009470
00009480
94
2e
03
73
0c
77
01
78
67
00
6f
00
6f
00
64
00
62
00
79
00
65
00
2e
00
74
00
78
00
74
00
7e
00
|....goodbye.txt~|
|.swx............|
as explicit directory entries too; since 00009490
*
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

this is the root directory, it make


The mydir directory entry is immediately
sense that both . and .. refer to the
followed by what appears to be garbage —
same inode
you’re seeing the remnants of prior
directory entries that have since been
deleted or overwritten (see how the
filename is 5 bytes long, and how the
directory entry itself is 3ac bytes long —
i.e., the remainder of the data block!)
Files etc.: At last, we get to some actual files — the text
files are easy to locate, and additional directories are read in
the same way as the root directory
• hello.txt is in inode c, which translates • mydir is in inode d (right after hello.txt’s
to offset 1980; the inode then says that inode), and the file type of 02 indicates that
the file’s first data block is in overall it is a directory; its first data block is 0439
data block 0438
00001980 b0 81 f4 03 0d 00 00 00 6e 0f 33 44 71 0f 33 44 |........n.3Dq.3D| 00001a00 f8 41 f4 03 00 04 00 00 d7 21 3c 44 83 21 3c 44 |.A.......!<D.!<D|
00001990 71 0f 33 44 00 00 00 00 f4 03 01 00 02 00 00 00 |q.3D............| 00001a10 83 21 3c 44 00 00 00 00 f4 03 02 00 02 00 00 00 |.!<D............|
000019a0 00 00 00 00 00 00 00 00 38 04 00 00 00 00 00 00 |........8.......| 00001a20 00 00 00 00 00 00 00 00 39 04 00 00 00 00 00 00 |........9.......|
000019b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00001a30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* *

• Thus, the file’s data can be found at • Data block 0439 is at offset 10e400, which
offset 400 * 0438 = 10e000, and indeed should now be somewhat recognizable as an
the bytes are there! array of directory entries (note how . and ..
now refer to different inodes)

0010e000 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 00 00 |Hello world!....|


0010e010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0010e400 0d 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................|
0010e410 0c 00 02 02 2e 2e 00 00 0f 00 00 00 14 00 0b 01 |................|
0010e420 67 6f 6f 64 62 79 65 2e 74 78 74 00 0e 00 00 00 |goodbye.txt.....|
0010e430 d4 03 09 07 68 65 6c 6c 6f 2e 74 78 74 00 00 00 |....hello.txt...|
0010e440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*

Special Files: If we follow the directory entries in mydir,


we’ll notice a few more variations in how files are handled
0010e400 0d 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................|
0010e410 0c 00 02 02 2e 2e 00 00 0f 00 00 00 14 00 0b 01 |................|
0010e420 67 6f 6f 64 62 79 65 2e 74 78 74 00 0e 00 00 00 |goodbye.txt.....|
0010e430 d4 03 09 07 68 65 6c 6c 6f 2e 74 78 74 00 00 00 |....hello.txt...|
0010e440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*

• goodbye.txt is indicated to be in inode f, just like goodbye.txt in


the root directory — this is how hard links are implemented:
they just directory entries that refer to the same inode!

0010e400 0d 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................|
0010e410 0c 00 02 02 2e 2e 00 00 0f 00 00 00 14 00 0b 01 |................|
0010e420 67 6f 6f 64 62 79 65 2e 74 78 74 00 0e 00 00 00 |goodbye.txt.....|
0010e430 d4 03 09 07 68 65 6c 6c 6f 2e 74 78 74 00 00 00 |....hello.txt...|
0010e440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*

/*
* Ext2 directory file types. Only the • hello.txt’s directory entry lists its file type as 07, which,
* low 3 bits are used. The other bits
* are reserved for now. according to the source code, means a symbolic link
*/
enum {
EXT2_FT_UNKNOWN, /* 00 */
EXT2_FT_REG_FILE, /* 01 */
• When we look at inode e, we see that the symbolic link’s
EXT2_FT_DIR,
EXT2_FT_CHRDEV,
/* 02 */
/* 03 */
path is stored in the inode itself, where the data blocks
EXT2_FT_BLKDEV,
EXT2_FT_FIFO,
/* 04 */
/* 05 */
would normally be:
EXT2_FT_SOCK, /* 06 */
EXT2_FT_SYMLINK, /* 07 */ 00001a80 ff a1 f4 03 0c 00 00 00 d7 21 3c 44 83 21 3c 44 |.........!<D.!<D|
EXT2_FT_MAX /* 08 */ 00001a90 83 21 3c 44 00 00 00 00 f4 03 01 00 00 00 00 00 |.!<D............|
}; 00001aa0 00 00 00 00 00 00 00 00 2e 2e 2f 68 65 6c 6c 6f |........../hello|
00001ab0 2e 74 78 74 00 00 00 00 00 00 00 00 00 00 00 00 |.txt............|
00001ac0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*

You might also like