Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac updates #384

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open

Mac updates #384

wants to merge 15 commits into from

Conversation

ahknight
Copy link

@ahknight ahknight commented Apr 4, 2017

Updates and fixes for macOS #115

@trapexit
Copy link
Owner

trapexit commented Apr 4, 2017

Thanks. Not sure if I'll refactor first and repush or accept and refactor after but this helps a lot. I should probably setup travis-ci to ensure in the future things build.

@ahknight
Copy link
Author

ahknight commented Apr 4, 2017

I'd advise updating it on this branch before merge. One thing I'm noticing with the changes I made is that file creation is broken due to a failure to resolve a file descriptor. I'll keep looking at it, though.

Is there some kind of test suite you use?

@trapexit
Copy link
Owner

trapexit commented Apr 4, 2017

Unfortunately not. It's been a TODO for quite a while. It's not all that easy given dealing with the filesystem is nothing but side effects and relying on state held by the fuse library and OS. Would need to mock a lot of things.

I have just added basic Travis-CI integration but that'd only confirm building works right now. (Which for OSX it doesn't in the main branch but if you rebase to master it'll build for you.)

@ahknight
Copy link
Author

ahknight commented Apr 4, 2017

Found the source of the descriptor problem. OS X doesn't like AT_FDCWD in fcntl, but has no problem with opendir(".")/dirfd() to get the same value. ಠ_ಠ

I'll poke around some more.

As the position argument isn’t carried all the way down resource forks won’t work properly (though it easily could be, that would change the internal API for everyone).
OSX doesn’t like AT_FDCWD in it’s fcntl, but getcwd does the job.
Also fixed an issue with absolute paths in the process.
After running Clang’s analyzer on it there were quite a few places where there were mis-matched store sizes and signs.  I updated them so that the underlying code more closely-matches the OS APIs and the type change happens at the FUSE connection.

In this way, if FUSE ever updates the API to actually match the APIs of modern OSes, it’s a fix in one place.

Also, I updated read/write to return the total read/write value the OS returned rather than blindly return the request count.
Finder copies are broken without this argument, even if it’s fixed to 0. Upside: it reduces the number of times we platform-check in the xattr code.
@ahknight
Copy link
Author

ahknight commented Apr 5, 2017

Fixed the issues with the missing API (wrote a version of it) and fixed xattrs and Finder copies as well.

A side effect is that the xattr APIs in mergerfs now have a position argument to them, but it's immediately defaulted to zero for non-Apple platforms and omitted in the final call to the OS.

Every other solution I looked at just filled the code with tons of ifdefs and/or foo_osx.icpp files for very small API changes.

@trapexit
Copy link
Owner

trapexit commented Apr 5, 2017

Oops. I've been refactoring your last commit into a more abstract interface and aligned to the overall style/layout. Let me know when you're finished and I'll tweak it if necessary.

If you'd like you can give me access to the PR.

}

return count;
return nwritten;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't make a difference so long as count > 0 and it's a successful write/s but I see now that if count == 0 then it won't behave correctly even with your change. I'll need to fix that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't, no. But while making hfsinspect I ran into this issue a few times. There was some kind of situation where the read returned early but I wasn't at the end of the file/device yet and the next read kept going. So, since then, I've just always returned the real count so that client apps' counters are accurate and can handle that.

Copy link
Owner

@trapexit trapexit Apr 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that for the outer function (copyfile_rw) but for writen the only possible bug should be that it doesn't accept zero for byte length. writen should not return till all bytes passed in are written or an error.

