Added initial unfs3 sources for version 0.9.22+dfsg-1maemo2
[unfs3] / unfs3 / mount.c
diff --git a/unfs3/mount.c b/unfs3/mount.c
new file mode 100644 (file)
index 0000000..d14956e
--- /dev/null
@@ -0,0 +1,289 @@
+
+/*
+ * UNFS3 mount protocol procedures
+ * (C) 2004, Pascal Schmidt
+ * see file LICENSE for license details
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN32
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif                                /* WIN32 */
+#include <fcntl.h>
+
+#include "nfs.h"
+#include "mount.h"
+#include "daemon.h"
+#include "fh.h"
+#include "fh_cache.h"
+#include "fd_cache.h"
+#include "Config/exports.h"
+#include "password.h"
+#include "backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX      4096
+#endif
+
+#define IS_SECURE(port) ((port) < 1024)
+
+/*
+ * number of active mounts
+ *
+ * only a guess since clients can crash and/or not sent UMNT calls
+ */
+static int mount_cnt = 0;
+
+/* list of currently mounted directories */
+static mountlist mount_list = NULL;
+
+static char nonce[32] = "";
+
+/*
+ * add entry to mount list
+ */
+static void add_mount(const char *path, struct svc_req *rqstp)
+{
+    mountlist new;
+    mountlist iter;
+    char *host;
+
+    new = malloc(sizeof(struct mountbody));
+    if (!new) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       return;
+    }
+
+    host = inet_ntoa(get_remote(rqstp));
+    new->ml_hostname = malloc(strlen(host) + 1);
+    if (!new->ml_hostname) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       free(new);
+       return;
+    }
+
+    new->ml_directory = malloc(strlen(path) + 1);
+    if (!new->ml_directory) {
+       logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
+       free(new->ml_hostname);
+       free(new);
+       return;
+    }
+
+    /* initialize the new entry */
+    new->ml_next = NULL;
+    strcpy(new->ml_hostname, host);
+    strcpy(new->ml_directory, path);
+
+    iter = mount_list;
+    if (iter) {
+       while (iter->ml_next)
+           iter = iter->ml_next;
+       iter->ml_next = new;
+    } else
+       mount_list = new;
+
+    mount_cnt++;
+}
+
+/*
+ * remove entries from mount list
+ */
+static void remove_mount(const char *path, struct svc_req *rqstp)
+{
+    mountlist iter, next, prev = NULL;
+    char *host;
+
+    host = inet_ntoa(get_remote(rqstp));
+
+    iter = mount_list;
+    while (iter) {
+       if (strcmp(iter->ml_hostname, host) == 0 &&
+           (!path || strcmp(iter->ml_directory, path) == 0)) {
+           if (prev)
+               prev->ml_next = iter->ml_next;
+           else
+               mount_list = iter->ml_next;
+
+           next = iter->ml_next;
+
+           free(iter->ml_hostname);
+           free(iter->ml_directory);
+           free(iter);
+
+           iter = next;
+
+           /* adjust mount count */
+           if (mount_cnt > 0)
+               mount_cnt--;
+       } else {
+           prev = iter;
+           iter = iter->ml_next;
+       }
+    }
+}
+
+void *mountproc_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    static void *result = NULL;
+
+    return &result;
+}
+
+mountres3 *mountproc_mnt_3_svc(dirpath * argp, struct svc_req * rqstp)
+{
+    char buf[PATH_MAX];
+    static unfs3_fh_t fh;
+    static mountres3 result;
+    static int auth = AUTH_UNIX;
+    int authenticated = 0;
+    char *password;
+
+    /* We need to modify the *argp pointer. Make a copy. */
+    char *dpath = *argp;
+
+    /* error out if not version 3 */
+    if (rqstp->rq_vers != 3) {
+       logmsg(LOG_INFO,
+              "%s attempted mount with unsupported protocol version",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_INVAL;
+       return &result;
+    }
+
+    /* Check for "mount commands" */
+    if (strncmp(dpath, "@getnonce", sizeof("@getnonce") - 1) == 0) {
+       if (backend_gen_nonce(nonce) < 0) {
+           result.fhs_status = MNT3ERR_IO;
+       } else {
+           result.fhs_status = MNT3_OK;
+           result.mountres3_u.mountinfo.fhandle.fhandle3_len = 32;
+           result.mountres3_u.mountinfo.fhandle.fhandle3_val = nonce;
+           result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
+           result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
+               &auth;
+       }
+       return &result;
+    } else if (strncmp(dpath, "@password:", sizeof("@password:") - 1) == 0) {
+       char pw[PASSWORD_MAXLEN + 1];
+
+       mnt_cmd_argument(&dpath, "@password:", pw, PASSWORD_MAXLEN);
+       if (exports_options(dpath, rqstp, &password, NULL) != -1) {
+           authenticated = !strcmp(password, pw);
+       }
+       /* else leave authenticated unchanged */
+    } else if (strncmp(dpath, "@otp:", sizeof("@otp:") - 1) == 0) {
+       /* The otp from the client */
+       char otp[PASSWORD_MAXLEN + 1];
+
+       /* Our calculated otp */
+       char hexdigest[32];
+
+       mnt_cmd_argument(&dpath, "@otp:", otp, PASSWORD_MAXLEN);
+       if (exports_options(dpath, rqstp, &password, NULL) != -1) {
+           otp_digest(nonce, password, hexdigest);
+
+           /* Compare our calculated digest with what the client submitted */
+           authenticated = !strncmp(hexdigest, otp, 32);
+
+           /* Change nonce */
+           backend_gen_nonce(nonce);
+       }
+       /* else leave authenticated unchanged */
+    }
+
+    if ((exports_opts & OPT_REMOVABLE) && export_point(dpath)) {
+       /* Removable media export point. Do not call realpath; simply copy
+          path */
+       strncpy(buf, dpath, PATH_MAX);
+    } else if (!backend_realpath(dpath, buf)) {
+       /* the given path does not exist */
+       result.fhs_status = MNT3ERR_NOENT;
+       return &result;
+    }
+
+    if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
+       logmsg(LOG_INFO, "%s attempted to mount jumbo path",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_NAMETOOLONG;
+       return &result;
+    }
+
+    if ((exports_options(buf, rqstp, &password, NULL) == -1)
+       || (!authenticated && password[0])
+       || (!(exports_opts & OPT_INSECURE) &&
+           !IS_SECURE(ntohs(get_port(rqstp))))
+       ) {
+       /* not exported to this host or at all, or a password defined and not 
+          authenticated */
+       result.fhs_status = MNT3ERR_ACCES;
+       return &result;
+    }
+
+    fh = fh_comp(buf, rqstp, FH_DIR);
+
+    if (!fh_valid(fh)) {
+       logmsg(LOG_INFO, "%s attempted to mount non-directory",
+              inet_ntoa(get_remote(rqstp)));
+       result.fhs_status = MNT3ERR_NOTDIR;
+       return &result;
+    }
+
+    add_mount(dpath, rqstp);
+
+    result.fhs_status = MNT3_OK;
+    result.mountres3_u.mountinfo.fhandle.fhandle3_len = fh_length(&fh);
+    result.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *) &fh;
+    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1;
+    result.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &auth;
+
+    return &result;
+}
+
+mountlist *mountproc_dump_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    return &mount_list;
+}
+
+void *mountproc_umnt_3_svc(dirpath * argp, struct svc_req *rqstp)
+{
+    /* RPC times out if we use a NULL pointer */
+    static void *result = NULL;
+
+    remove_mount(*argp, rqstp);
+
+    /* if no more mounts are active, flush all open file descriptors */
+    if (mount_cnt == 0)
+       fd_cache_purge();
+
+    return &result;
+}
+
+void *mountproc_umntall_3_svc(U(void *argp), struct svc_req *rqstp)
+{
+    /* RPC times out if we use a NULL pointer */
+    static void *result = NULL;
+
+    remove_mount(NULL, rqstp);
+
+    /* if no more mounts are active, flush all open file descriptors */
+    if (mount_cnt == 0)
+       fd_cache_purge();
+
+    return &result;
+}
+
+exports *mountproc_export_3_svc(U(void *argp), U(struct svc_req *rqstp))
+{
+    return &exports_nfslist;
+}