summaryrefslogtreecommitdiff
path: root/nfs/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'nfs/ops.c')
-rw-r--r--nfs/ops.c1416
1 files changed, 1019 insertions, 397 deletions
diff --git a/nfs/ops.c b/nfs/ops.c
index efeded10..05cfbe9a 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -1,5 +1,5 @@
-/* Libnetfs callbacks for node operations in NFS client
- Copyright (C) 1994, 1995, 1996 Free Software Foundation
+/* ops.c - Libnetfs callbacks for node operations in NFS client.
+ Copyright (C) 1994,95,96,97,99,2002,2011 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -21,17 +21,19 @@
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stddef.h>
#include <dirent.h>
#include <unistd.h>
+#include <maptime.h>
-/* We have fresh stat information for NP; the fattr structure is at
- P. Update our entry. Return the address of the next int after
- the fattr structure. */
+/* We have fresh stat information for NP; the file attribute (fattr)
+ structure is at P. Update our entry. Return the address of the next
+ int after the fattr structure. */
int *
register_fresh_stat (struct node *np, int *p)
{
int *ret;
-
+
ret = xdr_decode_fattr (p, &np->nn_stat);
np->nn->stat_updated = mapped_time->seconds;
@@ -40,12 +42,12 @@ register_fresh_stat (struct node *np, int *p)
case NOT_POSSIBLE:
case POSSIBLE:
break;
-
+
case SYMLINK:
np->nn_stat.st_size = strlen (np->nn->transarg.name);
np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFLNK);
break;
-
+
case CHRDEV:
np->nn_stat.st_rdev = np->nn->transarg.indexes;
np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFCHR);
@@ -70,75 +72,147 @@ register_fresh_stat (struct node *np, int *p)
np->nn_stat.st_gen = 0;
np->nn_stat.st_author = np->nn_stat.st_uid;
np->nn_stat.st_flags = 0;
-
+ np->nn_translated = np->nn_stat.st_mode & S_IFMT;
+
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)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ attrs_exist = ntohl (*p);
+ 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);
+ 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>. */
+ <hurd/netfs.h>. */
error_t
-netfs_validate_stat (struct node *np, struct netcred *cred)
+netfs_validate_stat (struct node *np, struct iouser *cred)
{
int *p;
void *rpcbuf;
error_t err;
-
+
if (mapped_time->seconds - np->nn->stat_updated < stat_timeout)
return 0;
- p = nfs_initialize_rpc (NFSPROC_GETATTR, (struct netcred *) -1,
- 0, &rpcbuf, np, -1);
+ p = nfs_initialize_rpc (NFSPROC_GETATTR (protocol_version),
+ (struct iouser *) -1, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
-
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
if (!err)
register_fresh_stat (np, p);
- np->istranslated = 0;
-
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_chown callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_chown (struct netcred *cred, struct node *np,
+netfs_attempt_chown (struct iouser *cred, struct node *np,
uid_t uid, gid_t gid)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, gid);
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, gid);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_sattr_ids (p, uid, gid);
-
- err = conduct_rpc (&rpcbuf, &p);
+ 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));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
-
+
return err;
}
/* Implement the netfs_attempt_chauthor callback as described in
<hurd/netfs.h>. */
error_t
-netfs_attempt_chauthor (struct netcred *cred, struct node *rp,
+netfs_attempt_chauthor (struct iouser *cred, struct node *rp,
uid_t author)
{
return EOPNOTSUPP;
}
/* Implement the netfs_attempt_chmod callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_chmod (struct netcred *cred, struct node *np,
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
mode_t mode)
{
int *p;
@@ -150,26 +224,29 @@ netfs_attempt_chmod (struct netcred *cred, struct node *np,
err = netfs_validate_stat (np, cred);
if (err)
return err;
+
+ /* Has the file type changed? (e.g. from symlink to
+ directory). */
if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
{
char *f = 0;
-
+
if (np->nn->dtrans == NOT_POSSIBLE)
return EOPNOTSUPP;
-
+
if (np->nn->dtrans == SYMLINK)
f = np->nn->transarg.name;
-
+
switch (mode & S_IFMT)
{
default:
return EOPNOTSUPP;
-
+
case S_IFIFO:
np->nn->dtrans = FIFO;
np->nn->stat_updated = 0;
break;
-
+
case S_IFSOCK:
np->nn->dtrans = SOCK;
np->nn->stat_updated = 0;
@@ -180,25 +257,33 @@ netfs_attempt_chmod (struct netcred *cred, struct node *np,
}
}
- p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1);
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
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++));
-
- if (!err)
- register_fresh_stat (np, p);
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_chflags callback as described in
- <hurd/netfs.h>. */
-error_t
-netfs_attempt_chflags (struct netcred *cred, struct node *np,
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
int flags)
{
return EOPNOTSUPP;
@@ -207,48 +292,91 @@ netfs_attempt_chflags (struct netcred *cred, struct node *np,
/* Implement the netfs_attempt_utimes callback as described in
<hurd/netfs.h>. */
error_t
-netfs_attempt_utimes (struct netcred *cred, struct node *np,
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
struct timespec *atime, struct timespec *mtime)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1);
+ struct timeval tv;
+ struct timespec current;
+
+ /* XXX For version 3 we can actually do this right, but we don't
+ just yet. */
+ if (!atime || !mtime)
+ {
+ maptime_read (mapped_time, &tv);
+ current.tv_sec = tv.tv_sec;
+ current.tv_nsec = tv.tv_usec * 1000;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
- p = xdr_encode_sattr_times (p, atime, mtime);
-
+ p = xdr_encode_sattr_times (p,
+ atime ?: &current,
+ mtime ?: &current);
+ 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));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_set_size callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_set_size (struct netcred *cred, struct node *np,
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
off_t size)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1);
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
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++));
-
- if (!err)
- register_fresh_stat (np, p);
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ /* If we got EACCES, but the user has the file open for writing,
+ then the NFS protocol has screwed us. There's nothing we can do,
+ except in the important case of opens with
+ O_TRUNC|O_CREAT|O_WRONLY|O_EXCL where the new mode does not allow
+ writing. RCS, for example, uses this to create lock files. So permit
+ cases where the O_TRUNC isn't doing anything to succeed if the user
+ does have the file open for writing. */
+ if (err == EACCES)
+ {
+ int error = netfs_validate_stat (np, cred);
+ if (!error && np->nn_stat.st_size == size)
+ err = 0;
+ }
free (rpcbuf);
return err;
@@ -257,45 +385,52 @@ netfs_attempt_set_size (struct netcred *cred, struct node *np,
/* Implement the netfs_attempt_statfs callback as described in
<hurd/netfs.h>. */
error_t
-netfs_attempt_statfs (struct netcred *cred, struct node *np,
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
struct statfs *st)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_STATFS, cred, 0, &rpcbuf, np, -1);
+
+ p = nfs_initialize_rpc (NFS2PROC_STATFS, cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
-
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
if (!err)
{
- long iosize, bsize;
- iosize = *p++;
- bsize = *p++;
- st->f_bsize = iosize;
- st->f_blocks = ((*p++) * bsize) / iosize;
- st->f_bfree = ((*p++) * bsize) / iosize;
- st->f_bavail = ((*p++) * bsize) / iosize;
-
+ p++; /* skip IOSIZE field */
+ st->f_bsize = ntohl (*p);
+ p++;
+ st->f_blocks = ntohl (*p);
+ p++;
+ st->f_bfree = ntohl (*p);
+ p++;
+ st->f_bavail = ntohl (*p);
+ p++;
st->f_type = FSTYPE_NFS;
st->f_files = 0;
st->f_ffree = 0;
- st->f_fsid = 0; /* XXX wrong */
+ st->f_fsid = getpid ();
st->f_namelen = 0;
}
-
+
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_sync callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_sync (struct netcred *cred, struct node *np, int wait)
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
{
/* We are already completely synchronous. */
return 0;
@@ -304,15 +439,15 @@ netfs_attempt_sync (struct netcred *cred, struct node *np, int wait)
/* Implement the netfs_attempt_syncfs callback as described in
<hurd/netfs.h>. */
error_t
-netfs_attempt_syncfs (struct netcred *cred, int wait)
+netfs_attempt_syncfs (struct iouser *cred, int wait)
{
return 0;
}
/* Implement the netfs_attempt_read callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_read (struct netcred *cred, struct node *np,
+netfs_attempt_read (struct iouser *cred, struct node *np,
off_t offset, size_t *len, void *data)
{
int *p;
@@ -320,191 +455,370 @@ 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;)
{
thisamt = amt;
if (thisamt > read_size)
thisamt = read_size;
- p = nfs_initialize_rpc (NFSPROC_READ, cred, 0, &rpcbuf, np, -1);
+ p = nfs_initialize_rpc (NFSPROC_READ (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
- *p++ = htonl (offset);
- *p++ = htonl (thisamt);
- *p++ = 0;
-
+ *(p++) = htonl (offset);
+ *(p++) = htonl (thisamt);
+ if (protocol_version == 2)
+ *(p++) = 0;
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (err)
{
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (!err || protocol_version == 3)
+ p = process_returned_stat (np, p, !err);
+
+ if (err)
+ {
+ free (rpcbuf);
+ return err;
+ }
+
+ trans_len = ntohl (*p);
+ p++;
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ if (protocol_version == 3)
+ {
+ eof = ntohl (*p);
+ p++;
+ }
+ else
+ eof = (trans_len < thisamt);
+
+ memcpy (data, p, trans_len);
free (rpcbuf);
- return err;
- }
-
- p = register_fresh_stat (np, p);
-
- trans_len = ntohl (*p++);
- if (trans_len > thisamt)
- 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;
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ if (eof)
+ {
+ *len -= amt;
+ return 0;
+ }
}
}
return 0;
}
-
+
/* Implement the netfs_attempt_write callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_write (struct netcred *cred, struct node *np,
+netfs_attempt_write (struct iouser *cred, struct node *np,
off_t offset, size_t *len, void *data)
{
int *p;
void *rpcbuf;
error_t err;
size_t amt, thisamt;
-
+ size_t count;
+
for (amt = *len; amt;)
{
thisamt = amt;
if (thisamt > write_size)
thisamt = write_size;
-
- p = nfs_initialize_rpc (NFSPROC_WRITE, cred, thisamt, &rpcbuf, np, -1);
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version),
+ cred, thisamt, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
- *p++ = 0;
- *p++ = htonl (offset);
- *p++ = 0;
+ if (protocol_version == 2)
+ *(p++) = 0;
+ *(p++) = htonl (offset);
+ 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));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ if (!err)
+ {
+ if (protocol_version == 3)
+ {
+ count = ntohl (*p);
+ p++;
+ p++; /* ignore COMMITTED */
+ /* ignore verf for now */
+ p += NFS3_WRITEVERFSIZE / sizeof (int);
+ }
+ else
+ /* assume it wrote the whole thing */
+ count = thisamt;
+
+ amt -= count;
+ data += count;
+ offset += count;
+ }
+ }
+
+ free (rpcbuf);
+
+ if (err == EINTR && amt != *len)
+ {
+ *len -= amt;
+ return 0;
+ }
+
if (err)
{
*len = 0;
- free (rpcbuf);
return err;
}
- register_fresh_stat (np, p);
- free (rpcbuf);
- amt -= thisamt;
- data += thisamt;
- offset += thisamt;
}
return 0;
}
+/* See if NAME exists in DIR for CRED. If so, return EEXIST. */
+error_t
+verify_nonexistent (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Don't use the lookup cache for this; we want a full sync to
+ get as close to real exclusive create behavior as possible. */
+
+ assert (protocol_version == 2);
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
+
+ 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));
+ p++;
+ }
+
+ free (rpcbuf);
+
+ if (!err)
+ return EEXIST;
+ else
+ return 0;
+}
+
/* Implement the netfs_attempt_lookup callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_lookup (struct netcred *cred, struct node *np,
+netfs_attempt_lookup (struct iouser *cred, struct node *np,
char *name, struct node **newnp)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_LOOKUP, cred, 0, &rpcbuf, np, -1);
+ char dirhandle[NFS3_FHSIZE];
+ size_t dirlen;
+
+ /* Check the cache first. */
+ *newnp = check_lookup_cache (np, name);
+ if (*newnp)
+ {
+ if (*newnp == (struct node *) -1)
+ {
+ *newnp = 0;
+ return ENOENT;
+ }
+ else
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_string (p, name);
-
+
+ /* Remember the directory handle for later cache use. */
+
+ dirlen = np->nn->handle.size;
+ memcpy (dirhandle, np->nn->handle.data, dirlen);
+
mutex_unlock (&np->lock);
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- if (!err)
{
- *newnp = lookup_fhandle (p);
- p += NFS_FHSIZE / sizeof (int);
- register_fresh_stat (*newnp, p);
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_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); /* XXX Do we have to lock np? */
+ mutex_unlock (&np->lock);
+ if (*newnp)
+ mutex_lock (&(*newnp)->lock);
+ }
}
else
*newnp = 0;
-
+
+ /* Notify the cache of the hit or miss. */
+ enter_lookup_cache (dirhandle, dirlen, *newnp, name);
+
free (rpcbuf);
-
+
return err;
}
/* Implement the netfs_attempt_mkdir callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_mkdir (struct netcred *cred, struct node *np,
+netfs_attempt_mkdir (struct iouser *cred, struct node *np,
char *name, mode_t mode)
{
int *p;
void *rpcbuf;
error_t err;
-
- p = nfs_initialize_rpc (NFSPROC_MKDIR, cred, 0, &rpcbuf, np, -1);
+ uid_t owner;
+ struct node *newnp;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_MKDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_string (p, name);
- p = xdr_encode_create_state (p, mode);
-
+ p = xdr_encode_create_state (p, mode, owner);
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- /* Ignore returned information */
- /* XXX should probably cache it */
+ {
+ err = nfs_error_trans (ntohl (*p));
+ 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)
+ netfs_attempt_chown ((struct iouser *) -1, newnp, owner,
+ newnp->nn_stat.st_gid);
+
+ /* We don't actually return this. */
+ netfs_nput (newnp);
+ }
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_rmdir callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_rmdir (struct netcred *cred, struct node *np,
+netfs_attempt_rmdir (struct iouser *cred, struct node *np,
char *name)
{
int *p;
void *rpcbuf;
error_t err;
-
+
/* Should we do the same sort of thing here as with attempt_unlink? */
- p = nfs_initialize_rpc (NFSPROC_RMDIR, cred, 0, &rpcbuf, np, -1);
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_RMDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
p = xdr_encode_string (p, name);
-
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_link callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_link (struct netcred *cred, struct node *dir,
+netfs_attempt_link (struct iouser *cred, struct node *dir,
struct node *np, char *name, int excl)
{
int *p;
void *rpcbuf;
error_t err = 0;
-
+
if (!excl)
- return EOPNOTSUPP; /* XXX */
+ {
+ /* We have no RPC available that will do an atomic replacement,
+ so we settle for second best; just doing an unlink and ignoring
+ any errors. */
+ mutex_lock (&dir->lock);
+ netfs_attempt_unlink (cred, dir, name);
+ mutex_unlock (&dir->lock);
+ }
/* If we have postponed a translator setting on an unlinked node,
then here's where we set it, by creating the new node instead of
@@ -515,32 +829,51 @@ netfs_attempt_link (struct netcred *cred, struct node *dir,
case POSSIBLE:
case NOT_POSSIBLE:
mutex_lock (&dir->lock);
- p = nfs_initialize_rpc (NFSPROC_LINK, cred, 0, &rpcbuf, dir, -1);
+ p = nfs_initialize_rpc (NFSPROC_LINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ mutex_unlock (&dir->lock);
+ return errno;
+ }
+
mutex_unlock (&dir->lock);
-
+
mutex_lock (&np->lock);
p = xdr_encode_fhandle (p, &np->nn->handle);
mutex_unlock (&np->lock);
-
+
mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+
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++));
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
mutex_unlock (&dir->lock);
-
+
free (rpcbuf);
break;
case SYMLINK:
mutex_lock (&dir->lock);
- p = nfs_initialize_rpc (NFSPROC_SYMLINK, cred, 0, &rpcbuf, dir, -1);
+ p = nfs_initialize_rpc (NFSPROC_SYMLINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ mutex_unlock (&dir->lock);
+ return errno;
+ }
+
p = xdr_encode_fhandle (p, &dir->nn->handle);
mutex_unlock (&dir->lock);
-
+
p = xdr_encode_string (p, name);
mutex_lock (&np->lock);
@@ -551,85 +884,198 @@ netfs_attempt_link (struct netcred *cred, struct node *dir,
free (rpcbuf);
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);
+
+ purge_lookup_cache (dir, name, strlen (name));
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, 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);
+ err = nfs_error_trans (ntohl (*p));
+ p++;
- if (err)
- err = EGRATUITOUS; /* damn */
- else
+ if (protocol_version == 2 && !err)
{
- mutex_lock (&np->lock);
- recache_handle (np, p);
- p += NFS_FHSIZE / sizeof (int);
- register_fresh_stat (np, p);
- mutex_unlock (&np->lock);
+ 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);
+ if (! p)
+ {
+ mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ mutex_unlock (&dir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ 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)
+ {
+ 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:
case BLKDEV:
case FIFO:
case SOCK:
- mutex_lock (&dir->lock);
- p = nfs_initialize_rpc (NFSPROC_CREATE, 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)
+ 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);
+ if (! p)
+ {
+ mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ 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 (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+ mutex_unlock (&dir->lock); /* XXX Should this really be after the
+ _lengthy_ (blocking) conduct_rpc? */
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ register_fresh_stat (np, p);
+ mutex_unlock (&np->lock);
+ }
+
free (rpcbuf);
- return err;
}
-
- p = xdr_encode_sattr_stat (p, &np->nn_stat);
- mutex_unlock (&np->lock);
+ else /* protocol_version != 2 */
+ {
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ mutex_unlock (&dir->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);
- recache_handle (np, p);
- p += NFS_FHSIZE / sizeof (int);
- register_fresh_stat (np, p);
- mutex_unlock (&np->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)
+ {
+ *(p++) = htonl (major (np->nn_stat.st_rdev));
+ *(p++) = htonl (minor (np->nn_stat.st_rdev));
+ }
+ mutex_unlock (&np->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ 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);
+ }
break;
}
if (err)
return err;
-
+
mutex_lock (&np->lock);
if (np->nn->dtrans == SYMLINK)
@@ -644,21 +1090,21 @@ netfs_attempt_link (struct netcred *cred, struct node *dir,
np->nn->dead_dir = 0;
np->nn->dead_name = 0;
mutex_unlock (&np->lock);
-
+
mutex_lock (&dir->lock);
- netfs_attempt_unlink ((struct netcred *)-1, dir, name);
+ netfs_attempt_unlink ((struct iouser *)-1, dir, name);
mutex_unlock (&dir->lock);
}
else
mutex_unlock (&np->lock);
-
+
return 0;
}
/* Implement the netfs_attempt_mkfile callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_mkfile (struct netcred *cred, struct node *dir,
+netfs_attempt_mkfile (struct iouser *cred, struct node *dir,
mode_t mode, struct node **newnp)
{
error_t err;
@@ -668,16 +1114,20 @@ netfs_attempt_mkfile (struct netcred *cred, struct node *dir,
/* This is the best we can do. */
name = malloc (50);
+ if (! name)
+ return ENOMEM;
do
{
sprintf (name, ".nfstmpgnu.%d", n++);
err = netfs_attempt_create_file (cred, dir, name, mode, newnp);
if (err == EEXIST)
- mutex_lock (&dir->lock);
+ mutex_lock (&dir->lock); /* XXX is this right? does create need this
+ and drop this on error? Doesn't look
+ like it. */
}
while (err == EEXIST);
-
+
if (err)
{
free (name);
@@ -695,43 +1145,101 @@ netfs_attempt_mkfile (struct netcred *cred, struct node *dir,
}
/* Implement the netfs_attempt_create_file callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_create_file (struct netcred *cred, struct node *np,
+netfs_attempt_create_file (struct iouser *cred, struct node *np,
char *name, mode_t mode, struct node **newnp)
{
int *p;
void *rpcbuf;
error_t err;
+ uid_t owner;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ /* 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)
+ {
+ mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
- p = nfs_initialize_rpc (NFSPROC_CREATE, 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, owner);
+
err = conduct_rpc (&rpcbuf, &p);
- if (!err)
- err = nfs_error_trans (ntohl (*p++));
mutex_unlock (&np->lock);
if (!err)
{
- *newnp = lookup_fhandle (p);
- p += NFS_FHSIZE / sizeof (int);
- register_fresh_stat (*newnp, p);
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_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);
+ }
+
+ if (*newnp && !netfs_validate_stat (*newnp, (struct iouser *) -1)
+ && (*newnp)->nn_stat.st_uid != owner)
+ netfs_attempt_chown ((struct iouser *) -1, *newnp, owner, (*newnp)->nn_stat.st_gid);
}
else
*newnp = 0;
-
+
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_unlink callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_unlink (struct netcred *cred, struct node *dir,
+netfs_attempt_unlink (struct iouser *cred, struct node *dir,
char *name)
{
int *p;
@@ -747,20 +1255,36 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir,
return err;
}
- /* See if there are any other users of this node than the
+ /* Restore the locks to sanity. */
+ mutex_unlock (&np->lock);
+ mutex_lock (&dir->lock);
+
+ /* Purge the cache of entries for this node, so that we don't
+ regard cache-held references as live. */
+ purge_lookup_cache_node (np);
+
+ /* See if there are any other users of this node than the
one we just got; if so, we must give this file another link
so that when we delete the one we are asked for it doesn't go
away entirely. */
if (np->references > 1)
{
- char *newname;
+ char *newname = 0;
int n = 0;
+ mutex_unlock (&dir->lock);
+
newname = malloc (50);
- mutex_unlock (&np->lock);
+ if (! newname)
+ {
+ mutex_lock (&dir->lock);
+ netfs_nrele (np); /* XXX Is this the correct thing to do? */
+ return ENOMEM;
+ }
+
do
{
- sprintf (newname, ".nfs%xgnu.%d", (int) np, n++);
+ sprintf (newname, ".nfs%txgnu.%d", (ptrdiff_t) np, n++);
err = netfs_attempt_link (cred, dir, np, newname, 1);
}
while (err == EEXIST);
@@ -776,6 +1300,7 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir,
/* Write down what name we gave it; we'll delete this when all
our uses vanish. */
mutex_lock (&np->lock);
+
if (np->nn->dead_dir)
netfs_nrele (np->nn->dead_dir);
netfs_nref (dir);
@@ -785,60 +1310,120 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir,
np->nn->dead_name = newname;
if (np->nn->dtrans == NOT_POSSIBLE)
np->nn->dtrans = POSSIBLE;
+
+ netfs_nput (np);
+ mutex_lock (&dir->lock);
}
- netfs_nput (np);
+ else
+ netfs_nrele (np);
+
+ p = nfs_initialize_rpc (NFSPROC_REMOVE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
- mutex_lock (&dir->lock);
- p = nfs_initialize_rpc (NFSPROC_REMOVE, 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++));
-
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (dir, p, !err);
+ }
+
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_rename callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_rename (struct netcred *cred, struct node *fromdir,
- char *fromname, struct node *todir, char *toname,
+netfs_attempt_rename (struct iouser *cred, struct node *fromdir,
+ char *fromname, struct node *todir, char *toname,
int excl)
{
int *p;
void *rpcbuf;
error_t err;
-
+
if (excl)
- return EOPNOTSUPP; /* XXX */
+ {
+ struct node *np;
+
+ /* Just do a lookup/link/unlink sequence. */
+
+ mutex_lock (&fromdir->lock);
+ err = netfs_attempt_lookup (cred, fromdir, fromname, &np);
+ mutex_unlock (&fromdir->lock);
+ if (err)
+ return err;
+
+ err = netfs_attempt_link (cred, todir, np, toname, 1);
+ netfs_nput (np);
+ if (err)
+ return err;
+
+ mutex_lock (&fromdir->lock);
+ err = netfs_attempt_unlink (cred, fromdir, fromname);
+ mutex_unlock (&fromdir->lock);
+
+ /* If the unlink failed, then back out the link */
+ if (err)
+ {
+ mutex_lock (&todir->lock);
+ netfs_attempt_unlink (cred, todir, toname);
+ mutex_unlock (&todir->lock);
+ return err;
+ }
+
+ return 0;
+ }
mutex_lock (&fromdir->lock);
- p = nfs_initialize_rpc (NFSPROC_RENAME, cred, 0, &rpcbuf, fromdir, -1);
+ purge_lookup_cache (fromdir, fromname, strlen (fromname));
+ p = nfs_initialize_rpc (NFSPROC_RENAME (protocol_version),
+ cred, 0, &rpcbuf, fromdir, -1);
+ if (! p)
+ {
+ mutex_unlock (&fromdir->lock);
+ return errno;
+ }
+
p = xdr_encode_fhandle (p, &fromdir->nn->handle);
p = xdr_encode_string (p, fromname);
mutex_unlock (&fromdir->lock);
-
+
mutex_lock (&todir->lock);
+ purge_lookup_cache (todir, toname, strlen (toname));
p = xdr_encode_fhandle (p, &todir->nn->handle);
p = xdr_encode_string (p, toname);
mutex_unlock (&todir->lock);
-
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3) /* XXX Should we add `&& !err' ? */
+ {
+ mutex_lock (&fromdir->lock);
+ p = process_wcc_stat (fromdir, p, !err);
+ p = process_wcc_stat (todir, p, !err);
+ }
+ }
+
free (rpcbuf);
return err;
}
/* Implement the netfs_attempt_readlink callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_readlink (struct netcred *cred, struct node *np,
+netfs_attempt_readlink (struct iouser *cred, struct node *np,
char *buf)
{
int *p;
@@ -850,105 +1435,132 @@ netfs_attempt_readlink (struct netcred *cred, struct node *np,
strcpy (buf, np->nn->transarg.name);
return 0;
}
-
- p = nfs_initialize_rpc (NFSPROC_READLINK, cred, 0, &rpcbuf, np, -1);
+
+ p = nfs_initialize_rpc (NFSPROC_READLINK (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
p = xdr_encode_fhandle (p, &np->nn->handle);
-
+
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));
+ p++;
+ if (protocol_version == 3)
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ p = xdr_decode_string (p, buf);
+ }
free (rpcbuf);
return err;
}
-/* For an NFS node NODE, guess whether CRED is able to read or write
- it by hoping the server uses permissions bits in the "expected
- way". Return the or of O_READ, O_WRITE, and O_EXEC accordingly as
- each is possible. */
-static int
-guess_mode_use (struct node *np,
- struct netcred *cred)
-{
- error_t err;
-
- err = netfs_validate_stat (np, cred);
- if (err)
- return err;
-
- if (cred_has_uid (cred, 0))
- return O_READ|O_WRITE|O_EXEC;
- else if (cred->nuids == 0
- && (np->nn_stat.st_mode & S_IUSEUNK))
- return
- (((np->nn_stat.st_mode & 04000000) ? O_READ : 0)
- | ((np->nn_stat.st_mode & 02000000) ? O_WRITE : 0)
- | ((np->nn_stat.st_mode & 01000000) ? O_EXEC : 0));
- else if (cred_has_uid (cred, np->nn_stat.st_uid)
- || (cred_has_gid (cred, np->nn_stat.st_gid)
- && cred_has_uid (cred, np->nn_stat.st_gid)))
- /* Owner */
- return
- (((np->nn_stat.st_mode & 0400) ? O_READ : 0)
- | ((np->nn_stat.st_mode & 0200) ? O_WRITE : 0)
- | ((np->nn_stat.st_mode & 0100) ? O_EXEC : 0));
- else if (cred_has_gid (cred, np->nn_stat.st_gid))
- /* Group */
- return
- (((np->nn_stat.st_mode & 040) ? O_READ : 0)
- | ((np->nn_stat.st_mode & 020) ? O_WRITE : 0)
- | ((np->nn_stat.st_mode & 010) ? O_EXEC : 0));
- else
- /* Other */
- return
- (((np->nn_stat.st_mode & 4) ? O_READ : 0)
- | ((np->nn_stat.st_mode & 2) ? O_WRITE : 0)
- | ((np->nn_stat.st_mode & 1) ? O_EXEC : 0));
-}
-
/* Implement the netfs_check_open_permissions callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_check_open_permissions (struct netcred *cred, struct node *np,
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
int flags, int newnode)
{
- if ((flags & (O_READ|O_WRITE|O_EXEC)) == 0)
+ int modes;
+
+ if (newnode || (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;
}
/* Implement the netfs_report_access callback as described in
- <hurd/netfs.h>. */
-void
-netfs_report_access (struct netcred *cred,
+ <hurd/netfs.h>. */
+error_t
+netfs_report_access (struct iouser *cred,
struct node *np,
int *types)
{
- *types = guess_mode_use (np, cred);
-}
+ error_t err;
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ if (protocol_version == 2)
+ {
+ /* Hope the server means the same thing for the bits as we do. */
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+ }
+ else
+ {
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ int ret;
+ int write_check, execute_check;
+
+ 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);
+ if (! p)
+ return errno;
+
+ 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++;
+ p = process_returned_stat (np, p, 0); /* XXX Should this be
+ protected by the
+ if (!err) ? */
+ if (!err)
+ {
+ ret = ntohl (*p);
+ p++;
+ *types = ((ret & ACCESS3_READ ? O_READ : 0)
+ | (ret & write_check ? O_WRITE : 0)
+ | (ret & execute_check ? O_EXEC : 0));
+ }
+ }
+ return err;
+ }
+}
/* These definitions have unfortunate side effects, don't use them,
- clever though they are. */
+ clever though they are. */
#if 0
/* Implement the netfs_check_open_permissions callback as described in
<hurd/netfs.h>. */
error_t
-netfs_check_open_permissions (struct netcred *cred, struct node *np,
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
int flags, int newnode)
{
char byte;
error_t err;
size_t len;
-
+
/* Sun derived nfs client implementations attempt to reproduce the
server's permission restrictions by hoping they look like Unix,
and using that to give errors at open time. Sadly, that loses
@@ -966,17 +1578,17 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np,
&& (flags & O_WRITE) == 0
&& (flags & O_EXEC) == 0)
return 0;
-
+
err = netfs_validate_stat (np, cred);
if (err)
return err;
-
+
switch (np->nn_stat.st_mode & S_IFMT)
{
/* Don't know how to check, so return provisional success. */
default:
return 0;
-
+
case S_IFREG:
len = 1;
err = netfs_attempt_read (cred, np, 0, &len, &byte);
@@ -985,16 +1597,16 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np,
if ((flags & O_READ) || (flags & O_EXEC))
return err;
else
- /* If we couldn't read a byte, but the user wasn't actually asking
+ /* If we couldn't read a byte, but the user wasn't actually asking
for read, then we shouldn't inhibit the open now. */
return 0;
}
-
+
if (len != 1)
/* The file is empty; reads are known to be OK, but writes can't be
tested, so no matter what, return success. */
return 0;
-
+
if (flags & O_WRITE)
{
err = netfs_attempt_write (cred, np, 0, &len, &byte);
@@ -1015,35 +1627,38 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np,
return failure. Otherwise, succeed. */
p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, np, -1);
p = xdr_encode_fhandle (p, &np->nn->handle);
- *p++ = 0;
- *p++ = htonl (50);
+ *(p++) = 0;
+ *(p++) = htonl (50);
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
free (rpcbuf);
-
+
if (err)
return err;
}
return 0;
}
}
-
+
/* Implement the netfs_report_access callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
void
-netfs_report_access (struct netcred *cred,
+netfs_report_access (struct iouser *cred,
struct node *np,
int *types)
{
char byte;
error_t err;
size_t len;
-
+
/* Much the same logic as netfs_check_open_permissions, except that
here we err on the side of denying access, and that we always
have to check everything. */
-
+
*types = 0;
len = 1;
@@ -1080,9 +1695,9 @@ netfs_report_access (struct netcred *cred,
*BUFP to that buffer. *BUFP must be freed by the caller when no
longer needed. If an error occurs, don't touch *BUFP and return
the error code. Set BUFSIZEP to the amount of data used inside
- *BUFP and TOTALENTRIES to the total number of entries copied. */
+ *BUFP and TOTALENTRIES to the total number of entries copied. */
static error_t
-fetch_directory (struct netcred *cred, struct node *dir,
+fetch_directory (struct iouser *cred, struct node *dir,
void **bufp, size_t *bufsizep, int *totalentries)
{
void *buf;
@@ -1097,7 +1712,11 @@ fetch_directory (struct netcred *cred, struct node *dir,
int isnext;
bufmalloced = read_size;
+
buf = malloc (bufmalloced);
+ if (! buf)
+ return ENOMEM;
+
bp = buf;
cookie = 0;
eof = 0;
@@ -1106,66 +1725,82 @@ fetch_directory (struct netcred *cred, struct node *dir,
while (!eof)
{
/* Fetch new directory entries */
- p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, dir, -1);
+ p = nfs_initialize_rpc (NFSPROC_READDIR (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ free (buf);
+ return errno;
+ }
+
p = xdr_encode_fhandle (p, &dir->nn->handle);
- *p++ = cookie;
- *p++ = ntohl (read_size);
+ *(p++) = cookie;
+ *(p++) = ntohl (read_size);
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
if (err)
{
free (buf);
return err;
}
- isnext = ntohl (*p++);
-
+ isnext = ntohl (*p);
+ p++;
+
/* Now copy them one at a time. */
while (isnext)
{
ino_t fileno;
int namlen;
int reclen;
-
- fileno = ntohl (*p++);
- namlen = ntohl (*p++);
+
+ fileno = ntohl (*p);
+ p++;
+ namlen = ntohl (*p);
+ p++;
/* There's a hidden +1 here for the null byte and -1 because d_name
has a size of one already in the sizeof. */
reclen = sizeof (struct dirent) + namlen;
reclen = (reclen + 3) & ~3; /* make it a multiple of four */
-
+
/* Expand buffer if necessary */
if (bp + reclen > buf + bufmalloced)
{
char *newbuf;
-
+
newbuf = realloc (buf, bufmalloced *= 2);
+ assert (newbuf);
if (newbuf != buf)
bp = newbuf + (bp - buf);
buf = newbuf;
}
-
+
/* Fill in new entry */
entry = (struct dirent *) bp;
entry->d_fileno = fileno;
entry->d_reclen = reclen;
entry->d_type = DT_UNKNOWN;
entry->d_namlen = namlen;
- bcopy (p, entry->d_name, namlen);
+ memcpy (entry->d_name, p, namlen);
entry->d_name[namlen] = '\0';
p += INTSIZE (namlen);
bp = bp + entry->d_reclen;
++*totalentries;
-
- cookie = *p++;
- isnext = ntohl (*p++);
+
+ cookie = *(p++);
+ isnext = ntohl (*p);
+ p++;
}
- eof = ntohl (*p++);
+ eof = ntohl (*p);
+ p++;
free (rpcbuf);
}
@@ -1175,11 +1810,11 @@ fetch_directory (struct netcred *cred, struct node *dir,
return 0;
}
-
+
/* Implement the netfs_get_directs callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_get_dirents (struct netcred *cred, struct node *np,
+netfs_get_dirents (struct iouser *cred, struct node *np,
int entry, int nentries, char **data,
mach_msg_type_number_t *datacnt,
vm_size_t bufsiz, int *amt)
@@ -1195,15 +1830,15 @@ netfs_get_dirents (struct netcred *cred, struct node *np,
err = fetch_directory (cred, np, &buf, &our_bufsiz, &totalentries);
if (err)
return err;
-
+
/* Allocate enough space to hold the maximum we might return. */
if (!bufsiz || bufsiz > our_bufsiz)
allocsize = round_page (our_bufsiz);
else
allocsize = round_page (bufsiz);
if (allocsize > *datacnt)
- vm_allocate (mach_task_self (), (vm_address_t *)data, allocsize, 1);
-
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
/* Skip ahead to the correct entry. */
bp = buf;
for (thisentry = 0; thisentry < entry;)
@@ -1212,18 +1847,18 @@ netfs_get_dirents (struct netcred *cred, struct node *np,
bp += entry->d_reclen;
thisentry++;
}
-
+
/* Now copy them one at a time */
{
int entries_copied;
-
- for (entries_copied = 0, userdp = *data;
+
+ for (entries_copied = 0, userdp = *data;
(nentries == -1 || entries_copied < nentries)
&& (!bufsiz || userdp - *data < bufsiz)
&& thisentry < totalentries;)
{
struct dirent *entry = (struct dirent *) bp;
- bcopy (bp, userdp, entry->d_reclen);
+ memcpy (userdp, bp, entry->d_reclen);
bp += entry->d_reclen;
userdp += entry->d_reclen;
entries_copied++;
@@ -1231,45 +1866,34 @@ netfs_get_dirents (struct netcred *cred, struct node *np,
}
*amt = entries_copied;
}
-
+
free (buf);
- /* If we allocated the buffer ourselves, but didn't use
+ /* If we allocated the buffer ourselves, but didn't use
all the pages, free the extra. */
if (allocsize > *datacnt
&& round_page (userdp - *data) < round_page (allocsize))
- vm_deallocate (mach_task_self (), round_page (userdp),
- round_page (allocsize) - round_page (userdp - *data));
+ munmap ((caddr_t) round_page (userdp),
+ round_page (allocsize) - round_page (userdp - *data));
*datacnt = userdp - *data;
return 0;
}
-
-/* Implement the netfs_set_translator callback as described in
- <hurd/netfs.h>. */
-error_t
-netfs_set_translator (struct netcred *cred,
- struct node *np,
- char *argz,
- size_t argzlen)
-{
- return EOPNOTSUPP;
-}
/* Implement the netfs_attempt_mksymlink callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_mksymlink (struct netcred *cred,
+netfs_attempt_mksymlink (struct iouser *cred,
struct node *np,
char *arg)
{
if (np->nn->dtrans == NOT_POSSIBLE)
return EOPNOTSUPP;
-
+
if (np->nn->dtrans == SYMLINK)
free (np->nn->transarg.name);
-
+
np->nn->transarg.name = malloc (strlen (arg) + 1);
strcpy (np->nn->transarg.name, arg);
np->nn->dtrans = SYMLINK;
@@ -1278,19 +1902,19 @@ netfs_attempt_mksymlink (struct netcred *cred,
}
/* Implement the netfs_attempt_mkdev callback as described in
- <hurd/netfs.h>. */
+ <hurd/netfs.h>. */
error_t
-netfs_attempt_mkdev (struct netcred *cred,
+netfs_attempt_mkdev (struct iouser *cred,
struct node *np,
mode_t type,
dev_t indexes)
{
if (np->nn->dtrans == NOT_POSSIBLE)
return EOPNOTSUPP;
-
+
if (np->nn->dtrans == SYMLINK)
free (np->nn->transarg.name);
-
+
np->nn->transarg.indexes = indexes;
if (type == S_IFBLK)
np->nn->dtrans = BLKDEV;
@@ -1299,5 +1923,3 @@ netfs_attempt_mkdev (struct netcred *cred,
np->nn->stat_updated = 0;
return 0;
}
-
-