I think the issue is actually in copyfile_rw in which a zero return from read isn't handled properly. Should exit the loop. This is a generic bug. I'll probably fix that separately.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think of it as a data pipe. The OS gives us a total of the bytes written and then we hand that back to FUSE to deliver to the application. If anything goes wrong along the way then returning a fixed value might lead to odd things, especially for a partial write (it's not a fail because data changed, but it's not a success because the whole request wasn't completed). That'd be my only concern.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has nothing to do with FUSE. It's for copying data between to underlying drives.

A write, according to standards, does not return 0 unless it's asked to write 0. It can return from 1 to count or -1. That's why writen exists. To make sure that the whole buffer is written.

The problem is the outer function. It should exit on EOF but write should never return anything but 1 - bufsize or -1. And writen should manage being passed a zero bufsize.

@ahknight
Copy link
Author

ahknight commented Apr 6, 2017

The box to allow edits is checked, so feel free to add on here.

@ahknight
Copy link
Author

ahknight commented Apr 6, 2017

The current version should be good. I've tested it with "rsync -aHAX" and with Finder copies and it checks out (I even mixed HFS+ and APFS-CI backing drives).

When you have it looking how you want it, let me know and I'll run those tests again.

@trapexit
Copy link
Owner

trapexit commented Apr 6, 2017

Sure. Thanks for the effort. My one concern though is the copying of xattrs. You've hardcoded XATTR_SHOWCOMPRESSION and while I'm not positive yet... from my random readings it appears that you'd need to do with and without it to get the compressed resource forks and more traditional xattrs.

Right now I'm kindof leaning toward having xattr disabled on OSX for a first patch and focus on getting it right later.

@@ -64,6 +64,7 @@ namespace fs
const char *path,
const struct timeval times[2])
{
int rv;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm watching Travis. If I commit something which breaks I'll fix it.

@ahknight
Copy link
Author

ahknight commented Apr 6, 2017

For the xattrs, the Finder uses them a lot for normal operation on the files (things like labels, and display preferences) and other apps keep metadata in them on a semi-regular basis. Not having them limits the usefulness of the resultant mount. :\

The different API with FUSE and the OS is a small pain, but I don't think there's a clean way around having this FS be a superset of functionality in order to pass the right data down.

You're right about the compression flag; that was in there for a test and I missed it. The consumer of the API will pass that if it's expected and there will be side effects if it's there without their knowing/expecting.

Clients should get the xattrs they actually asked for, after all.
@trapexit
Copy link
Owner

trapexit commented Apr 6, 2017

There are two xattr uses. One is the FUSE interface. The other is the clonepath and clonefile components. Until the clone'ing usage has to be figured out else the copy will be incomplete.

@ahknight
Copy link
Author

ahknight commented Apr 6, 2017

For OSX, if you're copying a file from one backing store to another, use copyfile(3) with COPYFILE_ALL. The OS will handle all the details for you, including xattrs, resource forks, Finder info, etc.

https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/copyfile.3.html

@trapexit
Copy link
Owner

trapexit commented Apr 6, 2017

Then all fs::xattr stuff used by copyfile aren't needed for Mac. I'll take a look. Thanks.

@trapexit
Copy link
Owner

trapexit commented Apr 6, 2017

There isn't a clear separation in these commits. The type stuff, bug fixes, and osx changes.

I'm going to go through it all and pick out the general fixes and abstractions and put them in the main branch first. Leave out the type stuff (what did you run to discover them? clang's c++ analyzer isn't reporting anything.). Then rebase this branch and focus solely on osx changes.

Copy link

@briankendall briankendall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a potential bug

return (errno=ENOTSUP,-1);
#endif
#elif __APPLE__
return ::setxattr(path.c_str(),name,value,size,position,flags & XATTR_NOFOLLOW);
Copy link

@briankendall briankendall Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't flags & XATTR_NOFOLLOW actually be flags | XATTR_NOFOLLOW? I think using bitwise AND means that it will always be 0 as flags will never have the XATTR_NOFOLLOW bit set, effectively discarding the value of flags.

There's a few other similar places where bitwise AND is used where it probably should be a OR.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Should be ORed.

@briankendall
Copy link

Any update on this PR? I'm quite interested in trying this out in macOS. If not, is there a particular commit in the PR that I could compile and try out, like the most recent one?

@trapexit
Copy link
Owner

This PR was incomplete and the code has changed quite a bit since it was created. libfuse on OSX is different from Linux (and other platforms) and since I embedded libfuse into mergerfs to simplify installation and normalize behavior its now more complicated to port.

I now have access to an OSX machine but I'm not sure when I'll have time to work on working on a port.

@briankendall
Copy link

Would it be possible or at all advisable for me to try compiling using the latest commit in this PR's branch?

@trapexit
Copy link
Owner

You can obviously do whatever you like but as I recall this PR was incomplete and IIRC had some bugs.

@briankendall
Copy link

Just checking in again to see if there's any progress on this. @ahknight is this still something you're working on?

@trapexit
Copy link
Owner

Sorry, I've not had the time to look into this.

@briankendall
Copy link

briankendall commented Feb 27, 2020

I have a renewed interest in trying to get mergerfs working in macOS and was thinking of seeing if I could port it.

I was going through the up-to-date codebase to try and get a sense of what's involved. The way certain low level functions are factored out makes this easier in comparison to when @ahknight was working on it.

But I can see how it's more difficult now that libfuse is included in the project. There's a few places where you're reaching into fuse's internal structures (e.g. fuse_config_set_attr_timeout and fuse_config_get_attr_timeout) and of course those functions aren't available in osxfuse. And osxfuse differs in a few places compared to libfuse; namely there are some constants mergerfs refers to that are missing from osxfuse, and osxfuse is missing some member variables such as fuse_file_info.auto_cache.

If it's not too much trouble, could you share any thoughts on how to proceed?

@trapexit
Copy link
Owner

trapexit commented Feb 27, 2020

It's going to be a lot of trouble. Unless OSXFuse is very similar in protocol... you aren't going to be able to just use the high level libfuse API. And my intent is to further hack away at libfuse. If the protocol is similar enough then it might be doable but it's not going to get more API friendly anytime soon. Even if I hadn't vendored libfuse I had intended to move to the low level API which isn't cross platform.

Not knowing anything about osxfuse I really can't comment further.

@briankendall
Copy link

Ah, I see now that some of the differences I pointed out are in fact significant changes you've made to libfuse, and so in order to port to mac it's necessary to make equivalent changes to osxfuse. That's certainly more work than I'm willing to take on right now.

It does look like osxfuse includes the low-level API, so if and when you transition mergerfs to that then maybe it'd be worth looking into a mac port again. I'd certainly love to see it on the mac platform as this seems to be the most full featured union filesystem currently available.

@trapexit
Copy link
Owner

It's a big change to move to the low level API. My plan is to slowly take out or collapse the abstractions in libfuse to get it down to the LLAPI or maybe even replace libfuse all together. That's why I'm mentioning the protocol and setup. As I understand osxfuse is a fork of the original libfuse so I wouldn't think the protocol has changed too much. Maybe those changes could be incorporated into mergerfs. I should probably just take a look at the osxfuse code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants