NFSv4 Quirks
I didnt see and issue for this so I figured I'd open one. NFSv4 support is currently, to my understanding, in the lower level API. This is a feature request to finish is to the level that v3 is supported. This is currently a blocker for GVFS, and therefore the Nautilus file manager, to have NFSv4 support.
See also
https://gitlab.gnome.org/GNOME/gvfs/issues/18
NFSv3 is the default but you can select v4 two different ways.
1, If you pass a URL to libnfs, then pass ?version=4 as a URL argument
2, Programatically, call nfs_set_version(nfs, NFS_V4); before you connect to the server.
On Mon, Sep 3, 2018 at 4:22 AM, Max Ehrlich [email protected] wrote:
I didnt see and issue for this so I figured I'd open one. NFSv4 support is currently, to my understanding, in the lower level API. This is a feature request to finish is to the level that v3 is supported. This is currently a blocker for GVFS, and therefore the Nautilus file manager, to have NFSv4 support.
See also
https://gitlab.gnome.org/GNOME/gvfs/issues/18
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkLHcPiFxab2ctxdqcaQIwJZj6Innks5uXCHTgaJpZM4WWtdR .
Right so the problem that GVFS (and therefore all the way up to nautilus) has is that they use NFSv3 through a higher level API which they claim is not available for v4. Unless you're saying that v3 and v4 have the same level of support now in which I should probably let them know. The language they used was the " asynchronous high-level API".
The async high level api works for nfs v4 too.
Just call nfs_set_version(nfs, NFS_V4) before connecting to the server and then the high-level API will use nfs v4.
If there is something specific missing from the high level api when used with nfsv4, please open a bug and I will add the missing pieces.
On Mon, Sep 3, 2018 at 7:08 AM, Max Ehrlich [email protected] wrote:
Right so the problem that GVFS (and therefore all the way up to nautilus) has is that they use NFSv3 through a higher level API which they claim is not available for v4. Unless you're saying that v3 and v4 have the same level of support now in which I should probably let them know. The language they used was the " asynchronous high-level API".
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265#issuecomment-417960021, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkDgk2qy9aeZnFnWEKTr6ougVUWCoks5uXEi8gaJpZM4WWtdR .
Ok great let me loop back with the GVFS guys and see what they say
I've been convinced that only the low-level API is supported for NFSv4 as per the following statements: https://github.com/sahlberg/libnfs/blob/master/README#L26 https://github.com/sahlberg/libnfs/blob/master/README#L69
@sahlberg Can you please update the README file to be obvious that NFSv4 is fully supported?
Sorry about that mate. I am really useless sometimes getting things updated everywhere they should be updated.
I have updated README to just state v3 is default but v4 can be selected.
On Tue, Sep 4, 2018 at 4:38 PM, Ondrej Holy [email protected] wrote:
I've been convinced that only the low-level API is supported for NFSv4 as per the following statements: https://github.com/sahlberg/libnfs/blob/master/README#L26 https://github.com/sahlberg/libnfs/blob/master/README#L69
@Queuecumber https://github.com/Queuecumber Can you please update the README file to be obvious that NFSv4 is fully supported?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265#issuecomment-418257076, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkI8HWI1N7d-bxZ0xpxO4fKdrFz9Wks5uXh_WgaJpZM4WWtdR .
Great, thanks.
Don't you plan to add some kind of protocol version negotiation? Similarly to what happens if nfsvers is not specified?
If this option is not specified, the client negotiates a suitable version with the server, trying version 4 first, version 3 second, and version 2 last.
https://linux.die.net/man/5/nfs
@sahlberg Trying to add in support now and one thing that I'm running into is that the first thing GVFS does is run
export_list = mount_getexports (host);
to see if the directory a user wants to mount is valid. That function doesnt take a context, does it work with v4?
On Thu, Sep 6, 2018 at 7:25 AM, Max Ehrlich [email protected] wrote:
@sahlberg https://github.com/sahlberg Trying to add in support now and one thing that I'm running into is that the first thing GVFS does is run
export_list = mount_getexports (host);
Don't use this function for NFSv4. In NFSv4 you don't really mount a specific share/export on the client anymore and in fact NFSv4 does not even have methods to collect the list of shares.
mount_getexports() uses the MOUNT protocol and this is only available in NFSv3.
Instead, NFSv4 uses a concept of a virtual directory tree, starting at '/' which is then populated with the actual exports. Assume you export /data/foo and /data/bar from the server.
Then this virtual filesystem will look like '/' 'data' 'foo' (which contains the export) 'bar' (which contains the export)
Think of it as in NFSv4 you always connect to '/' and from there you get to access all the shares of the server.
to see if the directory a user wants to mount is valid. That function
doesnt take a context, does it work with v4?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265#issuecomment-418886699, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkOMMmfCKqrA5IFMzNVUXwhHOubzvks5uYEFDgaJpZM4WWtdR .
I guess, to answer your initial question.
If you want to validate if what the user specified is valid, i.e. if the user specified server=="server" and share="/data/foo"
Then just connect to the server (which connects you to '/') and then just do a stat("/data/foo") stat will tell you whether the directory/share can be accessed or not/is valid or not.
Easier is probably to skip the whole concept of shares for NFSv4 completely. Just connect to '/' and then let the user browse down into the subdirectories until he/she finds the directory/export they want to access.
On Thu, Sep 6, 2018 at 7:34 AM, ronnie sahlberg [email protected] wrote:
On Thu, Sep 6, 2018 at 7:25 AM, Max Ehrlich [email protected] wrote:
@sahlberg https://github.com/sahlberg Trying to add in support now and one thing that I'm running into is that the first thing GVFS does is run
export_list = mount_getexports (host);
Don't use this function for NFSv4. In NFSv4 you don't really mount a specific share/export on the client anymore and in fact NFSv4 does not even have methods to collect the list of shares.
mount_getexports() uses the MOUNT protocol and this is only available in NFSv3.
Instead, NFSv4 uses a concept of a virtual directory tree, starting at '/' which is then populated with the actual exports. Assume you export /data/foo and /data/bar from the server.
Then this virtual filesystem will look like '/' 'data' 'foo' (which contains the export) 'bar' (which contains the export)
Think of it as in NFSv4 you always connect to '/' and from there you get to access all the shares of the server.
to see if the directory a user wants to mount is valid. That function
doesnt take a context, does it work with v4?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265#issuecomment-418886699, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkOMMmfCKqrA5IFMzNVUXwhHOubzvks5uYEFDgaJpZM4WWtdR .
Ok interesting, I hate for it to seem like I'm offloading work, but this implies we should know beforehand whether the server is running NFSv4 or NFSv3, can you think of a simple way I could detect that so I know which validation code to use?
My initial thought was to set up a connection w/ v4 and see if it fails, if it works go the v4 codepath if it doesnt go the v3 codepath.
So specific question: is there a function which will just do the connection so I can detect the failure quickly.
On Thu, Sep 6, 2018 at 7:39 AM, Max Ehrlich [email protected] wrote:
Ok interesting, I hate for it to seem like I'm offloading work, but this implies we should know beforehand whether the server is running NFSv4 or NFSv3, can you think of a simple way I could detect that so I know which validation code to use?
I want to say that using portmapper to check if NFSv4 is available would be the answer but sadly, portmapper is not required for NFSv4 and some NFSv4 servers thus don't have it running :--( So, sadly, no there is no reliable way to probe for protocol support. The only way to check is to actually attempt to connect and access the server.
My initial thought was to set up a connection w/ v4 and see if it fails, if it works go the v4 codepath if it doesnt go the v3 codepath.
So specific question: is there a function which will just do the connection so I can detect the failure quickly.
The easiest is probably just trying to connect to '/' on NFSv4 and that fails switch to the workflow for NFSv3.
There is a way however but it is a little involved and require you use the lower leverl raw RPC interface. Create a RPC context, then connect directly to port 2049 using this function :
/*
- Async function to connect to a specific RPC program/version.
- This connects directly to the specified port without using portmapper.
- Function returns
- 0 : The connection was initiated. The callback will be invoked once the
-
connection establish finishes. - <0 : An error occured when trying to set up the connection.
-
The callback will not be invoked. - When the callback is invoked, status indicates the result:
- RPC_STATUS_SUCCESS : The tcp connection was successfully established.
-
data is NULL. - RPC_STATUS_ERROR : The connection failed to establish.
-
data is the error string. - RPC_STATUS_CANCEL : The connection attempt was aborted before it could
-
complete. -
: data is NULL.
*/ EXTERN int rpc_connect_port_async(struct rpc_context *rpc, const char *server, int port, int program, int version, rpc_cb cb, void *private_data);
This function will connect to the port and attempt to call the NULL procedure for the protocol. The NULL procedure will fail with an error, and thus this command will too, if the protocol/version is not available.
Try this function with port==2049 program==NFS4_PROGRAM version==NFS_V4 if it is successful then v4 is present on that server, you can destroy the rpc context and switch back to connecting using the nfs layer. If this fails, then v4 is not available and you can fall back to using v3.
—
You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sahlberg/libnfs/issues/265#issuecomment-418890519, or mute the thread https://github.com/notifications/unsubscribe-auth/AAeNkOGWKmEIpRsHaNvVNNbD_1F9Y0MXks5uYEScgaJpZM4WWtdR .
OK thanks I'll give option 2 a shot, it sounds like the right way to do it
I was able to implement option 2 and it works quite well, one thing that would be cool is exporting the function wait_for_reply and its assoicated sync_cb_data callback to allow for making functions with only an _async version synchronous. It would have required a large re-architect of the gvfs mounting function to use the async versions so I copied these implementations to make them work synchronously.
Most functions are working on nfsv4 but I am running into an issue with reading files. The flow that GVFS uses when opening a file is
- call
nfs_open_asyncpassingO_RDONLYand their callback functionopen_for_read_cb open_for_read_cbis called by libnfs with err = 0 (no error)open_for_read_cbcallsnfs_fstat_asyncpassing their callback functionopen_for_read_fstat_cb
With NFSv3 this flow continues into the open_for_read_fstat_cb called from libnfs. For NFSv4, however, that callback function isn't being called, so the daemon just sits and applications trying to read files spin forever. Do you happen to know why that wouldn't work?
Ah OK I just found
nfs_set_error(nfs, "%s does not support NFSv4",
__FUNCTION__);
in the fstat code, I'll have to come up with a workaround for that
OK one more issue. Reads are fixed by simply using the fstat64. Writes don't work though
It appears the GVFS uses the following procedure for writes:
- Call
nfs_create_asyncpassingO_EXCLand its callbackreplace_create_cb replace_create_cbis called, if there is no error then the new file is written to. If there is error-EEXIST, the it callsnfs_lstat64_asyncwith its callbackreplace_lstat_cbreplace_lstat_cbis called, if there is no error then the existing file is opened for writing.
On NFSV3, when opening an existing file and saving it, this all works as expected. However on NFSV4, even if the file exists, I am not seeing an -EEXIST error, nfs_create_async is passing err=0 in the callback. This causes a failure when the write is performed presumably because O_CREAT and O_EXCL are used to write the file on the server side which is not allowed.
I havent dived too deeply into the libnfs side of this, and I am still verifying that things like paths are being passed properly, but I thought maybe you would know of something wrong with this procedure (or be able to find a potential bug faster than me).
Update:
Paths look correct for NFSV3 and V4, the only difference I can spot is that the callback from nfs_create_async is not giving error -EEXIST for existing files. The exact error message from nfs_write_async is then
WRITE (path (null)) failed with NFS4ERR_OPENMODE(-5)
Hi @sahlberg just wondering if you have any ideas about this, I still havent been able to figure out a workaround. It really seems like the EEXIST error should be passed by libnfs.
You are right, it looks like O_CREAT|O_EXCL is broken on nfsv4. I will look into it.
OK thanks for taking a look
I just pushed 9454d9f4c13145ea81799a23 to master. It adds O_EXCL support and seems to work after some light testing.
Please test it.
Thanks for the fast turnaround
So now I am seeing the following output from my debugging statements:
replace_create_cb -17
try_write 94430055222080
write_cb NFS4: WRITE (path (null)) failed with NFS4ERR_OPENMODE(-5)
So replace_create_cb (step 2 in the procedure I described above) is getting error -17, I think that's the fix you added but I need to double check that thats -EEXIST. However, I ultimately still get the openmode error -5. The path(null) part of that is also bothering me. I'll have a look but if this seems like something obvious let me know
Regarding to check if server supports NFSv4 or NFSv3. If I set the initial version to NFSv4 using nfs_set_version() and if my call to nfs_mount() is failing, then I can try setting NFSv3 in nfs_set_version() and then again call nfs_mount() API. @sahlberg Do you see any issue with this approach?
That should work. I haven't tested, but it should work.
Thanks, I tested that and it works.
Good to hear.
I think the optimal would be to try to make it automatic for the default case. If no version has been set, then first try nfsv3, as it is the most tested dialect in libnfs, and if that fails then automatically try nfsv4.0.
Yes, I can call in that sequence. Only reason I was planning for NFSv4 first and if its fails then only NFSv3, because based on this only I will query for the ACLs also. If it is able to connect using NFSv4 then for ACL I will use nfs4_getacl() else I would use NGSv3 ACL as shown in https://github.com/sahlberg/libnfs/blob/master/examples/nfsclient-raw.c
Any success or workaround for mounting NFSv4 through nautilus?