Sherlock-Holo / fuse3

an async version fuse library for rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with hard link support

KongzhangHao opened this issue · comments

I found that when running ln target_file link_file in a mounted directory where target_file exists and link_file does not exist, it shows an error: ln: failed to create hard link 'link_file' => 'target_file': No such file or directory. When checking the log, it shows that lookup(target_file) and lookup(link_file) are called, while lookup(target_file) finds the inode and lookup(link_file) doesn't find anything, which is expected. But then it says because link_file doesn't exist, there is an ENOENT error, which is very weird. If I create a file called link_file, it shows the error: ln: failed to create hard link 'link_file': file exists. Also, link() in the filesystem trait is never called and the opcode for it is never received.

I update the memfs example, implement the link method, and check the hard link

the log reports

2023-01-15T04:43:21.974053Z DEBUG fuse3::raw::session: receive opcode FUSE_GETATTR
at src/raw/session.rs:349

2023-01-15T04:43:21.974151Z DEBUG fuse3::raw::session: getattr unique 222 inode 3
at src/raw/session.rs:959
in fuse3::raw::session::fuse_getattr
in fuse3::raw::session::handle_getattr with request: Request { unique: 222, uid: 1000, gid: 1000, pid: 39849 }, in_header: fuse_in_header { len: 56, opcode: 3, unique: 222, nodeid: 3, uid: 1000, gid: 1000, pid: 39849, padding: 0 }

2023-01-15T04:43:21.977295Z DEBUG fuse3::raw::session: receive opcode FUSE_LOOKUP
at src/raw/session.rs:349

2023-01-15T04:43:21.977348Z DEBUG fuse3::raw::session: lookup unique 224 name "a" in parent 1
at src/raw/session.rs:850
in fuse3::raw::session::fuse_lookup
in fuse3::raw::session::handle_lookup with request: Request { unique: 224, uid: 1000, gid: 1000, pid: 39853 }, in_header: fuse_in_header { len: 42, opcode: 1, unique: 224, nodeid: 1, uid: 1000, gid: 1000, pid: 39853, padding: 0 }

2023-01-15T04:43:21.977373Z DEBUG fuse3::raw::session: lookup response fuse_entry_out { nodeid: 2, generation: 0, entry_valid: 1, attr_valid: 1, entry_valid_nsec: 0, attr_valid_nsec: 0, attr: fuse_attr { ino: 2, size: 0, blocks: 0, atime: 0, mtime: 0, ctime: 0, atimensec: 0, mtimensec: 0, ctimensec: 0, mode: 16877, nlink: 0, uid: 0, gid: 0, rdev: 0, blksize: 0, padding: 0 } }
at src/raw/session.rs:871
in fuse3::raw::session::fuse_lookup
in fuse3::raw::session::handle_lookup with request: Request { unique: 224, uid: 1000, gid: 1000, pid: 39853 }, in_header: fuse_in_header { len: 42, opcode: 1, unique: 224, nodeid: 1, uid: 1000, gid: 1000, pid: 39853, padding: 0 }

2023-01-15T04:43:21.977433Z DEBUG fuse3::raw::session: receive opcode FUSE_LOOKUP
at src/raw/session.rs:349

2023-01-15T04:43:21.977475Z DEBUG fuse3::raw::session: lookup unique 226 name "123.txt" in parent 2
at src/raw/session.rs:850
in fuse3::raw::session::fuse_lookup
in fuse3::raw::session::handle_lookup with request: Request { unique: 226, uid: 1000, gid: 1000, pid: 39853 }, in_header: fuse_in_header { len: 48, opcode: 1, unique: 226, nodeid: 2, uid: 1000, gid: 1000, pid: 39853, padding: 0 }

2023-01-15T04:43:21.977498Z DEBUG fuse3::raw::session: lookup response fuse_entry_out { nodeid: 4, generation: 0, entry_valid: 1, attr_valid: 1, entry_valid_nsec: 0, attr_valid_nsec: 0, attr: fuse_attr { ino: 4, size: 4, blocks: 0, atime: 0, mtime: 0, ctime: 0, atimensec: 0, mtimensec: 0, ctimensec: 0, mode: 33188, nlink: 0, uid: 0, gid: 0, rdev: 0, blksize: 0, padding: 0 } }
at src/raw/session.rs:871
in fuse3::raw::session::fuse_lookup
in fuse3::raw::session::handle_lookup with request: Request { unique: 226, uid: 1000, gid: 1000, pid: 39853 }, in_header: fuse_in_header { len: 48, opcode: 1, unique: 226, nodeid: 2, uid: 1000, gid: 1000, pid: 39853, padding: 0 }

