jacobsa / fuse

A Go package for implementing a FUSE file system.
Apache License 2.0
487 stars 106 forks source link

WriteFile does not work in memfs as it is invoked py PID 0 #118

Closed JakWai01 closed 2 years ago

JakWai01 commented 2 years ago

Problem

In my case, the WriteFile function in the memfs example does not work as the PID check triggers and fuse.EINVAL is returned. This is because apparently WriteFile is only invoked by PID 0. If I remove this condition, everything works appropriately. I do not really understand what causes this issue and was wondering if anybody else can reproduce it. If so, i can submit a PR fixing the issue by removing the PID check for this function.

To reproduce this issue, running the memfs example after wrapping the PID check in the WriteFile function with some print statements:

func (fs *memFS) WriteFile(
    ctx context.Context,
    op *fuseops.WriteFileOp) error {

    fmt.Println("Before")
    if op.OpContext.Pid == 0 {
        return fuse.EINVAL
    }
    fmt.Println("After")
        ...

If you can see "Before" but not "After" after writing to a file, this issue affects you as well.

Thanks for your work!

Jakob

stapelberg commented 2 years ago

[…] was wondering if anybody else can reproduce it.

I just tried reproducing the issue, but for me, the memfs test passes on Fedora 35:

--- PASS: TestMemFS (6.25s)

I wonder what’s different on your system

JakWai01 commented 2 years ago

Thanks for investigating! I also had this issue on Fedora 35 first. I now switched to Ubuntu 21.10 and am still experiencing the same issue. The test actually work for me as well. But as soon as I use it "manually" I can't write files. I created a repository to reproduce this issue by just providing a main.go and using memfs.go and inode.go from the jacobsa/fuse repository (https://github.com/JakWai01/sile-fystem).

When running said main.go using go run main.go --mountpoint "test", I can't write to files in the "test" directory. Of course, the directory test needs to be created in the repo first. The directory can be unmounted later by using sudo umount -lf test.

When using Gedit I get: Unexpected error: Error writing to file: Invalid argument and when using vim I get: "test" E667: Fsync failed WARNING: Original file may be lost or damaged don't quit the editor until the file is successfully written!.

As soon as I remove the PID-Check this issue is then resolved for me.

stapelberg commented 2 years ago

Thanks for the steps to reproduce.

I checked out your https://github.com/JakWai01/sile-fystem repository at git revision 6ea23e4727308ea00e21cbc929ce71be094ca1f2.

I used go run main.go memfs --mp test to mount the file system.

This is the log output I see when writing a file, where pid is indeed set to 0:

[…]
{"time":1641764410,"level":"DEBUG","event":"FUSE.WriteFile","data":[{"handle":0,"inode":2120192514,"offset":0,"opContext":{"Pid":0}}]}
[…]

Looking at the kernel source code, the uid, gid and pid fields are always sent, except if nocreds is set. Per https://elixir.bootlin.com/linux/v5.15.13/C/ident/nocreds, nocreds is checked in a few places, but set only for RELEASE, DESTROY and INIT requests, and the only place where it’s set for WRITE requests is https://elixir.bootlin.com/linux/v5.15.13/source/fs/fuse/file.c#L1670

This function is only called from the page cache writeback code path, where the association with an individual pid has been lost.


Indeed, as soon as I add DisableWritebackCaching: true to the &fuse.MountConfig{} literal in mount_memfs.go, I get pids from the Linux kernel with my writes:

[…]
{"time":1641767910,"level":"DEBUG","event":"FUSE.WriteFile","data":[{"handle":0,"inode":2749445729,"offset":0,"opContext":{"Pid":24391}}]}                                                                             
[…]

Looking back at memfs, I can see the following lines in memfs_test.go, which explains why the memfs test works, but not the memfs sample program itself: https://github.com/jacobsa/fuse/blob/1b9b09fd17a4bbee89a506fdccefbd9d1d414a0c/samples/memfs/memfs_test.go#L100-L101


So, in summary:

  1. It’s normal/expected that when writeback caching is enabled, the Linux kernel won’t send pids with fuse WRITE messages.
  2. memfs_test.go disables writeback caching, but that should be moved to memfs itself instead.
  3. In your own file system, if you need the uid/gid/pid fields of processes for writes, disable writeback caching. If writeback caching is desired, remove the check instead.
JakWai01 commented 2 years ago

Thanks for the detailed explanation. Should I work on moving the writeback caching to memfs?

stapelberg commented 2 years ago

Yes, if you could send a PR to move the DisableWritebackCaching into memfs itself, that’d be great!