Linux 如何在不基于 inode 的文件系统上分配 inode 编号?

tok*_*a-n 15 linux filesystems

这是该ls -li命令在 VFAT 文件系统上的输出。

% ls -li
?? 736
1207 drwxr-xr-x 3 root root  16384  3? 10 10:42 efi
1208 -rwxr-xr-x 1 root root 721720  3? 22 14:15 kernel.bin
Run Code Online (Sandbox Code Playgroud)

12071208是目录和文件的 inode 编号。但是,VFAT 文件系统没有 inode 的概念。

Linux 如何为没有 inode 概念的文件系统上的文件分配 inode 编号?

Chr*_*own 32

tl;dr:对于虚拟、易失或与 inode 无关的文件系统,inode 编号通常在创建 inode 时从单调递增的 32 位计数器生成。inode 的其余部分(例如权限)是从底层文件系统中的等效数据构建的,或者{uid,gid}=如果不存在这样的概念,则替换为在安装时设置的值(例如)。


要回答标题中的问题(即抽象地说,Linux 如何为没有 inode 概念的文件系统分配 inode 编号),这取决于文件系统。对于某些虚拟或无节点文件系统,在实例化时从get_next_ino池中提取 inode 编号。但是,这有很多问题:

  1. get_next_ino()即使在 64 位内核上也使用 32 位 inode 编号,这是由于 32 位用户空间的遗留处理而没有_FILE_OFFSET_BITS=64;
  2. get_next_ino() 只是多个文件系统使用的全局递增计数器,因此溢出的风险会进一步增加。

像这样的问题是我去年将 tmpfs 从 get_next_ino 支持的 inode 移开的原因之一。

出于这个原因,tmpfs 特别是大多数易失性或“无节点”文件系统格式的例外。get_next_ino从 5.11 开始,套接字、管道、ramfs 等仍然使用池。


至于您关于 FAT 文件系统的具体问题:fs/fat/inode.c是为 FAT 邪恶系统分配 inode 编号的位置。如果我们往里面看,我们会看到fat_build_inode来源):

struct inode *fat_build_inode(struct super_block *sb,
                              struct msdos_dir_entry *de, loff_t i_pos)
{
        struct inode *inode;
        int err;

        fat_lock_build_inode(MSDOS_SB(sb));
        inode = fat_iget(sb, i_pos);
        if (inode)
                goto out;
        inode = new_inode(sb);
        if (!inode) {
                inode = ERR_PTR(-ENOMEM);
                goto out;
        }
        inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
        inode_set_iversion(inode, 1);
        err = fat_fill_inode(inode, de);
        if (err) {
                iput(inode);
                inode = ERR_PTR(err);
                goto out;
        }
        fat_attach(inode, i_pos);
        insert_inode_hash(inode);
out:
        fat_unlock_build_inode(MSDOS_SB(sb));
        return inode;
}
Run Code Online (Sandbox Code Playgroud)

这基本上是这样说的:

  1. 为这个超级块获取 FAT inode 创建锁。
  2. 检查 inode 是否已经存在于超级块中的这个位置。如果是这样,请解锁并返回该 inode。
  3. 否则,创建一个新的 inode。
  4. 从中获取 inode 编号iunique(sb, MSDOS_ROOT_INO)(稍后会详细介绍)。
  5. 从等效的 FAT 数据结构填充 inode 的其余部分。

inode->i_ino = iunique(sb, MSDOS_ROOT_INO);是在这里设置 inode 编号的地方。iunique( source ) 是一个 fs 不可知函数,它为给定的超级块提供唯一的 inode 编号。它通过使用一个超级块 + 基于 inode 的哈希表来实现,并带有一个单调递增的计数器:

ino_t iunique(struct super_block *sb, ino_t max_reserved)
{
        static DEFINE_SPINLOCK(iunique_lock);
        static unsigned int counter;
        ino_t res;

        rcu_read_lock();
        spin_lock(&iunique_lock);
        do {
                if (counter <= max_reserved)
                        counter = max_reserved + 1;
                res = counter++;
        } while (!test_inode_iunique(sb, res)); /* nb: this checks the hash table */
        spin_unlock(&iunique_lock);
        rcu_read_unlock();

        return res;
}
Run Code Online (Sandbox Code Playgroud)

在这方面,它与前面提到的非常相似get_next_ino:只是每个超级块而不是全局的(如管道、套接字等),并且具有一些基于哈希表的基本冲突保护。它甚至继承了get_next_ino使用 32 位 inode 编号作为尝试避免遗留应用程序上的 EOVERFLOW 的方法的行为,因此可能会有更多的文件系统需要 64 位 inode 修复(如我前面提到inode64的 tmpfs 实现)未来。

所以总结一下:

  1. 大多数虚拟或无节点文件系统对 inode 编号使用单调递增的计数器。
  2. 即使对于磁盘无节点文件系统*,该计数器也不稳定。在重新挂载时,它可能会更改而无需对文件系统进行其他更改。
  3. 大多数处于这种状态的文件系统(tmpfs 除外inode64)仍在使用 32 位计数器,因此大量使用时,计数器完全有可能溢出,最终可能会出现重复的 inode。

* ...虽然,公平地说,即使对于在更改时确实具有 inode 概念的文件系统,这也是正确的i_generation- 在实践中不太可能发生,因为 inode 编号通常与其物理位置相关,或者相似的。

  • “FAT vilesystems”——有史以来最伟大、技术上最准确的错别字? (20认同)
  • 说到物理位置,是否有某种原因 VFAT 驱动程序不能使用目录条目的物理位置来生成 inode 号? (3认同)