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

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

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

12071208是目录和文件的索引节点号。然而,VFAT文件系统没有inode的概念。

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

答案1

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


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

  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 文件系统分配索引节点号的位置。如果我们往里面看,我们会看到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;
}

这基本上说的是:

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

inode->i_ino = iunique(sb, MSDOS_ROOT_INO);此处设置 inode 编号。iunique来源) 是一个与文件系统无关的函数,它为给定的超级块提供唯一的 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;
}

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

总结一下:

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

* ...不过,公平地说,根据合同,即使对于以下文件系统也是如此当变化时有一个 inode 概念i_generation- 只是在实践中不太可能发生,因为 inode 编号通常与其物理位置或类似位置相关。

相关内容