zargony / fuse-rs

Rust library for filesystems in userspace (FUSE)
MIT License
1.05k stars 131 forks source link

Truncate? #145

Closed delehef closed 4 years ago

delehef commented 4 years ago

I'm currently using fuse-rs (0.4.0-dev) to implement a rather trivial FS. Read-only is working as a charm, but I'm tripping over an issue if I make my virtual files writeable.

When editing then saving one with vim for instance, appending works perfectly fine: vim's call to write asks for a write to a buffer that runs after the end of the file, I extend it and write, done. But when removing stuff however, vim (according to strace) first calls a ftruncate on the file, to which fuse-rs returns an ENOSYS/not implemented, then rewrite the whole content. But as I can't know which parts of it were removed, I can't fit the file to the right size, and thus I end up with a file with overflow garbage at the end.

In another example, when doing the same with strace + vi, instead of doing a ftruncate + write, it calls open with the O_TRUNC flag. According to there (end of the page), FUSE kernel module automatically calls a truncate, that is still ENOSYS, and thus vi's save fail with a Function not implemented.

As mentioned in the previous link, I also tried to pass -o atomic_o_trunc flag to fuse, but if the other ones (-o autounmount and -o nonempty) works perfectly fine, this one seems to be ignored (strace shows the same calls to ftruncate).

So in the end, am I making a stupid mistake I can't find somewhere, or maybe is there a reason I missed for which truncate is not implemented?

wfraser commented 4 years ago

truncate is one of the many things that FUSE sends to the filesystem as part of the setattr syscall. If you get a setattr and the size argument is not None, that's a truncate call.

delehef commented 4 years ago

And here I was, naively thinking that syscalls and FUSE operations mapped one-to-one...

Thanks a lot, and sorry for the bother.

wfraser commented 4 years ago

No worries, it's certainly not very unintuitive.

As an aside, I have a crate, fuse-mt which is a wrapper around this one and which tries to make FUSE filesystems a bit easier to implement, among other things by splitting the getattr function into several which match the original syscalls better. See https://github.com/wfraser/fuse-mt/blob/f81f0eb77f1f5f5ebd6de819f5a1a79e7089b3e3/src/fusemt.rs#L142 where it does this. It also adds inode->path translation and some multi-threading around read/write as well. Just an alternative to consider.

delehef commented 4 years ago

The MT would prove very useful in my particular case; and the path-only system is the icing on the cake. I'll probably switch to it as soon as fuse-rs 0.4 is published and fuse-mt switched to it, thanks for the head-up!