On Sun, Nov 04, 2007 at 09:19:13PM -0000, Amit Singh wrote:
> Ummm... no, not a bug.
> 
> Moreover, it's not just with "defer_permissions" that you should see
> expected behavior. You should also see it with "default_permissions".
> You should also see expected behavior if you *do NOT* implement the
> access() method in your user-space file system.
> 
> As for how this works as you expect on Linux, well, as I keep saying:
> 
> 1. Mac OS X != Linux
> 2. Mac VFS != Linux VFS
> 3. MacFUSE != Linux FUSE

I agree with all of these. My understanding was that MacFUSE and Linux
FUSE were kernel modules that communicate with the FUSE library,
and that the FUSE library, and the daemons it calls into, are supposed to
work the same on both operating systems. But it seems I am wrong here.
The user level interfaces in MacFUSE and Linux FUSE are somewhat similar,
but they are not supposed to be the same.


> The issue is with involving access(2), which returns ELOOP when used
> on a self referencing symbolic link, even for F_OK.
> 
> The Mac OS X VFS has more knobs than Linux in terms of involving a
> file system in decision making. MacFUSE exposes several of these knobs
> to user space. Now, if a user-space file system implements the
> access() method, MacFUSE will plumb things such that the vnode-level
> access() call will result in calls to your file system's access(). In
> your example, when you want to unlink(2), the VFS wants to authorize
> that action, which eventually results in a call to your file system's
> access(), which in turn (I assume) uses access(2), and gets the ELOOP
> error.
> 
> Why doesn't it happen with HFS+? Because HFS+ doesn't implement its
> own vnode-level access()--it relies on the kernel to make such
> decisions. This is fine because the user/group/permission information
> returned by HFS+ is "reliable".
> 
> Why doesn't it happen with sshfs? Because sshfs doesn't implement an
> access() method.
> 
> Why does it happen with fusexmp_fh on Mac OS X? Because fusexmp_fh's
> access() uses access(2) and simply returns the result. If you see the
> implementation of xmp_access() in MacFUSE code (fusexmp_fh.c), I
> special case ENOENT for the case of a symlink pointing to a
> nonexistent target. I can similarly special case for ELOOP, and then
> fusexmp_fh should work too.

I am not quite convinced that macfuse should call fuse_access() to
determine whether it can unlink a file, for a few reasons.

1. fuse_access()/access(2) follow symbolic links. Thus there is not a way
to test the permission to modify a symbolic link file.

2. It seems that a fuse daemon's fuse_access() response should be a function
of whether the macfuse kernel module is doing permission checking or
whether a user process called access(2). For example, whether to try to
follow a symlink, or effective vs. real permissions. Can the daemon detect
this difference?

3. A separate permission check and action can be a security hole if the
underlying data may change in-between the two operations. Certainly some
file systems access concurrently accessed data (e.g, sshfs and xmp).
Another possibility, can MacFUSE intermix kernel calls into the fuse daemon?

What is incorrect with this logic? (thanks!)


Fwiw, I am not sure what "reliable" means here. Are ACLs "reliable" and
does a fuse file system implement ACLs checking through fuse_access()?


I do think fusexmp's behavior is incorrect, regardless of whether it is
a macfuse or macfuse fusemp issue. Given that it is the example file system,
I think making it correct would be great! Related, but more generally,
the fewer the special cases that need be implemented in each macfuse file
system, perhaps the better?


> Why doesn't it happen with defer_permissions or default_permissions?
> Because these options configure MacFUSE to not involve the user-space
> file system's access() method. default_permissions is very much like
> the HFS+ case.
> 
> The bottom line is: if your user-space file system provides an
> access() method, it means it doesn't trust the kernel to make
> authorization decisions based on file metadata (user/group/permissions
> etc.)--it wants to be involved in such decisions. The onus is then
> upon you to handle the ELOOP case for F_OK (I think special casing it
> in the kernel is ugly and unnecessary.)

I am not familiar enough with the kernel side to comment. But, as I asked
above, is it true that access() is the right way to check permissions?


> Why doesn't it happen on Linux? Because the unlink(2) code path on
> Linux doesn't have a precursor call to access().

I would enjoy reading why MacFUSE and Linux FUSE differ in this way.


> Try something like the following in your access() implementation (not
> perfect--tweak to taste):
> 
> static int
> whatever_access(const char *path, int mask)
> {
>     int res;
> 
>     res = access(path, mask);
>     if (res == -1) {
> #if (__FreeBSD__ >= 10)
>         if ((errno == ENOENT) || (error == ELOOP)) {
>             int next_res;
>             int saved_error = error;
>             struct stat sb;
>             next_res = lstat(path, &sb);
>             if (next_res == 0 && S_ISLNK(sb.st_mode) &&
>                 ((sb.st_uid == getuid()) || (geteuid() == 0))) {
>                 /*
>                  * The target of the symlink doesn't exist, but the
>                  * symlink itself does. Or it points to itself.
>                  *
>                  * A lame cheap fix here.
>                  */
>                 return 0;
>             }
>             return -saved_error;
>         }
> #endif
>         return -errno;
>     }  /* if (res == -1) */
> 
>     return 0;
> }

I may need to think more about this to fully understand its interactions.
But, access(foo, F_OK) when:
- foo->foo returns ELOOP on MacOSX HFS+, but this returns 0
- foo->does_not_exist returns ENOENT on MacOSX HFS+, but this returns 0
Unless I am mistaken? I did not test the above code, just MacOSX HFS+.

And there are other, not as relevant subtleties, like the sb.st_uid check
(it is insufficient, and I believe ignores effective vs real permission
differences for access(2) vs. operation permissions). I mention this point
just to note that getting this code correct is kind of hard. So there's a
decent chance that some fuse daemons will get it wrong.


thanks, Amit!
-- 
Chris Frost  |  <http://www.frostnet.net/chris/>
-------------+----------------------------------
PGP: <http://www.frostnet.net/chris/pgpkey.txt>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"macfuse-devel" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/macfuse-devel?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to