summaryrefslogtreecommitdiff
path: root/nfsd/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'nfsd/ops.c')
-rw-r--r--nfsd/ops.c652
1 files changed, 652 insertions, 0 deletions
diff --git a/nfsd/ops.c b/nfsd/ops.c
new file mode 100644
index 00000000..93c8373c
--- /dev/null
+++ b/nfsd/ops.c
@@ -0,0 +1,652 @@
+/* NFS daemon protocol operations
+ Copyright (C) 1996 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 <hurd/io.h>
+#include <hurd/fs.h>
+#include <fcntl.h>
+#include <hurd/paths.h>
+#include <hurd.h>
+#include <dirent.h>
+
+#include "nfsd.h"
+#include "../nfs/rpcsvc/mount.h" /* XXX */
+#include <rpc/pmap_prot.h>
+
+static error_t
+op_null (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ return 0;
+}
+
+static error_t
+op_getattr (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ struct stat st;
+ error_t err;
+
+ err = io_stat (c->port, &st);
+ if (!err)
+ *reply = encode_fattr (*reply, &st);
+ return err;
+}
+
+static error_t
+complete_setattr (mach_port_t port,
+ int *p)
+{
+ uid_t uid, gid;
+ off_t size;
+ time_value_t atime, mtime;
+ struct stat st;
+ error_t err;
+
+ err = io_stat (port, &st);
+ if (err)
+ return err;
+
+ uid = ntohl (*p++);
+ gid = ntohl (*p++);
+ if ((uid != -1 && uid != st.st_uid)
+ || (gid != -1 && gid != st.st_gid))
+ {
+ if (uid == -1)
+ uid = st.st_uid;
+ if (gid == -1)
+ gid = st.st_gid;
+ err = file_chown (port, uid, gid);
+ if (err)
+ return err;
+ }
+
+ size = ntohl (*p++);
+ if (size != -1 && size != st.st_size)
+ err = file_set_size (port, size);
+ if (err)
+ return err;
+
+ atime.seconds = ntohl (*p++);
+ atime.microseconds = ntohl (*p++);
+ mtime.seconds = ntohl (*p++);
+ mtime.microseconds = ntohl (*p++);
+ if (atime.seconds != -1 && atime.microseconds == -1)
+ atime.microseconds = 0;
+ if (mtime.seconds != -1 && mtime.microseconds == -1)
+ mtime.microseconds = 0;
+ if (atime.seconds != -1 || mtime.seconds != -1
+ || atime.microseconds != -1 || mtime.microseconds != -1)
+ {
+ if (atime.seconds == -1)
+ atime.seconds = st.st_atime;
+ if (atime.microseconds == -1)
+ atime.microseconds = st.st_atime_usec;
+ if (mtime.seconds == -1)
+ mtime.seconds = st.st_mtime;
+ if (mtime.microseconds == -1)
+ mtime.microseconds = st.st_mtime_usec;
+ err = file_utimes (port, atime, mtime);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static error_t
+op_setattr (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err = 0;
+ mode_t mode;
+
+ mode = ntohl (*p++);
+ if (mode != -1)
+ err = file_chmod (c->port, mode);
+ if (err)
+ return err;
+
+ return complete_setattr (c->port, p);
+}
+
+static error_t
+op_lookup (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+
+ decode_name (p, &name);
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry, retry_name,
+ &newport);
+ free (name);
+
+ /* Block attempts to bounce out of this filesystem by any technique */
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (!err)
+ err = io_stat (newport, &st);
+
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_readlink (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char buf[2048], *transp = buf;
+ mach_msg_type_number_t len = sizeof (buf);
+ error_t err;
+
+ /* Shamelessly copied from the libc readlink */
+ err = file_get_translator (c->port, &transp, &len);
+ if (err)
+ return err;
+
+ if (len < sizeof (_HURD_SYMLINK)
+ || memcmp (transp, _HURD_SYMLINK, sizeof (_HURD_SYMLINK)))
+ return EINVAL;
+
+ transp += sizeof (_HURD_SYMLINK);
+
+ *reply = encode_string (*reply, transp);
+ return 0;
+}
+
+static size_t
+count_read_buffersize (int *p)
+{
+ return ntohl (*++p); /* skip OFFSET, return COUNT */
+}
+
+static error_t
+op_read (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ off_t offset;
+ size_t count;
+ char buf[2048], *bp = buf;
+ mach_msg_type_number_t buflen = sizeof (buf);
+ struct stat st;
+ error_t err;
+
+ offset = ntohl (*p++);
+ count = ntohl (*p++);
+
+ err = io_read (c->port, &bp, &buflen, offset, count);
+ if (err)
+ return err;
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+
+ *reply = encode_fattr (*reply, &st);
+ *reply = encode_data (*reply, bp, buflen);
+ return 0;
+}
+
+static error_t
+op_write (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ off_t offset;
+ size_t count;
+ error_t err;
+ mach_msg_type_number_t amt;
+ char *bp;
+ struct stat st;
+
+ p++;
+ offset = ntohl (*p++);
+ p++;
+ count = ntohl (*p++);
+ bp = (char *) *reply;
+
+ while (count)
+ {
+ err = io_write (c->port, bp, count, offset, &amt);
+ if (err)
+ return err;
+ if (amt == 0)
+ return EIO;
+ count -= amt;
+ bp += amt;
+ offset += amt;
+ }
+
+ file_sync (c->port, 1, 0);
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_create (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+ mode_t mode;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p++);
+
+ err = dir_lookup (c->port, name, O_NOTRANS | O_CREAT | O_EXCL, mode,
+ &do_retry, retry_name, &newport);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (err)
+ return err;
+
+ err = complete_setattr (newport, p);
+ if (!err)
+ err = io_stat (newport, &st);
+
+ if (err)
+ {
+ dir_unlink (c->port, name);
+ free (name);
+ return err;
+ }
+ free (name);
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_remove (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+
+ decode_name (p, &name);
+
+ err = dir_unlink (c->port, name);
+ free (name);
+
+ return 0;
+}
+
+static error_t
+op_rename (struct cache_handle *fromc,
+ int *p,
+ int **reply)
+{
+ struct cache_handle *toc;
+ char *fromname, *toname;
+ error_t err = 0;
+
+ p = decode_name (p, &fromname);
+ p = lookup_cache_handle (p, &toc, fromc->ids);
+ decode_name (p, &toname);
+
+ if (!toc)
+ err = ESTALE;
+ if (!err)
+ err = dir_rename (fromc->port, fromname, toc->port, toname, 0);
+ free (fromname);
+ free (toname);
+ return err;
+}
+
+static error_t
+op_link (struct cache_handle *filec,
+ int *p,
+ int **reply)
+{
+ struct cache_handle *dirc;
+ char *name;
+ error_t err = 0;
+
+ p = lookup_cache_handle (p, &dirc, filec->ids);
+ decode_name (p, &name);
+
+ if (!dirc)
+ err = ESTALE;
+ if (!err)
+ err = dir_link (dirc->port, filec->port, name, 1);
+
+ free (name);
+ return err;
+}
+
+static error_t
+op_symlink (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name, *target;
+ error_t err;
+ mode_t mode;
+ file_t newport = MACH_PORT_NULL;
+ size_t len;
+ char *buf;
+
+ p = decode_name (p, &name);
+ p = decode_name (p, &target);
+ mode = ntohl (*p++);
+ if (mode == -1)
+ mode = 0777;
+
+ len = strlen (target) + 1;
+ buf = alloca (sizeof (_HURD_SYMLINK) + len);
+ memcpy (buf, _HURD_SYMLINK, sizeof (_HURD_SYMLINK));
+ memcpy (buf + sizeof (_HURD_SYMLINK), target, len);
+
+ err = dir_mkfile (c->port, O_WRITE, mode, &newport);
+ if (!err)
+ err = file_set_translator (newport,
+ FS_TRANS_EXCL|FS_TRANS_SET,
+ FS_TRANS_EXCL|FS_TRANS_SET, 0,
+ buf, sizeof (_HURD_SYMLINK) + len,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+ if (!err)
+ err = dir_link (c->port, newport, name, 1);
+
+ free (name);
+ free (target);
+
+ if (newport != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), newport);
+ return err;
+}
+
+static error_t
+op_mkdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name;
+ mode_t mode;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct stat st;
+ struct cache_handle *newc;
+ error_t err;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p++);
+
+ err = dir_mkdir (c->port, name, mode);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry,
+ retry_name, &newport);
+ free (name);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+ if (err)
+ return err;
+
+ err = complete_setattr (newport, p);
+ if (!err)
+ err = io_stat (newport, &st);
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_rmdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name;
+ error_t err;
+
+ decode_name (p, &name);
+
+ err = dir_rmdir (c->port, name);
+ free (name);
+ return err;
+}
+
+static error_t
+op_readdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ int cookie;
+ unsigned count;
+ error_t err;
+ char *buf;
+ struct dirent *dp;
+ size_t bufsize;
+ int nentries;
+ int i;
+ int *replystart;
+
+ cookie = ntohl (*p++);
+ count = ntohl (*p++);
+
+ buf = alloca (count);
+ bufsize = count;
+ err = dir_readdir (c->port, &buf, &bufsize, cookie, -1, count, &nentries);
+ if (err)
+ return err;
+
+ if (nentries == 0)
+ {
+ *(*reply)++ = htonl (0); /* no entry */
+ *(*reply)++ = htonl (1); /* EOF */
+ }
+ else
+ {
+ for (i = 0, dp = (struct dirent *) buf, replystart = *reply;
+ ((char *)dp < buf + bufsize
+ && i < nentries
+ && (char *)reply < (char *)replystart + count);
+ i++, dp = (struct dirent *) ((char *)dp + dp->d_reclen))
+ {
+ *(*reply)++ = htonl (1); /* entry present */
+ *(*reply)++ = htonl (dp->d_ino);
+ *reply = encode_string (*reply, dp->d_name);
+ *(*reply)++ = htonl (i + cookie + 1); /* next entry */
+ }
+ *(*reply)++ = htonl (0); /* not EOF */
+ }
+
+ return 0;
+}
+
+static size_t
+count_readdir_buffersize (int *p)
+{
+ return ntohl (*++p); /* skip COOKIE; return COUNT */
+}
+
+static error_t
+op_statfs (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ struct statfs st;
+ error_t err;
+
+ err = file_statfs (c->port, &st);
+ if (!err)
+ *reply = encode_statfs (*reply, &st);
+ return err;
+}
+
+static error_t
+op_mnt (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ file_t root;
+ struct cache_handle *newc;
+ char *name;
+
+ decode_name (p, &name);
+
+ root = file_name_lookup (name, 0, 0);
+ if (!root)
+ {
+ free (name);
+ return errno;
+ }
+
+ newc = create_cached_handle (enter_filesystem (name, root), c, root);
+ free (name);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ return 0;
+}
+
+static error_t
+op_getport (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ int prog, vers, prot;
+
+ prog = ntohl (*p++);
+ vers = ntohl (*p++);
+ prot = ntohl (*p++);
+
+ if (prot != IPPROTO_UDP)
+ *(*reply)++ = htonl (0);
+ else if ((prog == MOUNTPROG && vers == MOUNTVERS)
+ || (prog == NFS_PROGRAM && vers == NFS_VERSION))
+ *(*reply)++ = htonl (NFS_PORT);
+ else if (prog == PMAPPROG && vers == PMAPVERS)
+ *(*reply)++ = htonl (PMAPPORT);
+ else
+ *(*reply)++ = 0;
+
+ return 0;
+}
+
+
+struct proctable nfstable =
+{
+ NFSPROC_NULL, /* first proc */
+ NFSPROC_STATFS, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { op_getattr, 0, 1, 1},
+ { op_setattr, 0, 1, 1},
+ { 0, 0, 0, 0 }, /* deprecated NFSPROC_ROOT */
+ { op_lookup, 0, 1, 1},
+ { op_readlink, 0, 1, 1},
+ { op_read, count_read_buffersize, 1, 1},
+ { 0, 0, 0, 0 }, /* nonexistent NFSPROC_WRITECACHE */
+ { op_write, 0, 1, 1},
+ { op_create, 0, 1, 1},
+ { op_remove, 0, 1, 1},
+ { op_rename, 0, 1, 1},
+ { op_link, 0, 1, 1},
+ { op_symlink, 0, 1, 1},
+ { op_mkdir, 0, 1, 1},
+ { op_rmdir, 0, 1, 1},
+ { op_readdir, count_readdir_buffersize, 1, 1},
+ { op_statfs, 0, 1, 1},
+ }
+};
+
+
+struct proctable mounttable =
+{
+ MOUNTPROC_NULL, /* first proc */
+ MOUNTPROC_EXPORT, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { op_mnt, 0, 0, 1},
+ { 0, 0, 0, 0}, /* MOUNTPROC_DUMP */
+ { 0, 0, 0, 0}, /* MOUNTPROC_UMNT */
+ { 0, 0, 0, 0}, /* MOUNTPROC_UMNTALL */
+ { 0, 0, 0, 0}, /* MOUNTPROC_EXPORT */
+ }
+};
+
+struct proctable pmaptable =
+{
+ PMAPPROC_NULL, /* first proc */
+ PMAPPROC_CALLIT, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_SET */
+ { 0, 0, 0, 0}, /* PMAPPROC_UNSET */
+ { op_getport, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_DUMP */
+ { 0, 0, 0, 0}, /* PMAPPROC_CALLIT */
+ }
+};