2023-01-15T04:43:21.977559Z DEBUG fuse3::raw::session: receive opcode FUSE_LOOKUP
at src/raw/session.rs:349

2023-01-15T04:43:21.977601Z DEBUG fuse3::raw::session: lookup unique 228 name "bbb.txt" in parent 3
at src/raw/session.rs:850
in fuse3::raw::session::fuse_lookup
in fuse3::raw::session::handle_lookup with request: Request { unique: 228, uid: 1000, gid: 1000, pid: 39853 }, in_header: fuse_in_header { len: 48, opcode: 1, unique: 228, nodeid: 3, uid: 1000, gid: 1000, pid: 39853, padding: 0 }

that's so strange, when running ln a b, kernel expect b should be exists

the ln strace is

execve("/usr/bin/ln", ["ln", "../a/123.txt", "bbb.txt"], 0x7ffd405da150 /* 64 vars /) = 0
brk(NULL) = 0x55d99438c000
arch_prctl(0x3001 /
ARCH_??? /, 0x7ffed768dba0) = -1 EINVAL (无效的参数)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=194271, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 194271, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa79dd50000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P4\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1953472, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa79dd4e000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1994384, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa79db67000
mmap(0x7fa79db89000, 1421312, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7fa79db89000
mmap(0x7fa79dce4000, 356352, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17d000) = 0x7fa79dce4000
mmap(0x7fa79dd3b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d4000) = 0x7fa79dd3b000
mmap(0x7fa79dd41000, 52880, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa79dd41000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa79db64000
arch_prctl(ARCH_SET_FS, 0x7fa79db64740) = 0
set_tid_address(0x7fa79db64a10) = 39853
set_robust_list(0x7fa79db64a20, 24) = 0
rseq(0x7fa79db65060, 0x20, 0, 0x53053053) = 0
mprotect(0x7fa79dd3b000, 16384, PROT_READ) = 0
mprotect(0x55d992972000, 4096, PROT_READ) = 0
mprotect(0x7fa79ddb1000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192
1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7fa79dd50000, 194271) = 0
getrandom("\x39\x83\x0f\xc3\x56\x4d\xa4\x6e", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x55d99438c000
brk(0x55d9943ad000) = 0x55d9943ad000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=6213312, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 6213312, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa79d400000
close(3) = 0
linkat(AT_FDCWD, "../a/123.txt", AT_FDCWD, "bbb.txt", 0) = -1 ENOENT (没有那个文件或目录)
newfstatat(AT_FDCWD, "../a/123.txt", {st_mode=S_IFREG|0644, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2998, ...}, AT_EMPTY_PATH) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2998
read(3, "", 4096) = 0
close(3) = 0
openat(AT_FDCWD, "/usr/share/locale/zh_CN.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/usr/share/locale/zh_CN.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/usr/share/locale/zh_CN/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=359611, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 359611, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa79db0c000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=3916, ...}, AT_EMPTY_PATH) = 0
read(3, "# GNU libc iconv configuration.\n"..., 4096) = 3916
read(3, "", 4096) = 0
close(3) = 0
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=48, ...}, AT_EMPTY_PATH) = 0
getdents64(3, 0x55d994394500 /* 3 entries /, 32768) = 96
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.d/gconv-modules-extra.conf", O_RDONLY|O_CLOEXEC) = 4
newfstatat(4, "", {st_mode=S_IFREG|0644, st_size=53974, ...}, AT_EMPTY_PATH) = 0
read(4, "# GNU libc iconv configuration.\n"..., 4096) = 4096
read(4, "B1002//\tJUS_I.B1.002//\nmodule\tJU"..., 4096) = 4096
read(4, "59-5//\nalias\tISO_8859-5//\t\tISO-8"..., 4096) = 4096
read(4, "59-16//\t\tINTERNAL\t\tISO8859-16\t1\n"..., 4096) = 4096
read(4, "-SE-A\t1\nmodule\tINTERNAL\t\tEBCDIC-"..., 4096) = 4096
read(4, "97\t\t1\n\n#\tfrom\t\t\tto\t\t\tmodule\t\tcos"..., 4096) = 4096
read(4, "1\n\n#\tfrom\t\t\tto\t\t\tmodule\t\tcost\nal"..., 4096) = 4096
brk(0x55d9943ce000) = 0x55d9943ce000
read(4, "6//\t\tIBM1046//\nalias\tCP1046//\t\tI"..., 4096) = 4096
read(4, "\tto\t\t\tmodule\t\tcost\nalias\tRUSCII/"..., 4096) = 4096
read(4, "03//\nmodule\tCSN_369103//\t\tINTERN"..., 4096) = 4096
read(4, "\tmodule\t\tcost\nalias\tISO-IR-8-1//"..., 4096) = 4096
read(4, "IBM1156\t\t1\n\n#\tfrom\t\t\tto\t\t\tmodule"..., 4096) = 4096
read(4, "\t\tIBM1166//\nalias\tCP1166//\t\tIBM1"..., 4096) = 4096
read(4, "alias\tROMAN9//\t\tHP-ROMAN9//\nalia"..., 4096) = 726
read(4, "", 4096) = 0
close(4) = 0
getdents64(3, 0x55d994394500 /
0 entries */, 32768) = 0
close(3) = 0
futex(0x7fa79dd4098c, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(2, "ln: ", 4ln: ) = 4
write(2, "\346\227\240\346\263\225\345\210\233\345\273\272\347\241\254\351\223\276\346\216\245 'bbb.txt' "..., 49无法创建硬链接 'bbb.txt' => '../a/123.txt') = 49
openat(AT_FDCWD, "/usr/share/locale/zh_CN.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/usr/share/locale/zh_CN.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/usr/share/locale/zh_CN/LC_MESSAGES/libc.mo", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=129064, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 129064, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa79dd60000
close(3) = 0
write(2, ": \346\262\241\346\234\211\351\202\243\344\270\252\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225", 29: 没有那个文件或目录) = 29
write(2, "\n", 1
) = 1
lseek(0, 0, SEEK_CUR) = -1 ESPIPE (非法 seek 操作)
close(0) = 0
close(1) = 0
close(2) = 0
exit_group(1) = ?
+++ exited with 1 +++

I did some investigation and the problem seems to be with linkat() called by the kernel (i.e., linkat(AT_FDCWD, "../a/123.txt", AT_FDCWD, "bbb.txt", 0) = -1 ENOENT (没有那个文件或目录) in the above strace log). In the linux implementation of linkat (https://elixir.bootlin.com/linux/v6.2-rc3/source/fs/namei.c#L4553), it calls filename_lookup on target file and filename_create on the link file. Both filename_lookup and filename_create call lookup, but they are with different flags (filename_create has an additional flag LOOKUP_CREATE). Is it possible that they are calling lookup with different flags but fuse3 cannot receive that?

FWIW link works for me.

FWIW link works for me.

I think the problem should occur in the Filesystem implement, as @KongzhangHao says maybe the lookup implement should check the flags to distinguish the normal lookup and the link lookup?

EDIT:
https://pkg.go.dev/bazil.org/fuse#LookupRequest i read other fuse library, their lookup only have the name argument, no flags argument

It seems that the lookup function in the FileSystem trait doesn't have flag in its interface. Is it because the fuse3 binary doesn't support flags in its lookup interface? If so then it might be a problem with fuse3 binary?

What binary? This crate does not involve any such thing. There isn't any flags field because the FUSE protocol does not include any such field in the FUSE_LOOKUP operation. The only thing that the fuse server gets during a lookup is the parent's node id and the file name.

Yeah that's fair enough. I was thinking of the fusermount binary in Linux.

in the example memfs, the reason for the hard link error is the nlink filed is incorrect which return by lookup implement,
I modify the memfs example #61 and test success on my computer
@KongzhangHao could you check your Filesystem, is the nlink correct?

I fixed the nlink and it is working perfectly. Thanks for your help!