Sherlock-Holo / fuse3

an async version fuse library for rust
MIT License
83 stars 17 forks source link

Problem with hard link support #59

Closed KongzhangHao closed 1 year ago

KongzhangHao commented 1 year ago

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.

Sherlock-Holo commented 1 year ago

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 archprctl(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=81921024, 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 +++

KongzhangHao commented 1 year ago

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?

asomers commented 1 year ago

FWIW link works for me.

Sherlock-Holo commented 1 year ago

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

KongzhangHao commented 1 year ago

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?

asomers commented 1 year ago

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.

KongzhangHao commented 1 year ago

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

Sherlock-Holo commented 1 year ago

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 https://github.com/Sherlock-Holo/fuse3/pull/61 and test success on my computer @KongzhangHao could you check your Filesystem, is the nlink correct?

KongzhangHao commented 1 year ago

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