Unix readdir (3) 和 stat (2) 给出不同的 inode

Tho*_*mas 5 c unix inode stat readdir

我是我大学系统编程课的助教。最近,学生们正在完成一项涉及复制该程序的作业pwd

一些学生注意到了似乎不一致的地方。当他们从 readdir 条目中读取 ino 时,它会给出与统计同一目录时不同的 inode。他们中的许多人都在问为什么。目录项的索引节点不应该指向目标目录所在的索引节点吗?如果是这样,为什么 stat 给出不同的 inode?

这是一些示例代码来演示:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>

#define DIR_NAME "~redacted~"

int getReaddirInode(char* entName)
{
   DIR* directory;
   struct dirent* entry;
   ino_t result;
   if ((directory = opendir(".")) == NULL)
   {
      perror("opendir");
      exit(1);
   }
   while ((entry = readdir(directory)) != NULL)
   {
      if (strcmp(entName, entry->d_name) == 0)
      {
         result = entry->d_ino;
         break;
      }
   }
   if (entry == NULL)
   {
      fprintf(stderr, "No such directory: %s.\n", entName);
      exit(1);
   }
   if (closedir(directory) == -1)
   {
      perror("closedir");
      exit(1);
   }
   return result;
}

int getStatInode(char* entName)
{
   struct stat buf;
   if (stat(entName, &buf) == -1)
   {
      perror("stat");
      exit(1);
   }
   return buf.st_ino;
}

int main()
{
   if (chdir("/home") == -1)
   {
      perror("chdir");
      return 1;
   }
   printf("readdir (3) gives an inode of:%9d.\n", getReaddirInode(DIR_NAME));
   printf("stat (2) gives an inode of:   %9d.\n", getStatInode(DIR_NAME));
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

unix3:~ $ ./a.out 
readdir (3) gives an inode of:  4053392.
stat (2) gives an inode of:    69205302.
Run Code Online (Sandbox Code Playgroud)

编辑:我可以确认 DIR_NAME 是一个挂载点:

unix3:~ $ mount | grep ~redacted~
csc-na01.csc.~redacted~.edu:/student01/student01/0_t/~redacted~ on /home/~redacted~ type nfs (rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=129.65.158.8,mountvers=3,mountport=635,mountproto=udp,local_lock=none,addr=129.65.158.8)
Run Code Online (Sandbox Code Playgroud)

Edit2:inode 似乎仅在 nfs 文件系统转换时发生变化。我的问题是为什么。readdir 指向哪个 inode,stat 指向哪个 inode?

自从我发布这篇文章以来,这两个索引节点在过去 4 小时内都发生了变化。

我没有卸载权限。

我检查了从同一地址挂载的另一个目录,两个 inode 都与第一个目录不同,这表明每个目录确实有两个该目录唯一的 inode,但我不明白为什么。

Att*_*tie 6

一个目录将有一个 inode:

$ ls -li
total 4
264332 drwx------ 2 attie attie 4096 Nov  3 22:46 mnt
Run Code Online (Sandbox Code Playgroud)

在这种情况下,目录的索引节点mnt264322

但如果我们现在在该目录上挂载文件系统,inode 将会发生变化:

$ truncate -s $((5 * 1024 * 1024)) myfs.ext2
$ mkfs.ext2 ./myfs.ext2
mke2fs 1.42.13 (17-May-2015)
Discarding device blocks: done
Creating filesystem with 5120 1k blocks and 1280 inodes

Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

$ sudo mount -o loop myfs.ext2 ./mnt
$ ls -li
total 265
     2 drwxr-xr-x 3 root  root     1024 Nov  3 22:49 mnt
264339 -rw------- 1 attie attie 5242880 Nov  3 22:49 myfs.ext2
Run Code Online (Sandbox Code Playgroud)

现在,它的索引节点似乎mnt2?!(请注意,所有者/组也已更改)。

如果我们要在此目录上运行您的应用程序(删除chdir()并更改DIR_NAMEmnt),那么我们会得到一个有趣的结果:

$ ./test
readdir (3) gives an inode of:   264332.
stat (2) gives an inode of:           2.
Run Code Online (Sandbox Code Playgroud)
  • readdir()正在说264332
    • mnt这是底层文件系统上目录的索引节点。
  • stat()正在说2
    • 这是已挂载文件系统根的索引节点。

这是有道理的,因为readdir()正在遍历给定目录的节点,返回每个节点的信息(但它没有完整检查它们)...同时stat()正在返回给定完整路径的信息,包括解析任何安装点。

在您的情况下,您有一个 NFS 挂载,该远程文件系统挂载在该系统上的一个目录(有一个 inode)上...但它也是远程系统上的一个目录(有一个 inode)。


我们可以通过执行以下操作进一步证明这一点:

$ ls -lia /
total 137
      2 drwxr-xr-x  25 root root    4096 Oct 11 17:17 .
      2 drwxr-xr-x  25 root root    4096 Oct 11 17:17 ..
2097153 drwxr-xr-x   2 root root   12288 Oct 10 13:36 bin
[...]
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,挂载的文件系统的根目录/也有一个索引节点2...索引节点不是全局唯一的,而只是在文件系统内唯一的。


如果您参与作业评分,那么我想说这两个答案都是可以接受的...对于可能刚接触这个世界的学生来说,这是一个非常微妙和复杂的事情,需要完全理解。

stat()可以很容易地检查答案ls -i

我认为readdir()在不编写小应用程序的情况下检查答案将会很繁琐(这不是问题)...如果您有权限,您可以使用绑定安装:

$ mkdir mnt2
$ sudo mount -o bind . ./mnt2
$ ls -li
total 285
     2 drwxr-xr-x 3 root  root     1024 Nov  3 22:49 mnt
264319 drwx------ 4 attie attie    4096 Nov  3 23:09 mnt2
264339 -rw------- 1 attie attie 5242880 Nov  3 22:49 myfs.ext2
264346 -rwx------ 1 attie attie    9160 Nov  3 23:00 test
264349 -rw------- 1 attie attie     956 Nov  3 23:00 test.c
$ ls -li mnt2
total 288
264332 drwx------ 2 attie attie    4096 Nov  3 22:46 mnt
264347 drwx------ 2 attie attie    4096 Nov  3 23:09 mnt2
264339 -rw------- 1 attie attie 5242880 Nov  3 22:49 myfs.ext2
264346 -rwx------ 1 attie attie    9160 Nov  3 23:00 test
264349 -rw------- 1 attie attie     956 Nov  3 23:00 test.c
Run Code Online (Sandbox Code Playgroud)

  • @wildplasser 感谢您的反馈,但这非常迂腐,与问题或讨论无关。OP 并不是在审查他们的示例应用程序(在受控情况下不会出现错误),而是深入了解 inode 的情况。 (3认同)
  • 嗯,你们都是对的。`ino_t result;` 确实未初始化,但除非将全局变量 `DIR_NAME` 设置为除有效目录名之外的其他内容并且不是 `NULL`,否则不会产生任何效果。除了那个辅助点之外,@Attie 的答案很好。 (2认同)
  • 除非 Wildplasser 极度讽刺“没有抓住重点”,否则我不知道他们认为重点是什么。如果是的话,那么对一个笑话这样一个彻底的答案投反对票似乎是非常粗鲁的。 (2认同)