Product SiteDocumentation Site

Chapter 6.  Directories

6.1. Shortform Directories
6.2. Block Directories
6.3. Leaf Directories
6.4. Node Directories
6.5. B+tree Directories

6.1.  Shortform Directories

  • Directory entries are stored within the inode.
  • Only data stored is the name, inode # and offset, no "leaf" or "freespace index" information is required as an inode can only store a few entries.
  • "." is not stored (as it's in the inode itself), and ".." is a dedicated parent field in the header.
  • The number of directories that can be stored in an inode depends on the inode size (Chapter 4, On-disk Inode), the number of entries, the length of the entry names and extended attribute data.
  • Once the number of entries exceed the space available in the inode, the format is converted to a "Block Directory".
  • Shortform directory data is packed as tightly as possible on the disk with the remaining space zeroed:
    typedef struct xfs_dir2_sf {
         xfs_dir2_sf_hdr_t         hdr;
         xfs_dir2_sf_entry_t       list[1];
    } xfs_dir2_sf_t;
    typedef struct xfs_dir2_sf_hdr {
         __uint8_t                 count;
         __uint8_t                 i8count;
         xfs_dir2_inou_t           parent;
    } xfs_dir2_sf_hdr_t;
    typedef struct xfs_dir2_sf_entry {
         __uint8_t                 namelen;
         xfs_dir2_sf_off_t         offset;
         __uint8_t                 name[1];
         xfs_dir2_inou_t           inumber;
    } xfs_dir2_sf_entry_t;
    
    39
  • Inode numbers are stored using 4 or 8 bytes depending on whether all the inode numbers for the directory fit in 4 bytes (32 bits) or not. If all inode numbers fit in 4 bytes, the header's count value specifies the number of entries in the directory and i8count will be zero. If any inode number exceeds 4 bytes, all inode numbers will be 8 bytes in size and the header's i8count value specifies the number of entries and count will be zero. The following union covers the shortform inode number structure:
    	typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t;
    typedef struct { __uint8_t i[4]; } xfs_dir2_ino4_t;
    typedef union {
         xfs_dir2_ino8_t           i8;
         xfs_dir2_ino4_t           i4;
    } xfs_dir2_inou_t;
    

xfs_db Example:

A directory is created with 4 files, all inode numbers fitting within 4 bytes:
xfs_db> inode <inode#>
xfs_db> p
core.magic = 0x494e
core.mode = 040755
core.version = 1
core.format = 1 (local)
core.nlinkv1 = 2
...
core.size = 94
core.nblocks = 0
core.extsize = 0
core.nextents = 0
...
u.sfdir2.hdr.count = 4
u.sfdir2.hdr.i8count = 0
u.sfdir2.hdr.parent.i4 = 128              /* parent = root inode */
u.sfdir2.list[0].namelen = 15
u.sfdir2.list[0].offset = 0x30
u.sfdir2.list[0].name = "frame000000.tst"
u.sfdir2.list[0].inumber.i4 = 25165953
u.sfdir2.list[1].namelen = 15
u.sfdir2.list[1].offset = 0x50
u.sfdir2.list[1].name = "frame000001.tst"
u.sfdir2.list[1].inumber.i4 = 25165954
u.sfdir2.list[2].namelen = 15
u.sfdir2.list[2].offset = 0x70
u.sfdir2.list[2].name = "frame000002.tst"
u.sfdir2.list[2].inumber.i4 = 25165955
u.sfdir2.list[3].namelen = 15
u.sfdir2.list[3].offset = 0x90
u.sfdir2.list[3].name = "frame000003.tst"
u.sfdir2.list[3].inumber.i4 = 25165956
The raw data on disk with the first entry highlighted. The six byte header precedes the first entry:
code40
Next, an entry is deleted (frame000001.tst), and any entries after the deleted entry are moved or compacted to "cover" the hole:
xfs_db> inode <inode#>
xfs_db> p
core.magic = 0x494e
core.mode = 040755
core.version = 1
core.format = 1 (local)
core.nlinkv1 = 2
...
core.size = 72
core.nblocks = 0
core.extsize = 0
core.nextents = 0
...
u.sfdir2.hdr.count = 3
u.sfdir2.hdr.i8count = 0
u.sfdir2.hdr.parent.i4 = 128
u.sfdir2.list[0].namelen = 15
u.sfdir2.list[0].offset = 0x30
u.sfdir2.list[0].name = "frame000000.tst"
u.sfdir2.list[0].inumber.i4 = 25165953
u.sfdir2.list[1].namelen = 15
u.sfdir2.list[1].offset = 0x70
u.sfdir2.list[1].name = "frame000002.tst"
u.sfdir2.list[1].inumber.i4 = 25165955
u.sfdir2.list[2].namelen = 15
u.sfdir2.list[2].offset = 0x90
u.sfdir2.list[2].name = "frame000003.tst"
u.sfdir2.list[2].inumber.i4 = 25165956
Raw disk data, the space beyond the shortform entries is invalid and could be non-zero:
xfs_db> type text
xfs_db> p
00: 49  4e 41 ed 01 01 00 02 00 00 00 00 00 00 00 00 INA.............
10: 00  00 00 02 00 00 00 00 00 00 00 00 00 00 00 03 ................
20: 44  b2 45 a2 09 fd e4 50 44 b2 45 a3 12 ee b5 d0 D.E....PD.E.....
30: 44  b2 45 a3 12 ee b5 d0 00 00 00 00 00 00 00 48 D.E............H
40: 00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00  00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: ff  ff ff ff 03 00 00 00 00 80 0f 00 30 66 72 61 ............0fra
70: 6d  65 30 30 30 30 30 30 2e 74 73 74 01 80 00 81 me000000.tst....
80: 0f  00 70 66 72 61 6d 65 30 30 30 30 30 32 2e 74 ..pframe000002.t
90: 73  74 01 80 00 83 0f 00 90 66 72 61 6d 65 30 30 st.......frame00
a0: 30  30 30 33 2e 74 73 74 01 80 00 84 0f 00 90 66 0003.tst.......f
b0: 72  61 6d 65 30 30 30 30 30 33 2e 74 73 74 01 80 rame000003.tst..
c0: 00  84 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
TODO: 8-byte inode number example