summaryrefslogtreecommitdiff
path: root/nfs/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'nfs/ops.c')
-rw-r--r--nfs/ops.c531
1 files changed, 409 insertions, 122 deletions
diff --git a/nfs/ops.c b/nfs/ops.c
index 50218fa5..5251d423 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -74,6 +74,61 @@ register_fresh_stat (struct node *np, int *p)
return ret;
}
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it
+ checks to see if stat information is present too. If this follows
+ an operation that we expect has modified the attributes, MOD should
+ be set. (This unpacks the post_op_attr XDR type.) */
+int *
+process_returned_stat (struct node *np, int *p, int mod)
+{
+ int attrs_exist;
+
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ attrs_exist = ntohl (*p++);
+ if (attrs_exist)
+ p = register_fresh_stat (np, p);
+ else if (mod)
+ /* We know that our values are now wrong */
+ np->nn->stat_updated = 0;
+ return p;
+ }
+}
+
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it does
+ the wcc_data interpretation too. If this follows an operation that
+ we expect has modified the attributes, MOD should be set.
+ (This unpacks the wcc_data XDR type.) */
+int *
+process_wcc_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ /* First the pre_op_attr */
+ attrs_exist = ntohl (*p++);
+ if (attrs_exist)
+ {
+ /* Just skip them for now */
+ p += 2 * sizeof (int); /* size */
+ p += 2 * sizeof (int); /* mtime */
+ p += 2 * sizeof (int); /* atime */
+ }
+
+ /* Now the post_op_attr */
+ return process_returned_stat (np, p, mod);
+ }
+}
+
+
/* Implement the netfs_validate_stat callback as described in
<hurd/netfs.h>. */
error_t
@@ -116,11 +171,16 @@ netfs_attempt_chown (struct netcred *cred, struct node *np,
cred, 0, &rpcbuf, np, gid);
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_sattr_ids (p, uid, gid);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard_check == 0 */
err = conduct_rpc (&rpcbuf, &p);
-
if (!err)
- register_fresh_stat (np, p);
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
@@ -185,14 +245,17 @@ netfs_attempt_chmod (struct netcred *cred, struct node *np,
cred, 0, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_sattr_mode (p, mode);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard check == 0 */
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
- if (!err)
- register_fresh_stat (np, p);
-
free (rpcbuf);
return err;
}
@@ -220,13 +283,16 @@ netfs_attempt_utimes (struct netcred *cred, struct node *np,
cred, 0, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_sattr_times (p, atime, mtime);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard check == 0 */
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (!err)
- register_fresh_stat (np, p);
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
return err;
@@ -246,14 +312,17 @@ netfs_attempt_set_size (struct netcred *cred, struct node *np,
cred, 0, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_sattr_size (p, size);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard_check == 0 */
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
- if (!err)
- register_fresh_stat (np, p);
-
free (rpcbuf);
return err;
}
@@ -324,6 +393,7 @@ netfs_attempt_read (struct netcred *cred, struct node *np,
size_t trans_len;
error_t err;
size_t amt, thisamt;
+ int eof;
for (amt = *len; amt;)
{
@@ -336,36 +406,44 @@ netfs_attempt_read (struct netcred *cred, struct node *np,
p = xdr_encode_fhandle (p, &np->nn->handle);
*p++ = htonl (offset);
*p++ = htonl (thisamt);
- *p++ = 0;
+ if (protocol_version == 2)
+ *p++ = 0;
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (err)
{
- free (rpcbuf);
- return err;
- }
-
- p = register_fresh_stat (np, p);
-
- trans_len = ntohl (*p++);
- if (trans_len > thisamt)
- trans_len = thisamt; /* ??? */
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err || protocol_version == 3)
+ p = process_returned_stat (np, p, !err);
+
+ if (err)
+ {
+ free (rpcbuf);
+ return err;
+ }
- bcopy (p, data, trans_len);
- free (rpcbuf);
+ trans_len = ntohl (*p++);
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
- data += trans_len;
- offset += trans_len;
- amt -= trans_len;
+ if (protocol_version == 3)
+ eof = ntohl (*p++);
+ else
+ eof = (trans_len < thisamt);
+
+ bcopy (p, data, trans_len);
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
- /* If we got a short count, that means we're all done */
- if (trans_len < thisamt)
- {
- *len -= amt;
- return 0;
+ if (eof)
+ {
+ *len -= amt;
+ return 0;
+ }
}
}
return 0;
@@ -381,6 +459,7 @@ netfs_attempt_write (struct netcred *cred, struct node *np,
void *rpcbuf;
error_t err;
size_t amt, thisamt;
+ size_t count;
for (amt = *len; amt;)
{
@@ -391,26 +470,47 @@ netfs_attempt_write (struct netcred *cred, struct node *np,
p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version),
cred, thisamt, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
- *p++ = 0;
+ if (protocol_version == 2)
+ *p++ = 0;
*p++ = htonl (offset);
- *p++ = 0;
+ if (protocol_version == 2)
+ *p++ = 0;
+ if (protocol_version == 3)
+ *p++ = htonl (FILE_SYNC);
p = xdr_encode_data (p, data, thisamt);
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ if (!err)
+ {
+ if (protocol_version == 3)
+ {
+ count = ntohl (*p++);
+ p++; /* ignore COMMITTED */
+ /* ignore verf for now */
+ p += NFS3_WRITEVERFSIZE / sizeof (int);
+ }
+ else
+ /* assume it wrote the whole thing */
+ count = thisamt;
+
+ free (rpcbuf);
+ amt -= count;
+ data += count;
+ offset += count;
+ }
+ }
+
if (err)
{
*len = 0;
free (rpcbuf);
return err;
}
- register_fresh_stat (np, p);
- free (rpcbuf);
- amt -= thisamt;
- data += thisamt;
- offset += thisamt;
}
return 0;
}
@@ -424,6 +524,8 @@ verify_nonexistent (struct netcred *cred, struct node *dir,
void *rpcbuf;
error_t err;
+ assert (protocol_version == 2);
+
p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
cred, 0, &rpcbuf, dir, -1);
p = xdr_encode_fhandle (p, &dir->nn->handle);
@@ -458,12 +560,25 @@ netfs_attempt_lookup (struct netcred *cred, struct node *np,
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (!err)
{
- p = lookup_fhandle (p, newnp);
- register_fresh_stat (*newnp, p);
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ p = lookup_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ mutex_unlock (&(*newnp)->lock);
+ mutex_lock (&np->lock);
+ p = process_returned_stat (np, p, 0);
+ mutex_unlock (&np->lock);
+ if (*newnp)
+ mutex_lock (&(*newnp)->lock);
+ }
}
else
*newnp = 0;
@@ -519,7 +634,11 @@ netfs_attempt_rmdir (struct netcred *cred, struct node *np,
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
return err;
@@ -586,42 +705,71 @@ netfs_attempt_link (struct netcred *cred, struct node *dir,
return err;
}
- p = xdr_encode_string (p, np->nn->transarg.name);
- p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ if (protocol_version == 2)
+ {
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ }
+ else
+ {
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ }
mutex_unlock (&np->lock);
mutex_lock (&dir->lock);
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (!err)
{
- /* NFSPROC_SYMLINK stupidly does not pass back an
- fhandle, so we have to fetch one now. */
- p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
- cred, 0, &rpcbuf, dir, -1);
- p = xdr_encode_fhandle (p, &dir->nn->handle);
- p = xdr_encode_string (p, name);
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (protocol_version == 2 && !err)
+ {
+ free (rpcbuf);
+
+ /* NFSPROC_SYMLINK stupidly does not pass back an
+ fhandle, so we have to fetch one now. */
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
- err = conduct_rpc (&rpcbuf, &p);
- if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- mutex_unlock (&dir->lock);
-
- if (err)
- err = EGRATUITOUS; /* damn */
- else
+ mutex_unlock (&dir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ }
+ if (err)
+ err = EGRATUITOUS; /* damn */
+ }
+ else if (protocol_version == 3)
{
- mutex_lock (&np->lock);
- p = recache_handle (p, np);
- register_fresh_stat (np, p);
- mutex_unlock (&np->lock);
+ if (!err)
+ {
+ mutex_unlock (&dir->lock);
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ mutex_lock (&dir->lock);
+ }
+ p = process_wcc_stat (dir, p, !err);
+ mutex_unlock (&dir->lock);
}
+ else
+ mutex_unlock (&dir->lock);
}
else
mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
break;
case CHRDEV:
@@ -629,40 +777,87 @@ netfs_attempt_link (struct netcred *cred, struct node *dir,
case FIFO:
case SOCK:
- mutex_lock (&dir->lock);
- err = verify_nonexistent (cred, dir, name);
- if (err)
- return err;
+ if (protocol_version == 2)
+ {
+ mutex_lock (&dir->lock);
+ err = verify_nonexistent (cred, dir, name);
+ if (err)
+ return err;
- p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
- cred, 0, &rpcbuf, dir, -1);
- p = xdr_encode_fhandle (p, &dir->nn->handle);
- p = xdr_encode_string (p, name);
- mutex_unlock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ mutex_unlock (&np->lock);
- mutex_lock (&np->lock);
- err = netfs_validate_stat (np, cred);
- if (err)
+ mutex_lock (&dir->lock);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ register_fresh_stat (np, p);
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ }
+ else
{
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+ *p++ = htonl (hurd_mode_to_nfs_type (np->nn_stat.st_mode));
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ if (np->nn->dtrans == BLKDEV || np->nn->dtrans == CHRDEV)
+ {
+#define major(D) (((D)>>8) & 0xff)
+#define minor(D) ((D) & 0xff)
+ *p++ = htonl (major (np->nn_stat.st_rdev));
+ *p++ = htonl (minor (np->nn_stat.st_rdev));
+ }
mutex_unlock (&np->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ }
+ mutex_lock (&dir->lock);
+ p = process_wcc_stat (dir, p, !err);
+ mutex_unlock (&dir->lock);
+ }
free (rpcbuf);
- return err;
}
-
- p = xdr_encode_sattr_stat (p, &np->nn_stat);
- mutex_unlock (&np->lock);
-
- mutex_lock (&dir->lock);
- err = conduct_rpc (&rpcbuf, &p);
- if (!err)
- err = nfs_error_trans (ntohl (*p++));
- mutex_unlock (&dir->lock);
-
- mutex_lock (&np->lock);
- p = recache_handle (p, np);
- register_fresh_stat (np, p);
- mutex_unlock (&np->lock);
-
break;
}
@@ -743,26 +938,57 @@ netfs_attempt_create_file (struct netcred *cred, struct node *np,
void *rpcbuf;
error_t err;
- err = verify_nonexistent (cred, np, name);
- if (err)
- return err;
+ /* RFC 1094 says that create is always exclusive. But Sun doesn't
+ actually *implement* the spec. No, of course not. So we have to do
+ it for them. */
+ if (protocol_version == 2)
+ {
+ err = verify_nonexistent (cred, np, name);
+ if (err)
+ return err;
+ }
p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
cred, 0, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_string (p, name);
- p = xdr_encode_create_state (p, mode);
+ if (protocol_version == 3)
+ {
+ /* We happen to know this is where the XID is. */
+ int verf = *(int *)rpcbuf;
+
+ *p++ = ntohl (EXCLUSIVE);
+ /* 8 byte verf */
+ *p++ = ntohl (verf);
+ p++;
+ }
+ else
+ p = xdr_encode_create_state (p, mode);
err = conduct_rpc (&rpcbuf, &p);
- if (!err)
- err = nfs_error_trans (ntohl (*p++));
mutex_unlock (&np->lock);
if (!err)
{
- p = lookup_fhandle (p, newnp);
- register_fresh_stat (*newnp, p);
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ p = lookup_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ mutex_unlock (&(*newnp)->lock);
+ mutex_lock (&np->lock);
+ p = process_wcc_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ if (*newnp)
+ mutex_lock (&(*newnp)->lock);
+ }
}
else
*newnp = 0;
@@ -839,7 +1065,11 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir,
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_wcc_stat (dir, p, !err);
+ }
free (rpcbuf);
@@ -874,7 +1104,15 @@ netfs_attempt_rename (struct netcred *cred, struct node *fromdir,
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ {
+ mutex_lock (&fromdir->lock);
+ p = process_wcc_stat (fromdir, p, !err);
+ p = process_wcc_stat (todir, p, !err);
+ }
+ }
free (rpcbuf);
return err;
@@ -902,10 +1140,13 @@ netfs_attempt_readlink (struct netcred *cred, struct node *np,
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (!err)
- p = xdr_decode_string (p, buf);
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ p = xdr_decode_string (p, buf);
+ }
free (rpcbuf);
return err;
@@ -961,11 +1202,13 @@ error_t
netfs_check_open_permissions (struct netcred *cred, struct node *np,
int flags, int newnode)
{
+ int modes;
+
if ((flags & (O_READ|O_WRITE|O_EXEC)) == 0)
return 0;
- if ((flags & (O_READ|O_WRITE|O_EXEC))
- == (flags & guess_mode_use (np, cred)))
+ netfs_report_access (cred, np, &modes);
+ if ((flags & (O_READ|O_WRITE|O_EXEC)) == (flags & modes))
return 0;
else
return EACCES;
@@ -978,9 +1221,53 @@ netfs_report_access (struct netcred *cred,
struct node *np,
int *types)
{
- *types = guess_mode_use (np, cred);
-}
+ if (protocol_version == 2)
+ *types = guess_mode_use (np, cred);
+ else
+ {
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ int ret;
+ int write_check, execute_check;
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ goto fallback;
+ if (S_ISDIR (np->nn_stat.st_mode))
+ {
+ write_check = ACCESS3_MODIFY | ACCESS3_DELETE | ACCESS3_EXTEND;
+ execute_check = ACCESS3_LOOKUP;
+ }
+ else
+ {
+ write_check = ACCESS3_MODIFY;
+ execute_check = ACCESS3_EXECUTE;
+ }
+
+ p = nfs_initialize_rpc (NFS3PROC_ACCESS, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (ACCESS3_READ | write_check | execute_check);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ {
+ ret = ntohl (*p++);
+ *types = ((ret & ACCESS3_READ ? O_READ : 0)
+ | (ret & write_check ? O_WRITE : 0)
+ | (ret & execute_check ? O_EXEC : 0));
+ }
+ else
+ /* fall back, sigh. */
+ fallback:
+ *types = guess_mode_use (np, cred);
+ }
+ }
+}
/* These definitions have unfortunate side effects, don't use them,
clever though they are. */