Unix readdir (3) 和 stat (2) 给出不同的 inode
Unix readdir (3) and stat (2) giving different inodes
我是大学系统编程的助教class。最近,学生们一直在做一项涉及复制程序的作业 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;
}
输出:
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?
自从我发布这篇文章以来,这两个 inode 在过去 4 小时内都发生了变化。
我没有卸载权限。
我检查了从同一地址挂载的另一个目录,两个索引节点都与第一个目录不同,这表明每个目录确实有两个该目录唯一的索引节点,但我不明白为什么。
一个目录将有一个索引节点:
$ 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
的索引节点似乎是 2
...?! (注意 owner/group 也发生了变化)。
如果我们要 运行 您的应用程序在此目录上(删除 chdir()
并将 DIR_NAME
更改为 mnt
),那么我们会得到一个有趣的结果:
$ ./test
readdir (3) gives an inode of: 264332.
stat (2) gives an inode of: 2.
readdir()
是说 264332
- 这是底层文件系统上
mnt
目录的索引节点。
stat()
表示 2
- 挂载的文件系统root 的索引节点。
这是有道理的,因为 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
[...]
如您所见,安装在/
的文件系统的root 也有一个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
我是大学系统编程的助教class。最近,学生们一直在做一项涉及复制程序的作业 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;
}
输出:
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?
自从我发布这篇文章以来,这两个 inode 在过去 4 小时内都发生了变化。
我没有卸载权限。
我检查了从同一地址挂载的另一个目录,两个索引节点都与第一个目录不同,这表明每个目录确实有两个该目录唯一的索引节点,但我不明白为什么。
一个目录将有一个索引节点:
$ 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
的索引节点似乎是 2
...?! (注意 owner/group 也发生了变化)。
如果我们要 运行 您的应用程序在此目录上(删除 chdir()
并将 DIR_NAME
更改为 mnt
),那么我们会得到一个有趣的结果:
$ ./test
readdir (3) gives an inode of: 264332.
stat (2) gives an inode of: 2.
readdir()
是说264332
- 这是底层文件系统上
mnt
目录的索引节点。
- 这是底层文件系统上
stat()
表示2
- 挂载的文件系统root 的索引节点。
这是有道理的,因为 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
[...]
如您所见,安装在/
的文件系统的root 也有一个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