【问题标题】:Unix readdir (3) and stat (2) giving different inodesUnix readdir (3) 和 stat (2) 给出不同的 inode
【发布时间】:2018-04-16 12:34:43
【问题描述】:

我是我大学系统编程课的助教。最近学生们一直在做一个涉及复制程序pwd的作业。

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

这里有一些示例代码来演示:

#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;
}

输出:

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

编辑: 我可以确认 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)

编辑2: inode 似乎只在 nfs 文件系统转换时发生变化。我的问题是为什么。 readdir指向什么inode,stat指向什么inode?​​p>

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

我没有卸载权限。

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

【问题讨论】:

  • DIR_NAME 是挂载点吗?如果不是,它在什么文件系统上?无论哪种方式,我认为你在Unix and Linux Stack Exchange 上提问比在这里好。
  • 是的,它是一个挂载点。我将其添加到我的原始帖子中。我可能会把它放在这里一会儿,如果我没有得到很好的回应,就移动它。谢谢你的想法。
  • 回答 herehere 以及相关讨论可能具有一定的相关性。
  • 小心...有些 IP 不是匿名的...您已经仔细修改了 DNS,但不是 IP...许多大学都有大量公共 IP - 加州理工州立大学机会? beep
  • 您尝试过不是挂载点的东西吗?值是静态的吗?你有卸载它的能力吗?我猜stat() 是从本地文件系统提供inode,而readdir() 是从服务器提供inode。

标签: c unix stat inode readdir


【解决方案1】:

一个目录会有一个inode:

$ ls -li
total 4
264332 drwx------ 2 attie attie 4096 Nov  3 22:46 mnt

本例中mnt目录的inode为264322

但是如果我们现在在那个目录上挂载一个文件系统,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

现在,mnt 的 inode 似乎是 2...?! (请注意,所有者/组也已更改)。

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

$ ./test
readdir (3) gives an inode of:   264332.
stat (2) gives an inode of:           2.
  • readdir() 说的是264332
    • 这是基础文件系统上mnt 目录的inode。
  • stat() 说的是2
    • 哪个是已挂载文件系统的根目录的inode。

这是有道理的,因为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
[...]

如您所见,安装在/ 的文件系统的根也有一个2 的inode...inode 不是全局唯一的,而是仅在文件系统中唯一。


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

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

【讨论】:

  • 我实际上并没有写任何作业。您的 cmets 早些时候实际上促使我自己对此进行了一些检查,我发现了类似的东西。但我永远不可能对此事进行如此详细的介绍。
  • 我不知道谁反对这个或为什么。它对我来说似乎非常健壮。
  • 嗯,你们都是对的。 ino_t result; 确实未初始化,但除非全局变量 DIR_NAME 设置为有效目录名称以外的名称且不是 NULL,否则不会产生任何影响。除了那个辅助点,@Attie 的回答很好。
  • @wildplasser 感谢您的反馈,但这非常迂腐,与问题或讨论无关。 OP 不是在审查他们的示例应用程序(在受控情况下没有出现错误)之后,而是在深入了解 inode 的情况。
  • 除非 wildplasser 疯狂地讽刺“没有抓住重点”,我不知道他们的重点是什么。如果是这样,那么对这样一个通过开玩笑的​​答案投反对票似乎是非常粗鲁的。
猜你喜欢
  • 2015-06-22
  • 2018-12-13
  • 2014-07-03
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 2016-04-11
  • 2015-06-26
  • 1970-01-01
相关资源
最近更新 更多