summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Kelly <mike@weatherwax.co.uk>2025-07-13 12:40:37 +0200
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2025-07-13 12:40:37 +0200
commit24d3d2bded0b13e0f2569c2ce7ada7f883564a68 (patch)
treeef46b456a1598d8379221955892470425d72c393
parent6e02392b5d5db9f16f4e1316d8097573361bf637 (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.c82
1 files changed, 77 insertions, 5 deletions
diff --git a/nfs/ops.c b/nfs/ops.c
index 78e51cf4..e4450470 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -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)