diff options
author | Michael Kelly <mike@weatherwax.co.uk> | 2025-07-13 12:40:37 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2025-07-13 12:40:37 +0200 |
commit | 24d3d2bded0b13e0f2569c2ce7ada7f883564a68 (patch) | |
tree | ef46b456a1598d8379221955892470425d72c393 | |
parent | 6e02392b5d5db9f16f4e1316d8097573361bf637 (diff) |
nfs: Fix file creation for nfsv3
This adds new code to process the reply from a file creation operation that
applies equally to CREATE3, MKDIR3, SYMLINK3 and MKNOD3. RFC1813 permits
a reply without the created file's handle and it might be necessary to
perform a LOOKUP3 on the created file post-creation. Debian Linux NFSV3
server actually always returns the handle when compiling gnumach however
I have tested the case where it might not by adding code (now removed) to
pretend the handle wasn't present in the reply. I expect therefore that the
'skip_returned_stat' function that is added is rarely called.
Fixed netfs_attempt_mkdir() to process the NFSv3 reply properly.
-rw-r--r-- | nfs/ops.c | 82 |
1 files changed, 77 insertions, 5 deletions
@@ -134,6 +134,70 @@ process_wcc_stat (struct node *np, int *p, int mod) } } +/* advance the reply buffer beyond the 'post_op_attr' (v3) or + 'fattr' (v2). Populating 'struct stat' for the purposes of + advancing the correct number of bytes might seem wasteful + but it is not expected that this function will be called + often. */ +static int * +skip_returned_stat (int *p) +{ + struct stat st; + + if (protocol_version == 2) + return xdr_decode_fattr (p, &st); + + int attrs_exist = ntohl (*p); + p++; + + return (attrs_exist ? xdr_decode_fattr (p, &st) : p); +} + +/* The reply to CREATE, MKDIR, SYMLINK and MKNOD in v3 all have + the same reply content. It is expected that 'np' is supplied + locked and any 'newnp' is also returned locked. */ +static error_t +process_create_reply (struct iouser *cred, + struct node *np, + const char* name, + struct node **newnp, + int *p) +{ + assert_backtrace (protocol_version == 3); + + error_t err = nfs_error_trans (ntohl (*p)); + p++; + + if (!err) + { + int handle_follows = ntohl (*p); + p++; + + if (handle_follows) + { + p = xdr_decode_fhandle (p, newnp); + p = process_returned_stat (*newnp, p, 1); + } + else + /* These will be refetched in LOOKUP */ + p = skip_returned_stat (p); + + p = process_wcc_stat (np, p, 1); + + if (!handle_follows) + { + err = netfs_attempt_lookup (cred, np, name, newnp); + /* netfs_attempt_lookup always unlocks 'np' so... */ + pthread_mutex_lock (&np->lock); + } + } + else + { + p = process_wcc_stat (np, p, 1); + } + + return err; +} /* Implement the netfs_validate_stat callback as described in <hurd/netfs.h>. */ @@ -740,15 +804,23 @@ netfs_attempt_mkdir (struct iouser *cred, struct node *np, err = conduct_rpc (&rpcbuf, &p); if (!err) { - err = nfs_error_trans (ntohl (*p)); - p++; + if (protocol_version == 2) + { + err = nfs_error_trans (ntohl (*p)); + p++; + + if (!err) + { + p = xdr_decode_fhandle (p, &newnp); + p = process_returned_stat (newnp, p, 1); + } + } + else + err = process_create_reply (cred, np, name, &newnp, p); } if (!err) { - p = xdr_decode_fhandle (p, &newnp); - p = process_returned_stat (newnp, p, 1); - /* Did we set the owner correctly? If not, try, but ignore failures. */ if (!netfs_validate_stat (newnp, (struct iouser *) -1) && newnp->nn_stat.st_uid != owner) |