使用 NFS 挂载时,如何才能在目录中拥有两个同名的文件?

使用 NFS 挂载时,如何才能在目录中拥有两个同名的文件?

我有一个 C++ 应用程序测试,它在 NFS 挂载目录中创建了 10,000 个文件,但我的测试最近失败了一次,因为一个文件在该目录中以相同的名称出现了两次,而其他 10,000 个文件也是如此。这可以在目录挂载为 NFS 的 Linux Centos v4 或 v5 上看到,但在磁盘所在的主机上看不到。

怎么可能在同一个目录中有两个同名的文件呢?

[centos4x32 destination] ls -al ./testfile03373
-rwx------  1 user root 3373 Sep  3 03:23 ./testfile03373*
[centos4x32 destination] ls -al ./testfile03373*
-rwx------  1 user root 3373 Sep  3 03:23 ./testfile03373*
-rwx------  1 user root 3373 Sep  3 03:23 ./testfile03373*
[centos4x32 destination] ls -al *testfile03373
-rwx------  1 user root 3373 Sep  3 03:23 testfile03373*
-rwx------  1 user root 3373 Sep  3 03:23 testfile03373*
[centos4x32 destination] ls -alb test*file03373
-rwx------  1 user root 3373 Sep  3 03:23 testfile03373*
-rwx------  1 user root 3373 Sep  3 03:23 testfile03373*

运行以下答案之一中建议的 Perl 脚本:

ls -la *03373* | perl -e 'while(<>){chomp();while(/(.)/g){$c=$1;if($c=~/[!-~]/){print("$c");}else{printf("\\x%.2x",ord($c));}}print("\n");}'

给出:

-rwx------\x20\x201\x20user\x20root\x203373\x20Sep\x20\x203\x2003:23\x20testfile03373*
-rwx------\x20\x201\x20user\x20root\x203373\x20Sep\x20\x203\x2003:23\x20testfile03373*

使用 inode (-i) 值打印显示两个副本具有相同的 inode 条目 (36733444):

[h3-centos4x32 destination] ls -alib te*stfile03373
36733444 -rwx------  1 user root 3373 Sep  3 03:23 testfile03373*
36733444 -rwx------  1 user root 3373 Sep  3 03:23 testfile03373*

看起来目录条目已损坏。

我的应用程序是否合法地造成了这种情况,或者这是操作系统中的错误?在创建文件的程序中,我可以采取什么措施来防止这种情况发生?

我认为 NFS 安装软件中存在某种错误。此外,对出现问题的 NFS 驱动器执行“卸载”然后“安装”也无法解决问题,重新安装后仍会出现重复条目​​。


更新 1:几个小时后,我第二次遇到此问题,真正奇怪的是它发生在完全相同的文件上,testfile03373尽管这次对于重复的文件,它获得了不同的 inode 213352984。我还要补充一点,该文件是在托管磁盘的 Centos 5 计算机上创建的,因此它是在本地创建的,并且在本地显示正确,但 NFS 挂载它的所有其他计算机都看到了重复的条目。


更新 2:我在 Centos v6 机器上安装了驱动器,/var/log/messages列出并看到其中的双重条目后发现以下内容:

[root@c6x64 double3373file]# ls -laiB testfile03373* ; tail -3 /var/log/messages
36733444 -rwx------. 1 user root 3373 Sep  3 03:23 testfile03373
36733444 -rwx------. 1 user root 3373 Sep  3 03:23 testfile03373
...
Sep  4 14:59:46 c6x64 kernel: NFS: directory user/double3373file contains a readdir loop.Please contact your server vendor.  The file: testfile03373 has duplicate cookie 7675190874049154909
Sep  4 14:59:46 c6x64 kernel: NFS: directory user/double3373file contains a readdir loop.Please contact your server vendor.  The file: testfile03373 has duplicate cookie 7675190874049154909

此外,我发现重命名文件会导致重复条目消失,但将其重命名会导致它重新出现重复,或者,只需触摸名为的新文件testfile03373,就会导致出现重复条目​​,但这只会发生在看到此重复条目的两个目录中。

答案1

一位朋友帮助我追踪了这个问题,并发现这是一个错误,记录在Linux 内核的 Bugzilla 38572 在此处. 该错误据称已在内核 3.0.0 版本中修复,但至少在 2.6.38 版本中仍存在。

问题是服务器的 ReadDIR() RPC 调用返回了不正确的结果。发生这种情况的原因如下:

当客户端读取目录时,它会指定最大缓冲区大小并将 cookie 清零。如果目录太大,回复会表明回复只是部分回复并更新 cookie。然后,客户端可以使用更新后的 cookie 重新执行 RPC 以获取下一个数据块。(数据是文件句柄和名称的集合。对于 ReadDirPlus(),还有 stat/inode/vnode 数据。)文档并未表明这是 ReadDirPlus() 的一个错误,但它可能也存在。

实际问题是每个块(名称,句柄元组)中的最后一个文件是有时作为下一个块中的第一个文件返回。

与底层文件系统的交互很差。Ext4 存在此问题,而 XFS 不存在。

这就是为什么问题在某些情况下会出现,但在其他情况下不会出现,并且很少发生在小目录中。如问题描述中所示,文件显示相同的 inode 编号,并且名称相同(未损坏)。由于 Linux 内核调用 vnode 操作来执行底层操作(例如 open() 等),因此文件系统的底层例程决定会发生什么。在这种情况下,如果所需信息不在其属性缓存中,NFS3 客户端只会将 vnode 操作转换为 RPC。这会导致混乱,因为客户端认为服务器无法做到这一点。

答案2

该磁盘是 NFS 挂载磁盘。当我转到发布驱动器的主机时,该文件仅列出一次。

可能是 NFS 的一个错误、问题或竞争条件。

如果您使用十六进制编辑器直接编辑文件系统结构,则可能会有两个同名的文件。但是,我不确定如果您尝试删除或打开这些文件会发生什么。我不确定 Linux 上有哪些工具可以通过 inode 编号(不能重复)访问文件,但这可能有效。

重复的文件名很fsck可能会被发现并尝试修复。

确保所有文件的尾随空格均不不同。

答案3

有可能您的某个文件名中有一个隐藏的不可打印字符或空格。您可以通过提供选项来检查-bls例如:

user@server:~/test$ ls -lab
total 8
drwxr-xr-x 2 user user 4096 Sep  3 12:20 .
drwx------ 8 user user 4096 Sep  3 12:20 ..
-rw-r--r-- 1 user user    0 Sep  3 12:19 hello
-rw-r--r-- 1 user user    0 Sep  3 12:19 hello\

请注意\该文件名末尾的空格。

   -b, --escape
          print C-style escapes for nongraphic characters

作为替代方案(尽管上述方法应该有效),您可以通过此 perl 脚本管道输出,以将任何不可打印的 ASCII 字符替换为其十六进制代码。例如,空格变成\x20

while (<>) {
    chomp();
    while (/(.)/g) {
        $c = $1;
        if ($c=~/[!-~]/) {
            print("$c");
        } else {
            printf("\\x%.2x", ord($c));
        }
    }
    print("\n");
}

用法:

ls -la | perl -e 'while(<>){chomp();while(/(.)/g){$c=$1;if($c=~/[!-~]/){print("$c");}else{printf("\\x%.2x",ord($c));}}print("\n");}'

相关内容