summaryrefslogtreecommitdiff
path: root/nfs/ops.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1996-03-19 18:59:34 +0000
committerMichael I. Bushnell <mib@gnu.org>1996-03-19 18:59:34 +0000
commit80c937736e978a94dd3193fa2af9b2c0b2ad9c04 (patch)
treef90060caf10f2f4fa3e828a7bb0297c6e9cd167f /nfs/ops.c
parent3bd51e2e4bbbe978424d1d4492a54d56cee22bca (diff)
*** empty log message ***
Diffstat (limited to 'nfs/ops.c')
-rw-r--r--nfs/ops.c1225
1 files changed, 1183 insertions, 42 deletions
diff --git a/nfs/ops.c b/nfs/ops.c
index 824affe7..07963f87 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -1,5 +1,5 @@
-/*
- Copyright (C) 1994 Free Software Foundation
+/* Libnetfs callbacks for node operations in NFS client
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -15,62 +15,1203 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+#include "nfs.h"
+#include <hurd/netfs.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.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. */
+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;
+
+ switch (np->nn->dtrans)
+ {
+ 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);
+ break;
+
+ case BLKDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFBLK);
+ break;
+
+ case FIFO:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFIFO);
+ break;
+
+ case SOCK:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFSOCK);
+ break;
+ }
+
+ np->nn_stat.st_fsid = getpid ();
+ np->nn_stat.st_fstype = FSTYPE_NFS;
+ np->nn_stat.st_gen = 0;
+ np->nn_stat.st_author = np->nn_stat.st_uid;
+ np->nn_stat.st_flags = 0;
+
+ return ret;
+}
/* Implement the netfs_validate_stat callback as described in
<hurd/netfs.h>. */
error_t
-netfs_validate_stat (struct node *np, struct protid *cred)
+netfs_validate_stat (struct node *np, struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*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>. */
+error_t
+netfs_attempt_chown (struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_ids (p, uid, gid);
+
+ err = conduct_rpc (&rpcbuf, &p);
+
+ if (!err)
+ register_fresh_stat (np, p);
+
+ 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,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_chmod callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chmod (struct netcred *cred, struct node *np,
+ mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if ((mode & S_IFMT) != 0)
+ {
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+ 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;
+ }
+ if (f)
+ free (f);
+ return 0;
+ }
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_mode (p, mode);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ register_fresh_stat (np, p);
+
+ 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,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_utimes callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_utimes (struct netcred *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);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_times (p, atime, mtime);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ register_fresh_stat (np, p);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_set_size callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_set_size (struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_size (p, size);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ register_fresh_stat (np, p);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_statfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_statfs (struct netcred *cred, struct node *np,
+ struct fsys_statfsbuf *st)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ {
+ st->fsys_stb_iosize = *p++;
+ st->fsys_stb_bsize = *p++;
+ st->fsys_stb_blocks = *p++;
+ st->fsys_stb_bfree = *p++;
+ st->fsys_stb_bavail = *p++;
+
+ st->fsys_stb_type = FSTYPE_NFS;
+ st->fsys_stb_files = 0;
+ st->fsys_stb_ffree = 0;
+ st->fsys_stb_fsid = 0; /* XXX wrong */
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_sync callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_sync (struct netcred *cred, struct node *np, int wait)
+{
+ /* We are already completely synchronous. */
+ return 0;
+}
+
+/* Implement the netfs_attempt_syncfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_syncfs (struct netcred *cred, int wait)
+{
+ return 0;
+}
+
+/* Implement the netfs_attempt_read callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_read (struct netcred *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ size_t trans_len;
+ error_t err;
+ size_t amt, thisamt;
+
+ 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 = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (offset);
+ *p++ = htonl (thisamt);
+ *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; /* ??? */
+
+ 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;
+ }
+ }
+ return 0;
+}
+
+/* Implement the netfs_attempt_write callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_write (struct netcred *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (amt > write_size)
+ amt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE, cred, amt, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = 0;
+ *p++ = htonl (offset);
+ *p++ = 0;
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (err)
+ {
+ *len = 0;
+ free (rpcbuf);
+ return err;
+ }
+ register_fresh_stat (np, p);
+ free (rpcbuf);
+ amt -= thisamt;
+ data += thisamt;
+ offset += thisamt;
+ }
+ return 0;
+}
+
+/* Implement the netfs_attempt_lookup callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_lookup (struct netcred *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);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ 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);
+ }
+ else
+ *newnp = 0;
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_mkdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdir (struct netcred *cred, struct node *np,
+ char *name, mode_t mode)
{
- struct vfsv2_fattr *fattr;
- size_t hsize;
- char *rpc;
- struct nfsv2_attrstat *reply;
+ int *p;
+ void *rpcbuf;
error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_MKDIR, 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);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ /* Ignore returned information */
+ /* XXX should probably cache it */
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_rmdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rmdir (struct netcred *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? */
- /* The only arg is the file handle. */
+ p = nfs_initialize_rpc (NFSPROC_RMDIR, cred, 0, &rpcbuf, np, -1);
+ 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++));
+
+ free (rpcbuf);
+ return err;
+}
- hsize = rpc_compute_header_size (cred->nc.auth);
- rpc = alloca (hsize + sizeof (nfsv2_fhandle_t));
+/* Implement the netfs_attempt_link callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_link (struct netcred *cred, struct node *dir,
+ struct node *np, char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err = 0;
- /* Fill in request arg */
- bcopy (&np->nn.handle, rpc + hsize, NFSV2_FHSIZE);
+ /* 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
+ doing a normal link. */
+
+ switch (np->nn->dtrans)
+ {
+ case POSSIBLE:
+ case NOT_POSSIBLE:
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_LINK, cred, 0, &rpcbuf, dir, -1);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ mutex_unlock (&np->lock);
- /* Transmit */
- err = nfs_rpc_send (NFSV2_GETATTR, rpc, hsize + NFSV2_FHSIZE,
- &reply, sizeof (struct nfsv2_attrstat));
+ mutex_lock (&dir->lock);
+ 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);
+
+ free (rpcbuf);
+
+ break;
+
+ case SYMLINK:
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_SYMLINK, cred, 0, &rpcbuf, dir, -1);
+ mutex_unlock (&dir->lock);
+
+ p = xdr_encode_string (p, name);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ 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++));
+
+ 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);
+
+ if (err)
+ err = EGRATUITOUS; /* damn */
+ else
+ {
+ mutex_lock (&np->lock);
+ recache_handle (np, p);
+ p += NFS_FHSIZE / sizeof (int);
+ register_fresh_stat (np, p);
+ mutex_unlock (&np->lock);
+ }
+ }
+ else
+ mutex_unlock (&dir->lock);
+ 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)
+ {
+ 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);
+ 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);
+
+ break;
+ }
+
if (err)
return err;
- xdr_to_native_attrstat (reply);
+ mutex_lock (&np->lock);
- if (reply->status != NFSV2_OK)
- err = nfsv2err_to_hurderr (reply->status);
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ np->nn->dtrans = NOT_POSSIBLE;
+
+ /* If there is a dead-dir tag lying around, it's time to delete it now. */
+ if (np->nn->dead_dir)
+ {
+ struct node *dir = np->nn->dead_dir;
+ char *name = np->nn->dead_name;
+ 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);
+ mutex_unlock (&dir->lock);
+ }
+ else
+ mutex_unlock (&np->lock);
+
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkfile callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkfile (struct netcred *cred, struct node *dir,
+ mode_t mode, struct node **newnp)
+{
+ error_t err;
+ char *name;
+ static int n = 0;
+
+ /* This is the best we can do. */
+
+ name = malloc (50);
+
+ do
+ {
+ sprintf (name, ".nfstmpgnu.%d", n++);
+ err = netfs_attempt_create_file (cred, dir, name, mode, newnp);
+ if (err == EEXIST)
+ mutex_lock (&dir->lock);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ assert (!(*newnp)->nn->dead_dir);
+ assert (!(*newnp)->nn->dead_name);
+ netfs_nref (dir);
+ (*newnp)->nn->dead_dir = dir;
+ (*newnp)->nn->dead_name = name;
+ if ((*newnp)->nn->dtrans == NOT_POSSIBLE)
+ (*newnp)->nn->dtrans = POSSIBLE;
+ return 0;
+}
+
+/* Implement the netfs_attempt_create_file callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_create_file (struct netcred *cred, struct node *np,
+ char *name, mode_t mode, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ 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);
+
+ 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);
+ }
else
+ *newnp = 0;
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_unlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_unlink (struct netcred *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct node *np;
+
+ /* First lookup the node being removed */
+ err = netfs_attempt_lookup (cred, dir, name, &np);
+ if (err)
+ {
+ mutex_lock (&dir->lock);
+ return err;
+ }
+
+ /* 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;
+ int n = 0;
+
+ newname = malloc (50);
+ mutex_unlock (&np->lock);
+ do
+ {
+ sprintf (newname, ".nfs%xgnu.%d", (int) np, n++);
+ err = netfs_attempt_link (cred, dir, np, newname);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (newname);
+ mutex_lock (&dir->lock);
+ netfs_nrele (np);
+ return err;
+ }
+
+ /* 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);
+ np->nn->dead_dir = dir;
+ if (np->nn->dead_name)
+ free (np->nn->dead_name);
+ np->nn->dead_name = newname;
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ np->nn->dtrans = POSSIBLE;
+ }
+ netfs_nput (np);
+
+ 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++));
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_rename callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rename (struct netcred *cred, struct node *fromdir,
+ char *fromname, struct node *todir, char *toname)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ mutex_lock (&fromdir->lock);
+ p = nfs_initialize_rpc (NFSPROC_RENAME, cred, 0, &rpcbuf, fromdir, -1);
+ p = xdr_encode_fhandle (p, &fromdir->nn->handle);
+ p = xdr_encode_string (p, fromname);
+ mutex_unlock (&fromdir->lock);
+
+ mutex_lock (&todir->lock);
+ 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++));
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_readlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_readlink (struct netcred *cred, struct node *np,
+ char *buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (np->nn->dtrans == SYMLINK)
{
- np->nn_stat.st_mode = nfsv2mode_to_hurdmode (reply->attributes.type,
- reply->attributes.mode);
- np->nn_stat.st_nlink = reply->attributes.nlink;
- np->nn_stat.st_uid = reply->attributes.uid;
- np->nn_stat.st_gid = reply->attributes.gid;
- np->nn_stat.st_size = reply->attributes.size;
- np->nn_stat.st_blksize = reply->attributes.blocksize;
- np->nn_stat.st_rdev = reply->attributes.rdev;
- np->nn_stat.st_blocks = reply->attributes.blocks;
- np->nn_stat.st_fstype = FSTYPE_NFS;
- np->nn_stat.st_fsid = reply->attributes.fsid; /* surely wrong XXX */
- np->nn_stat.st_fileid = reply->attributes.fileid;
- np->nn_stat.st_atime = reply->attributes.atime.seconds;
- np->nn_stat.st_atime_usec = reply->attributes.atime.useconds;
- np->nn_stat.st_mtime = reply->attributes.mtime.seconds;
- np->nn_stat.st_mtime_usec = reply->attributes.mtime.useconds;
- np->nn_stat.st_ctime = reply->attributes.ctime.seconds;
- np->nn_stat.st_ctime_usec = reply->attributes.ctime.useconds;
- /* Deal with other values appropriately? */
-
- err = 0;
- }
-
- deallocate_reply_buffer (reply);
+ strcpy (buf, np->nn->transarg.name);
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_READLINK, cred, 0, &rpcbuf, np, -1);
+ 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);
+
+ free (rpcbuf);
return err;
}
+/* 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,
+ 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
+ here. So instead what we do is try and do what the user
+ requested; if we can't, then we fail. Otherwise, we allow the
+ open, but acknowledge that the server might still give an error
+ later. (Even with our check, the server can revoke access, thus
+ violiting Posix semantics; this means that erring on the side of
+ permitting illegal opens won't harm otherwise correct programs,
+ because they need to deal with revocation anyway.) We thus here
+ have the advantage of working correctly with servers that allow
+ things Unix denies. */
+
+ if ((flags & O_READ) == 0
+ && (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);
+ if (err)
+ {
+ if ((flags & O_READ) || (flags & O_EXEC))
+ return err;
+ else
+ /* 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);
+ return err;
+ }
+
+ /* Try as we might, we couldn't get the server to bump us, so
+ give (provisional) success. */
+ return 0;
+
+ case S_IFDIR:
+ if (flags & O_READ)
+ {
+ void *rpcbuf;
+ int *p;
+
+ /* Issue a readdir request; if it fails, then we can
+ 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);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ free (rpcbuf);
+
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+void
+netfs_report_access (struct netcred *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. */
+
+
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ {
+ *types = 0;
+ return;
+ }
+
+ *types = O_READ | O_EXEC;
+
+ if (len == 1)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ }
+ else
+ {
+ /* Oh, ugh. We have to try and write a byte and then truncate
+ back. God help us if the file becomes unwritable in-between.
+ But because of the use of this function (by setuid programs
+ wanting to see if they should write user's files) we must
+ check this and not just return a presumptive error. */
+ byte = 0;
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ netfs_attempt_set_size (cred, np, 0);
+ }
+}
+
+/* Fetch the complete contents of DIR into a buffer of directs. Set
+ *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. */
+static error_t
+fetch_directory (struct netcred *cred, struct node *dir,
+ void **bufp, size_t *bufsizep, int *totalentries)
+{
+ void *buf;
+ int cookie;
+ int *p;
+ void *rpcbuf;
+ struct dirent *entry;
+ void *bp;
+ int bufmalloced;
+ int eof;
+ error_t err;
+ int isnext;
+
+ bufmalloced = read_size;
+ buf = malloc (bufmalloced);
+ bp = buf;
+ cookie = 0;
+ eof = 0;
+ *totalentries = 0;
+
+ while (!eof)
+ {
+ /* Fetch new directory entries */
+ p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ *p++ = cookie;
+ *p++ = ntohl (read_size);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ free (buf);
+ return err;
+ }
+
+ isnext = ntohl (*p++);
+
+ /* Now copy them one at a time. */
+ while (isnext)
+ {
+ ino_t fileno;
+ int namlen;
+ int reclen;
+
+ fileno = ntohl (*p++);
+ namlen = ntohl (*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);
+ 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);
+ entry->d_name[namlen] = '\0';
+
+ p += INTSIZE (namlen);
+ bp = bp + entry->d_reclen;
+
+ ++*totalentries;
+
+ cookie = *p++;
+ isnext = ntohl (*p++);
+ }
+
+ eof = ntohl (*p++);
+ free (rpcbuf);
+ }
+
+ /* Return it all to the user */
+ *bufp = buf;
+ *bufsizep = bufmalloced;
+ return 0;
+}
+
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct netcred *cred, struct node *np,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ void *buf;
+ size_t our_bufsiz, allocsize;
+ void *bp;
+ char *userdp;
+ error_t err;
+ int totalentries;
+ int thisentry;
+
+ 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);
+
+ /* Skip ahead to the correct entry. */
+ bp = buf;
+ for (thisentry = 0; thisentry < entry;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ bp += entry->d_reclen;
+ thisentry++;
+ }
+
+ /* Now copy them one at a time */
+ {
+ int entries_copied;
+
+ 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);
+ bp += entry->d_reclen;
+ userdp += entry->d_reclen;
+ entries_copied++;
+ thisentry++;
+ }
+ *amt = entries_copied;
+ }
+
+ free (buf);
+
+ /* 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));
+
+ *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>. */
+error_t
+netfs_attempt_mksymlink (struct netcred *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;
+ np->nn->stat_updated = 0;
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkdev callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdev (struct netcred *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;
+ else
+ np->nn->dtrans = CHRDEV;
+ np->nn->stat_updated = 0;
+ return 0;
+}
+
+