-
Kirill Smelkov authored
Since https://git.kernel.org/linus/7678ac5061 FUSE filesystems were allowed to return ENOSYS from open and, open seeing this, the kernel will not send any open request anymore and just use Fh=0 for all opened files. This is handy for filesystems that don't have any per-opened-file state. However if there is at least one node for which opened files should have their own state, it is not correct to return ENOSYS from open _ever_, as that would prevent the kernel to call open on all nodes - in particular on the node where open needs to create a state associated with the file handle(*). It is already explicitly documented that Node.Open can return File=nil in which case filesystem operations like Read and Write will be called on the node directly. This way a filesystem could try to simulate an Open that was returning ENOSYS with Open that returns File=nil. However it is not the same as ENOSYS open also prevents the kernel from dropping the file cache: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/fuse/file.c?id=v5.0-rc3-241-g7c2614bf7a1f#n131 From this point of view Opens that were returning ENOSYS should be replaced with Open that returns WithFlags{File=nil, flags=FOPEN_KEEP_CACHE}. Unfortunately if one tries to do that, go-fuse segfaults as the test included in this patch demonstrates: 13:36:53.238706 rx 13: LOOKUP i1 ["world.txt"] 10b 13:36:53.238745 tx 13: OK, {i4 g3 tE=1s tA=1s {M0100644 SZ=5 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}} 13:36:53.238780 rx 14: OPEN i4 {O_RDONLY,0x8000} 13:36:53.238791 tx 14: OK, {Fh 2 CACHE} <-- NOTE Fh != 0 13:36:53.238706 rx 12: RELEASE i3 {Fh 0 NONBLOCK,0x8000 L0} 13:36:53.238804 tx 12: OK 13:36:53.238823 rx 15: READ i4 {Fh 2 [0 +4096) L 0 NONBLOCK,0x8000} 13:36:53.238830 tx 15: OK, 5b data "world" 13:36:53.238846 rx 16: GETATTR i4 {Fh 2} 13:36:53.238865 tx 16: OK, {tA=1s {M0100644 SZ=5 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}} 13:36:53.238879 rx 17: FLUSH i4 {Fh 2} panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x539f9f] goroutine 6 [running]: github.com/hanwen/go-fuse/fuse/nodefs.(*rawBridge).Flush(0xc000010960, 0xc000104180, 0x0) /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/nodefs/fsops.go:490 +0x6f github.com/hanwen/go-fuse/fuse.doFlush(0xc0000de000, 0xc000104000) /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/opcode.go:373 +0x42 github.com/hanwen/go-fuse/fuse.(*Server).handleRequest(0xc0000de000, 0xc000104000, 0xc000104000) /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/server.go:431 +0x26b github.com/hanwen/go-fuse/fuse.(*Server).loop(0xc0000de000, 0x0) /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/server.go:403 +0x18f github.com/hanwen/go-fuse/fuse.(*Server).Serve(0xc0000de000) /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/server.go:331 +0x6d created by github.com/hanwen/go-fuse/fuse/test.TestNoFile /home/kirr/src/neo/src/github.com/hanwen/go-fuse/fuse/test/nofile_test.go:73 +0x442 Fix it by teaching registerFileHandle not to register any handle at all and return Fh=0, if the inner file (file itself, or the file that was wrapped with WithFlags) is nil. After the patch the trace for world.txt open/read (that is opened with WithFlags{File=nil, Flags=FOPEN_KEEP_CACHE} is as follows. 14:59:31.714048 rx 13: LOOKUP i1 ["world.txt"] 10b 14:59:31.714062 tx 13: OK, {i4 g3 tE=1s tA=1s {M0100644 SZ=5 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}} 14:59:31.714081 rx 14: OPEN i4 {O_RDONLY,0x8000} 14:59:31.714091 tx 14: OK, {Fh 0 CACHE} <-- NOTE Fh = 0 14:59:31.714123 rx 15: READ i4 {Fh 0 [0 +4096) L 0 NONBLOCK,0x8000} 14:59:31.714132 tx 15: OK, 5b data "world" 14:59:31.714150 rx 16: GETATTR i4 {Fh 0} 14:59:31.714159 tx 16: OK, {tA=1s {M0100644 SZ=5 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}} 14:59:31.714181 rx 17: FLUSH i4 {Fh 0} 14:59:31.714186 tx 17: OK 14:59:31.714202 rx 18: RELEASE i4 {Fh 0 NONBLOCK,0x8000 L0} 14:59:31.714208 tx 18: OK (*) my particular case is filesystem where many nodes are just data, but additionally there are files that behave like sockets - for every client who opens such file the filesystem establishes separate bidirectional channel for control exchange via that stream: https://lab.nexedi.com/kirr/wendelin.core/blob/a50da567fd/wcfs/misc.go#L205
402331d4