aboutsummaryrefslogtreecommitdiff
path: root/nfs/nfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'nfs/nfs.c')
-rw-r--r--nfs/nfs.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/nfs/nfs.c b/nfs/nfs.c
new file mode 100644
index 00000000..46c95c22
--- /dev/null
+++ b/nfs/nfs.c
@@ -0,0 +1,645 @@
+/* XDR frobbing and lower level routines for NFS client
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return it. */
+mode_t
+nfs_mode_to_hurd_mode (int type, int mode)
+{
+ int hurdmode;
+
+ switch (type)
+ {
+ case NFDIR:
+ hurdmode = S_IFDIR;
+ break;
+
+ case NFCHR:
+ hurdmode = S_IFCHR;
+ break;
+
+ case NFBLK:
+ hurdmode = S_IFBLK;
+ break;
+
+ case NFREG:
+ hurdmode = S_IFREG;
+ break;
+
+ case NFLNK:
+ hurdmode = S_IFLNK;
+ break;
+
+ case NFSOCK:
+ hurdmode = S_IFSOCK;
+ break;
+
+ default:
+ if (protocol_version == 2)
+ switch (type)
+ {
+ case NF2NON:
+ case NF2BAD:
+ default:
+ hurdmode = S_IFREG;
+ break;
+
+ case NF2FIFO:
+ hurdmode = S_IFIFO;
+ break;
+ }
+ else
+ switch (type)
+ {
+ case NF3FIFO:
+ hurdmode = S_IFIFO;
+ break;
+
+ default:
+ hurdmode = S_IFREG;
+ break;
+ }
+ break;
+ }
+
+ hurdmode |= mode & ~NFSMODE_FMT;
+ return hurdmode;
+}
+
+/* Convert a Hurd mode to an NFS mode */
+int
+hurd_mode_to_nfs_mode (mode_t mode)
+{
+ /* This function is used only for chmod; just trim the bits that NFS
+ doesn't support. */
+ return mode & 07777;
+}
+
+/* Convert a Hurd mode to an NFS type */
+int
+hurd_mode_to_nfs_type (mode_t mode)
+{
+ switch (mode & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ default:
+ return NFCHR;
+
+ case S_IFBLK:
+ return NFBLK;
+
+ case S_IFREG:
+ return NFREG;
+
+ case S_IFLNK:
+ return NFLNK;
+
+ case S_IFSOCK:
+ return NFSOCK;
+
+ case S_IFIFO:
+ return protocol_version == 2 ? NF2FIFO : NF3FIFO;
+ }
+}
+
+
+
+/* Each of the functions on this page copies its second arg to *P,
+ converting it to XDR representation along the way. They then
+ return the address after the copied value. */
+
+/* Encode an NFS file handle. */
+int *
+xdr_encode_fhandle (int *p, struct fhandle *fhandle)
+{
+ if (protocol_version == 2)
+ {
+ bcopy (fhandle->data, p, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_FHSIZE);
+ }
+ else
+ return xdr_encode_data (p, fhandle->data, fhandle->size);
+}
+
+/* Encode uninterpreted bytes. */
+int *
+xdr_encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *p++ = htonl (len);
+ bcopy (data, p, len);
+ return p + nints;
+}
+
+/* Encode a 64 bit integer */
+int *
+xdr_encode_64bit (int *p, long long n)
+{
+ *p++ = htonl (n & 0xffffffff00000000LL >> 32);
+ *p++ = htonl (n & 0xffffffff);
+ return p;
+}
+
+/* Encode a C string. */
+int *
+xdr_encode_string (int *p, char *string)
+{
+ return xdr_encode_data (p, string, strlen (string));
+}
+
+/* Encode a MODE into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_mode (int *p, mode_t mode)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = -1; /* size */
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime usecs */
+ }
+ else
+ {
+ *p++ = htonl (1); /* set mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = 0; /* no size */
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode UID and GID into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_ids (int *p, u_int uid, u_int gid)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = htonl (uid);
+ *p++ = htonl (gid);
+ *p++ = -1; /* size */
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime usecs */
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (uid);
+ *p++ = htonl (1); /* set gid */
+ *p++ = htonl (gid);
+ *p++ = 0; /* no size */
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode a file size into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_size (int *p, off_t size)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = htonl (size);
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime secs */
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = htonl (1); /* size */
+ p = xdr_encode_64bit (p, size);
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode ATIME and MTIME into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = -1; /* size */
+ *p++ = htonl (atime->tv_sec);
+ *p++ = htonl (atime->tv_nsec / 1000);
+ *p++ = htonl (mtime->tv_sec);
+ *p++ = htonl (mtime->tv_nsec / 1000);
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = 0; /* no size */
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* atime */
+ *p++ = htonl (atime->tv_sec);
+ *p++ = htonl (atime->tv_nsec);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* mtime */
+ *p++ = htonl (mtime->tv_sec);
+ *p++ = htonl (mtime->tv_nsec);
+ }
+ return p;
+}
+
+/* Encode MODE, a size of zero, and the specified owner into an otherwise
+ empty sattr. */
+int *
+xdr_encode_create_state (int *p,
+ mode_t mode,
+ uid_t owner)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = htonl (owner); /* uid */
+ *p++ = -1; /* gid */
+ *p++ = 0; /* size */
+ *p++ = -1; /* atime sec */
+ *p++ = -1; /* atime usec */
+ *p++ = -1; /* mtime sec */
+ *p++ = -1; /* mtime usec */
+ }
+ else
+ {
+ *p++ = htonl (1); /* mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (owner);
+ *p++ = 0; /* no gid */
+ *p++ = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, 0);
+ *p++ = htonl (SET_TO_SERVER_TIME); /* atime */
+ *p++ = htonl (SET_TO_SERVER_TIME); /* mtime */
+ }
+ return p;
+}
+
+/* Encode ST into an sattr. */
+int *
+xdr_encode_sattr_stat (int *p,
+ struct stat *st)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *p++ = htonl (st->st_uid);
+ *p++ = htonl (st->st_gid);
+ *p++ = htonl (st->st_size);
+ *p++ = htonl (st->st_atime);
+ *p++ = htonl (st->st_atime_usec);
+ *p++ = htonl (st->st_mtime);
+ *p++ = htonl (st->st_mtime_usec);
+ }
+ else
+ {
+ *p++ = htonl (1); /* set mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (st->st_uid);
+ *p++ = htonl (1); /* set gid */
+ *p++ = htonl (st->st_gid);
+ *p++ = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, st->st_size);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* set atime */
+ *p++ = htonl (st->st_atime);
+ *p++ = htonl (st->st_atime_usec * 1000);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* set mtime */
+ *p++ = htonl (st->st_mtime);
+ *p++ = htonl (st->st_mtime_usec * 1000);
+ }
+ return p;
+}
+
+
+/* Decode *P into a long long; return the address of the following data. */
+int *
+xdr_decode_64bit (int *p, long long *n)
+{
+ long long high, low;
+ high = ntohl (*p++);
+ low = ntohl (*p++);
+ *n = ((high & 0xffffffff) << 32) | (low & 0xffffffff);
+ return p;
+}
+
+/* Decode *P into an fhandle and look up the associated node. Return
+ the address of the following data. */
+int *
+xdr_decode_fhandle (int *p, struct node **npp)
+{
+ size_t len;
+
+ len = protocol_version == 2 ? NFS2_FHSIZE : ntohl (*p++);
+ lookup_fhandle (p, len, npp);
+ return p + len / sizeof (int);
+}
+
+/* Decode *P into a stat structure; return the address of the
+ following data. */
+int *
+xdr_decode_fattr (int *p, struct stat *st)
+{
+ int type, mode;
+
+ type = ntohl (*p++);
+ mode = ntohl (*p++);
+ st->st_mode = nfs_mode_to_hurd_mode (type, mode);
+ st->st_nlink = ntohl (*p++);
+ st->st_uid = ntohl (*p++);
+ st->st_gid = ntohl (*p++);
+ if (protocol_version == 2)
+ {
+ st->st_size = ntohl (*p++);
+ st->st_blksize = ntohl (*p++);
+ st->st_rdev = ntohl (*p++);
+ st->st_blocks = ntohl (*p++);
+ }
+ else
+ {
+ long long size;
+ int major, minor;
+ p = xdr_decode_64bit (p, &size);
+ st->st_size = size;
+ p = xdr_decode_64bit (p, &size);
+ st->st_blocks = size / 512;
+ st->st_blksize = read_size < write_size ? read_size : write_size;
+ major = ntohl (*p++);
+ minor = ntohl (*p++);
+ st->st_rdev = makedev (major, minor);
+ }
+ st->st_fsid = ntohl (*p++);
+ st->st_ino = ntohl (*p++);
+ st->st_atime = ntohl (*p++);
+ st->st_atime_usec = ntohl (*p++);
+ st->st_mtime = ntohl (*p++);
+ st->st_mtime_usec = ntohl (*p++);
+ st->st_ctime = ntohl (*p++);
+ st->st_ctime_usec = ntohl (*p++);
+
+ if (protocol_version == 3)
+ {
+ st->st_atime_usec /= 1000;
+ st->st_mtime_usec /= 1000;
+ st->st_ctime_usec /= 1000;
+ }
+
+ return p;
+
+}
+
+/* Decode *P into a string, stored at BUF; return the address
+ of the following data. */
+int *
+xdr_decode_string (int *p, char *buf)
+{
+ int len;
+
+ len = ntohl (*p++);
+ bcopy (p, buf, len);
+ buf[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+
+/* Set up an RPC for procedure RPC_PROC for talking to the NFS server.
+ Allocate storage with malloc and point *BUFP at it; caller must
+ free this when done. Initialize RPC credential information with
+ information from CRED (identifying the user making this call; -1
+ means superuser), NP (identifying the node we are operating on), and
+ SECOND_GID (specifying another GID the server might be interested
+ in). Allocate at least LEN bytes of space for bulk data in
+ addition to the normal amount for an RPC. */
+int *
+nfs_initialize_rpc (int rpc_proc, struct iouser *cred,
+ size_t len, void **bufp, struct node *np,
+ uid_t second_gid)
+{
+ uid_t uid;
+ uid_t gid;
+ error_t err;
+
+ /* Use heuristics to figure out what ids to present to the server.
+ Don't lie, but adjust ids as necessary to secure the desired result. */
+
+ if (cred == (struct iouser *) -1)
+ {
+ uid = gid = 0;
+ second_gid = -1;
+ }
+ else if (cred
+ && (cred->uids->num || cred->gids->num))
+ {
+ if (idvec_contains (cred->uids, 0))
+ {
+ err = netfs_validate_stat (np, 0);
+ uid = 0;
+ gid = err ? -2 : 0;
+ if (err)
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (cred->uids->num == 0)
+ uid = -2;
+ else if (cred->uids->num == 1)
+ uid = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ uid = cred->uids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->uids, np->nn_stat.st_uid))
+ uid = np->nn_stat.st_uid;
+ else
+ uid = cred->uids->ids[0];
+ }
+ }
+
+ if (cred->gids->num == 0)
+ {
+ gid = -2;
+ second_gid = -1;
+ }
+ else if (cred->gids->num == 1)
+ {
+ gid = cred->gids->ids[0];
+ second_gid = -1;
+ }
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ gid = cred->gids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->gids, np->nn_stat.st_gid))
+ gid = np->nn_stat.st_gid;
+ else
+ gid = cred->gids->ids[0];
+ }
+ if (second_gid != -1
+ && !idvec_contains (cred->gids, second_gid))
+ second_gid = -1;
+ }
+ }
+ }
+ else
+ uid = gid = second_gid = -1;
+
+ return initialize_rpc (NFS_PROGRAM, NFS_VERSION, rpc_proc, len, bufp,
+ uid, gid, second_gid);
+}
+
+/* ERROR is an NFS error code; return the correspending Hurd error. */
+error_t
+nfs_error_trans (int error)
+{
+ switch (error)
+ {
+ case NFS_OK:
+ return 0;
+
+ case NFSERR_PERM:
+ return EPERM;
+
+ case NFSERR_NOENT:
+ return ENOENT;
+
+ case NFSERR_IO:
+ return EIO;
+
+ case NFSERR_NXIO:
+ return ENXIO;
+
+ case NFSERR_ACCES:
+ return EACCES;
+
+ case NFSERR_EXIST:
+ return EEXIST;
+
+ case NFSERR_NODEV:
+ return ENODEV;
+
+ case NFSERR_NOTDIR:
+ return ENOTDIR;
+
+ case NFSERR_ISDIR:
+ return EISDIR;
+
+ case NFSERR_FBIG:
+ return E2BIG;
+
+ case NFSERR_NOSPC:
+ return ENOSPC;
+
+ case NFSERR_ROFS:
+ return EROFS;
+
+ case NFSERR_NAMETOOLONG:
+ return ENAMETOOLONG;
+
+ case NFSERR_NOTEMPTY:
+ return ENOTEMPTY;
+
+ case NFSERR_DQUOT:
+ return EDQUOT;
+
+ case NFSERR_STALE:
+ return ESTALE;
+
+ case NFSERR_WFLUSH:
+ /* Not known in v3, but we just give EINVAL for unknown errors
+ so it's the same. */
+ return EINVAL;
+
+ default:
+ if (protocol_version == 2)
+ return EINVAL;
+ else
+ switch (error)
+ {
+ case NFSERR_XDEV:
+ return EXDEV;
+
+ case NFSERR_INVAL:
+ case NFSERR_REMOTE: /* not sure about this one */
+ default:
+ return EINVAL;
+
+ case NFSERR_MLINK:
+ return EMLINK;
+
+ case NFSERR_NOTSUPP:
+ case NFSERR_BADTYPE:
+ return EOPNOTSUPP;
+
+ case NFSERR_SERVERFAULT:
+ return EIO;
+
+ case NFSERR_BADHANDLE:
+ case NFSERR_NOT_SYNC:
+ case NFSERR_BAD_COOKIE:
+ case NFSERR_TOOSMALL:
+ case NFSERR_JUKEBOX: /* ??? */
+ /* These indicate bugs in the client, so EGRATUITOUS is right. */
+ return EGRATUITOUS;
+ }
+ }
+}