JordanMilne / ShenanigaNFS

Python library for making somewhat conformant-ish low-level NFSv2/3 clients and servers
MIT License
7 stars 2 forks source link

New macOS refuses to mount without -o nolock #1

Closed jaraco closed 1 year ago

jaraco commented 1 year ago

I know it should be obvious, but when I tried, I failed.

I ran the example server, then tried mounting it using macOS Finder.

Are there known limitations (like it runs on Linux only)? Can you share some step-by-step instructions that demonstrate how one could utilize the server? Next, I'll probably try mounting in Linux instead of macOS, so may be able to answer the question myself, but any advice would be appreciated.

JordanMilne commented 1 year ago

Sure thing! I've not tried running the server itself on Mac OS, but IIRC when I was testing mounting from Mac OS I used something to the effect of sudo mount -t nfs server_ip:/mount_path /Users/user/some_mount_point, I didn't use Finder.

I don't think there's anything in there that should preclude it from working on Mac OS, I'll take a look today.

JordanMilne commented 1 year ago

Yep, there looks to be an issue binding UDP sockets under Mac OS, thanks for raising this!

Since RPCBind / PortMapper run on port 111 by convention, you can verify that they're not correctly bound by running lsof -iUDP -Pn | grep 111. Similarly, when you run one of the NFS examples, $ showmount 127.0.0.1 -e should return something like

Export list for 127.0.0.1:
/tmp/nfs2 *

but just times out under Mac OS, due to the server port not being bound.

I'll take a look at this, my guess is it's some weirdo asyncio thing with how I'm binding the UDP server socket.

rgmisra commented 1 year ago

I was able to run the example server and mount it on my macOS Sonoma system using: mount_nfs -o nolock localhost:/tmp/nfs2 ~/mnt

JordanMilne commented 1 year ago

Hmmm, that makes sense, thanks! I didn't implement any of the NFS 3 locking service because I largely use per-mount filesystems, that might be forcing it down the UDP path on new macOS. I'll see if fixing UDP + implementing a stub lock service deals with the issue.

JordanMilne commented 1 year ago

@rgmisra Your pointer was really helpful, looks like this was actually due to a new check in XNU's NFS code that checks if statd is provided by PortMapper before allowing the mount if locks were enabled (the default.) https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/nfs/nfs_vfsops.c#L4622-L4655 . It doesn't actually call statd, it just checks if it's there, I can't see the exact rational since I don't have access to the referenced ticket.

@jaraco Could you check if the current HEAD works for you without extra options? It looks like XNU will automatically disable locking mode when it notices that NLM isn't provided later on, and everything seems to work on my laptop, but I'm not entirely sure.

jaraco commented 1 year ago

It's working great! I launched the daemon:

 ShenanigaNFS master @ pip-run . -- examples/nfsserverexample.py
Mounted '127.0.0.1': 926155170949658851, b'/tmp/nfs2'
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x104c8b080>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'._.')))
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x104c8b080>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'DCIM')))
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x1047c4140>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'.metadata_never_index_unless_rootfs')))
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x104c8b080>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'.metadata_never_index')))
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x1047c4140>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'.metadata_direct_scope_only')))
2  <function NFSV3Service.LOOKUP at 0x105682340> (<shenaniganfs.nfs3.NFSV3Service object at 0x105685c10>, <shenaniganfs.transport.CallContext object at 0x104c8b080>, LOOKUP3Args(what=DiropArgs3(dir_handle=b'Q\xa8\xbe\xff\xe88\x06\t\xe9\x95\xa8\xeaN\xf7\x10f\xac@\x87\xba\xca\xd5\x8c\xb0NM@\t\x13\x84\xc9U\x00\x00\x00\x00\x00\x00\x00\x02\x0c\xda]8\x93Xx\xe3', name=b'.Spotlight-V100')))

And mounted the example:

 ShenanigaNFS master @ mkdir /tmp/test
 ShenanigaNFS master @ sudo mount -t nfs localhost:/tmp/nfs2 /tmp/test
Password: 
 ShenanigaNFS master @ ls /tmp/test
testfile.txt
 ShenanigaNFS master @ cat /tmp/test/testfile.txt
test

Thanks for investigating and making the fix!