目录的存储格式(ext4)

目录的存储格式(ext4)

Content #

目录本身也是个文件,也有 inode。inode 里面也是指向一些块。和普通文件不同的是,普通文件的块里面保存的是文件数据,而目录文件的块里面保存的是目录里面一项一项的文件信息。这些信息我们称为 ext4_dir_entry。从代码来看,有两个版本,在成员来讲几乎没有差别,只不过第二个版本 ext4_dir_entry_2 是将一个 16 位的 name_len,变成了一个 8 位的 name_len 和 8 位的 file_type。

struct ext4_dir_entry {
  __le32  inode;      /* Inode number */
  __le16  rec_len;    /* Directory entry length */
  __le16  name_len;    /* Name length */
  char  name[EXT4_NAME_LEN];  /* File name */
};
struct ext4_dir_entry_2 {
  __le32  inode;      /* Inode number */
  __le16  rec_len;    /* Directory entry length */
  __u8  name_len;    /* Name length */
  __u8  file_type;
  char  name[EXT4_NAME_LEN];  /* File name */
};

在目录文件的块中,最简单的保存格式是列表,就是一项一项地将 ext4_dir_entry_2 列在那里。

每一项都会保存这个目录的下一级的文件的文件名和对应的 inode,通过这个 inode,就能找到真正的文件。第一项是“.”,表示当前目录,第二项是“..”,表示上一级目录,接下来就是一项一项的文件名和 inode。

如果一个目录下面的文件太多的时候,在这个目录下找一个文件,按照列表一个个去找,太慢了,于是我们就添加了索引的模式。如果在 inode 中设置 EXT4_INDEX_FL 标志,则目录文件的块的组织形式将发生变化,变成了下面定义的这个样子:

struct dx_root
{
  struct fake_dirent dot;
  char dot_name[4];
  struct fake_dirent dotdot;
  char dotdot_name[4];
  struct dx_root_info
  {
    __le32 reserved_zero;
    u8 hash_version;
    u8 info_length; /* 8 */
    u8 indirect_levels;
    u8 unused_flags;
  }
  info;
  struct dx_entry  entries[0];
};

第一项是“.”,表示当前目录;第二项是“..”,表示上一级目录,这两个不变。接下来是一个 dx_root_info 的结构,其中最重要的成员变量是 indirect_levels,表示间接索引的层数。

dx_entry的结构,其实就是文件名的哈希值和数据块的一个映射关系。

struct dx_entry
{
  __le32 hash;
  __le32 block;
};

如果我们要查找一个目录下面的文件名,可以通过名称取哈希。如果哈希能够匹配上,就说明这个文件的信息在相应的块里面。然后打开这个块,如果里面不再是索引,而是索引树的叶子节点的话,那里面还是 ext4_dir_entry_2 的列表,我们只要一项一项找文件名就行。通过索引树,我们可以将一个目录下面的 N 多的文件分散到很多的块里面,可以很快地进行查找。

Viewpoints #

From #

28 | 硬盘文件系统:如何最合理地组织档案库的文档?