--- util-linux-2.12q/mount/Makefile +++ util-linux-2.12q/mount/Makefile @@ -29,7 +29,7 @@ MAYBE = pivot_root swapoff -NFS_OBJS = nfsmount.o nfsmount_xdr.o nfsmount_clnt.o +NFS_OBJS = nfsmount.o nfsmount_xdr.o nfsmount_clnt.o nfs4mount.o GEN_FILES = nfsmount.h nfsmount_xdr.c nfsmount_clnt.c all: $(PROGS) --- util-linux-2.12q/mount/mount.c +++ util-linux-2.12q/mount/mount.c @@ -847,8 +847,23 @@ "without support for the type `nfs'")); #endif } +#ifdef HAVE_NFS + /* + * NFSv4 support + */ + if (!fake && types && streq (types, "nfs4")) { + mnt_err = nfs4mount(spec, node, &flags, &extra_opts, &mount_opts, bg); + if (mnt_err) + return mnt_err; + goto nosigblock; +#else + die (EX_SOFTWARE, _("mount: this version was compiled " + "without support for the type `nfs4'")); +#endif + } block_signals (SIG_BLOCK); +nosigblock: if (!fake) { mnt5_res = guess_fstype_and_mount (spec, node, &types, flags & ~MS_NOSYS, --- util-linux-2.12q/mount/nfs4mount.c +++ util-linux-2.12q/mount/nfs4mount.c @@ -0,0 +1,460 @@ +/* + * nfs4mount.c -- Linux NFS mount + * Copyright (C) 2002 Trond Myklebust + * + * This program 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. + * + * This program 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. + * + * Note: this file based on the original nfsmount.c + */ + +#include "../defines.h" /* for HAVE_rpcsvc_nfs_prot_h and HAVE_inet_aton */ + +#include +#include +#undef __FD_CLR +#undef __FD_SET +#undef __FD_ISSET +#undef __FD_ZERO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_rpcsvc_nfs_prot_h +#include +#else +#include +#define nfsstat nfs_stat +#endif + +#include "sundries.h" + +#include "mount_constants.h" +#include "nfs4_mount.h" + +#include "nls.h" + +#ifndef NFS_PORT +#define NFS_PORT 2049 +#endif + +extern int clnt_ping(struct sockaddr_in *, const u_long, const u_long, const u_int); +extern void rpc_strerror(void); + +struct { + char *flavour; + int fnum; +} flav_map[] = { + { "krb5", RPC_AUTH_GSS_KRB5 }, + { "krb5i", RPC_AUTH_GSS_KRB5I }, + { "krb5p", RPC_AUTH_GSS_KRB5P }, + { "lipkey", RPC_AUTH_GSS_LKEY }, + { "lipkey-i", RPC_AUTH_GSS_LKEYI }, + { "lipkey-p", RPC_AUTH_GSS_LKEYP }, + { "spkm3", RPC_AUTH_GSS_SPKM }, + { "spkm3i", RPC_AUTH_GSS_SPKMI }, + { "spkm3p", RPC_AUTH_GSS_SPKMP }, + { "unix", AUTH_UNIX }, + { "sys", AUTH_SYS }, + { "null", AUTH_NULL }, + { "none", AUTH_NONE }, +}; + +#define FMAPSIZE (sizeof(flav_map)/sizeof(flav_map[0])) +#define MAX_USER_FLAVOUR 16 + +static int parse_sec(char *sec, int *pseudoflavour) +{ + int i, num_flavour = 0; + + for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) { + if (num_flavour >= MAX_USER_FLAVOUR) { + fprintf(stderr, + _("mount: maximum number of security flavors " + "exceeded\n")); + return 0; + } + for (i = 0; i < FMAPSIZE; i++) { + if (strcmp(sec, flav_map[i].flavour) == 0) { + pseudoflavour[num_flavour++] = flav_map[i].fnum; + break; + } + } + if (i == FMAPSIZE) { + fprintf(stderr, + _("mount: unknown security type %s\n"), sec); + return 0; + } + } + if (!num_flavour) + fprintf(stderr, + _("mount: no security flavors passed to sec= option\n")); + return num_flavour; +} + +static int parse_devname(char *hostdir, char **hostname, char **dirname) +{ + char *s; + + if (!(s = strchr(hostdir, ':'))) { + fprintf(stderr, + _("mount: " + "directory to mount not in host:dir format\n")); + return -1; + } + *hostname = hostdir; + *dirname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + if ((s = strchr(hostdir, ','))) { + *s = '\0'; + fprintf(stderr, + _("mount: warning: " + "multiple hostnames not supported\n")); + } + return 0; +} + +static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr) +{ + struct hostent *hp; + addr->sin_family = AF_INET; + + if (inet_aton(hostname, &addr->sin_addr)) + return 0; + if ((hp = gethostbyname(hostname)) == NULL) { + fprintf(stderr, _("mount: can't get address for %s\n"), + hostname); + return -1; + } + if (hp->h_length > sizeof(struct in_addr)) { + fprintf(stderr, + _("mount: got bad hp->h_length\n")); + hp->h_length = sizeof(struct in_addr); + } + memcpy(&addr->sin_addr, hp->h_addr, hp->h_length); + return 0; +} + +static int get_my_ipv4addr(char *ip_addr, int len) +{ + char myname[1024]; + struct sockaddr_in myaddr; + + if (gethostname(myname, sizeof(myname))) { + fprintf(stderr, _("mount: can't determine client address\n")); + return -1; + } + if (fill_ipv4_sockaddr(myname, &myaddr)) + return -1; + snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr)); + ip_addr[len-1] = '\0'; + return 0; +} + +/* Convert RPC errors into strings */ +void rpc_strerror(void) +{ + int cf_stat = rpc_createerr.cf_stat; + int cf_errno = rpc_createerr.cf_error.re_errno; + char *ptr, *estr = clnt_sperrno(cf_stat); + + if (estr) { + if ((ptr = index(estr, ':'))) + estr = ++ptr; + + fprintf(stderr, "RPC Error: %d (%s )\n", cf_stat, estr); + if (cf_stat == RPC_SYSTEMERROR) + fprintf(stderr, "System Error: %d (%s)\n", cf_errno, strerror(cf_errno)); + } +} + +int +clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers, + const u_int prot) +{ + static const struct timeval TIMEOUT = { 20, 0 }; + static const struct timeval RETRY_TIMEOUT = { 3, 0 }; + CLIENT *clnt=NULL; + int sock, stat; + static char clnt_res; + + rpc_createerr.cf_stat = stat = 0; + sock = RPC_ANYSOCK; + switch(prot) { + case IPPROTO_UDP: + clnt = clntudp_bufcreate(saddr, prog, vers, + RETRY_TIMEOUT, &sock, + RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + break; + case IPPROTO_TCP: + clnt = clnttcp_create(saddr, prog, vers, &sock, + RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + break; + default: + goto out_bad; + } + if (!clnt) + goto out_bad; + memset(&clnt_res, 0, sizeof(clnt_res)); + stat = clnt_call(clnt, NULLPROC, + (xdrproc_t)xdr_void, (caddr_t)NULL, + (xdrproc_t)xdr_void, (caddr_t)&clnt_res, + TIMEOUT); + clnt_destroy(clnt); + close(sock); + if (stat != RPC_PROGVERSMISMATCH) + return 1; + + out_bad: + return 0; +} + + +int nfs4mount(const char *spec, const char *node, int *flags, + char **extra_opts, char **mount_opts, + int running_bg) +{ + static struct nfs4_mount_data data; + static char hostdir[1024]; + static char ip_addr[16] = "127.0.0.1"; + static struct sockaddr_in server_addr; + static int pseudoflavour[MAX_USER_FLAVOUR]; + int num_flavour = 0; + + char *hostname, *dirname, *old_opts; + char new_opts[1024]; + char *opt, *opteq; + char *s; + int val; + int bg, soft, intr; + int nocto, noac; + int retry; + int retval; + + retval = EX_FAIL; + if (strlen(spec) >= sizeof(hostdir)) { + fprintf(stderr, _("mount: " + "excessively long host:dir argument\n")); + goto fail; + } + strcpy(hostdir, spec); + if (parse_devname(hostdir, &hostname, &dirname)) + goto fail; + + if (fill_ipv4_sockaddr(hostname, &server_addr)) + goto fail; + if (get_my_ipv4addr(ip_addr, sizeof(ip_addr))) + goto fail; + + /* add IP address to mtab options for use when unmounting */ + s = inet_ntoa(server_addr.sin_addr); + old_opts = *extra_opts; + if (!old_opts) + old_opts = ""; + if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) { + fprintf(stderr, _("mount: " + "excessively long option argument\n")); + goto fail; + } + snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s", + old_opts, *old_opts ? "," : "", s); + *extra_opts = xstrdup(new_opts); + + /* Set default options. + * rsize/wsize and timeo are left 0 in order to + * let the kernel decide. + */ + memset(&data, 0, sizeof(data)); + data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; + data.proto = IPPROTO_TCP; + + bg = 0; + soft = 0; + intr = NFS4_MOUNT_INTR; + nocto = 0; + noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ + + /* + * NFSv4 specifies that the default port should be 2049 + */ + server_addr.sin_port = htons(NFS_PORT); + + /* parse options */ + + for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { + if ((opteq = strchr(opt, '='))) { + val = atoi(opteq + 1); + *opteq = '\0'; + if (!strcmp(opt, "rsize")) + data.rsize = val; + else if (!strcmp(opt, "wsize")) + data.wsize = val; + else if (!strcmp(opt, "timeo")) + data.timeo = val; + else if (!strcmp(opt, "retrans")) + data.retrans = val; + else if (!strcmp(opt, "acregmin")) + data.acregmin = val; + else if (!strcmp(opt, "acregmax")) + data.acregmax = val; + else if (!strcmp(opt, "acdirmin")) + data.acdirmin = val; + else if (!strcmp(opt, "acdirmax")) + data.acdirmax = val; + else if (!strcmp(opt, "actimeo")) { + data.acregmin = val; + data.acregmax = val; + data.acdirmin = val; + data.acdirmax = val; + } + else if (!strcmp(opt, "retry")) + retry = val; + else if (!strcmp(opt, "port")) + server_addr.sin_port = htons(val); + else if (!strcmp(opt, "proto")) { + if (!strncmp(opteq+1, "tcp", 3)) + data.proto = IPPROTO_TCP; + else if (!strncmp(opteq+1, "udp", 3)) + data.proto = IPPROTO_UDP; + else + printf(_("Warning: Unrecognized proto= option.\n")); + } else if (!strcmp(opt, "clientaddr")) { + if (strlen(opteq+1) >= sizeof(ip_addr)) + printf(_("Invalid client address %s"), + opteq+1); + strncpy(ip_addr,opteq+1, sizeof(ip_addr)); + ip_addr[sizeof(ip_addr)-1] = '\0'; + } else if (!strcmp(opt, "sec")) { + num_flavour = parse_sec(opteq+1, pseudoflavour); + if (!num_flavour) + goto fail; + } else if (!strcmp(opt, "addr")) { + /* ignore */; + } else { + printf(_("unknown nfs mount parameter: " + "%s=%d\n"), opt, val); + goto fail; + } + } else { + val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + if (!strcmp(opt, "bg")) + bg = val; + else if (!strcmp(opt, "fg")) + bg = !val; + else if (!strcmp(opt, "soft")) + soft = val; + else if (!strcmp(opt, "hard")) + soft = !val; + else if (!strcmp(opt, "intr")) + intr = val; + else if (!strcmp(opt, "cto")) + nocto = !val; + else if (!strcmp(opt, "ac")) + noac = !val; + else { + if (!sloppy) { + printf(_("unknown nfs mount option: " + "%s%s\n"), val ? "" : "no", opt); + goto fail; + } + } + } + } + + data.flags = (soft ? NFS4_MOUNT_SOFT : 0) + | (intr ? NFS4_MOUNT_INTR : 0) + | (nocto ? NFS4_MOUNT_NOCTO : 0) + | (noac ? NFS4_MOUNT_NOAC : 0); + + if (num_flavour == 0) + pseudoflavour[num_flavour++] = AUTH_UNIX; + data.auth_flavourlen = num_flavour; + data.auth_flavours = pseudoflavour; + + data.client_addr.data = ip_addr; + data.client_addr.len = strlen(ip_addr); + + data.mnt_path.data = dirname; + data.mnt_path.len = strlen(dirname); + + data.hostname.data = hostname; + data.hostname.len = strlen(hostname); + data.host_addr = (struct sockaddr *)&server_addr; + data.host_addrlen = sizeof(server_addr); + +#ifdef NFS_MOUNT_DEBUG + printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", + data.rsize, data.wsize, data.timeo, data.retrans); + printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", + data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); + printf("port = %d, bg = %d, retry = %d, flags = %.8x\n", + ntohs(server_addr.sin_port), bg, retry, data.flags); + printf("soft = %d, intr = %d, nocto = %d, noac = %d\n", + (data.flags & NFS4_MOUNT_SOFT) != 0, + (data.flags & NFS4_MOUNT_INTR) != 0, + (data.flags & NFS4_MOUNT_NOCTO) != 0, + (data.flags & NFS4_MOUNT_NOAC) != 0); + + if (num_flavour > 0) { + int pf_cnt, i; + + printf("sec = "); + for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) { + for (i = 0; i < FMAPSIZE; i++) { + if (flav_map[i].fnum == pseudoflavour[pf_cnt]) { + printf("%s", flav_map[i].flavour); + break; + } + } + printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n"); + } + } + printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp"); +#endif + + data.version = NFS4_MOUNT_VERSION; + + clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto); + if (rpc_createerr.cf_stat) { + fprintf(stderr, "mount to NFS server '%s' failed.\n", data.hostname.data); + goto fail; + } + + *mount_opts = (char *) &data; + /* clean up */ + return 0; + +fail: + if (verbose) { + rpc_strerror(); + } + return retval; +} --- util-linux-2.12q/mount/nfs4_mount.h +++ util-linux-2.12q/mount/nfs4_mount.h @@ -0,0 +1,82 @@ +#ifndef _LINUX_NFS4_MOUNT_H +#define _LINUX_NFS4_MOUNT_H + +/* + * linux/include/linux/nfs4_mount.h + * + * Copyright (C) 2002 Trond Myklebust + * + * structure passed from user-space to kernel-space during an nfsv4 mount + */ + +/* + * WARNING! Do not delete or change the order of these fields. If + * a new field is required then add it to the end. The version field + * tracks which fields are present. This will ensure some measure of + * mount-to-kernel version compatibility. Some of these aren't used yet + * but here they are anyway. + */ +#define NFS4_MOUNT_VERSION 1 + +struct nfs_string { + unsigned int len; + const char* data; +}; + +struct nfs4_mount_data { + int version; /* 1 */ + int flags; /* 1 */ + int rsize; /* 1 */ + int wsize; /* 1 */ + int timeo; /* 1 */ + int retrans; /* 1 */ + int acregmin; /* 1 */ + int acregmax; /* 1 */ + int acdirmin; /* 1 */ + int acdirmax; /* 1 */ + + /* see the definition of 'struct clientaddr4' in RFC3010 */ + struct nfs_string client_addr; /* 1 */ + + /* Mount path */ + struct nfs_string mnt_path; /* 1 */ + + /* Server details */ + struct nfs_string hostname; /* 1 */ + /* Server IP address */ + unsigned int host_addrlen; /* 1 */ + struct sockaddr* host_addr; /* 1 */ + + /* Transport protocol to use */ + int proto; /* 1 */ + + /* Pseudo-flavours to use for authentication. See RFC2623 */ + int auth_flavourlen; /* 1 */ + int *auth_flavours; /* 1 */ +}; + +/* bits in the flags field */ +/* Note: the fields that correspond to existing NFSv2/v3 mount options + * should mirror the values from include/linux/nfs_mount.h + */ + +#define NFS4_MOUNT_SOFT 0x0001 /* 1 */ +#define NFS4_MOUNT_INTR 0x0002 /* 1 */ +#define NFS4_MOUNT_NOCTO 0x0010 /* 1 */ +#define NFS4_MOUNT_NOAC 0x0020 /* 1 */ +#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */ +#define NFS4_MOUNT_FLAGMASK 0xFFFF + +/* pseudoflavors: */ + +#define RPC_AUTH_GSS_KRB5 390003 +#define RPC_AUTH_GSS_KRB5I 390004 +#define RPC_AUTH_GSS_KRB5P 390005 +#define RPC_AUTH_GSS_LKEY 390006 +#define RPC_AUTH_GSS_LKEYI 390007 +#define RPC_AUTH_GSS_LKEYP 390008 +#define RPC_AUTH_GSS_SPKM 390009 +#define RPC_AUTH_GSS_SPKMI 390010 +#define RPC_AUTH_GSS_SPKMP 390011 + +#endif --- util-linux-2.12q/mount/nfs_mount4.h +++ util-linux-2.12q/mount/nfs_mount4.h @@ -8,7 +8,9 @@ * so it is easiest to ignore the kernel altogether (at compile time). */ -#define NFS_MOUNT_VERSION 4 +#define NFS_MOUNT_VERSION 6 +#define NFS_MAX_CONTEXT_LEN 256 + struct nfs2_fh { char data[32]; @@ -36,6 +38,9 @@ int namlen; /* 2 */ unsigned int bsize; /* 3 */ struct nfs3_fh root; /* 4 */ + int pseudoflavor; /* 5 */ + char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */ + }; /* bits in the flags field */ @@ -52,3 +57,19 @@ #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */ #define NFS_MOUNT_NOACL 0x0800 /* 4 */ +#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */ + +/* security pseudoflavors */ + +#ifndef AUTH_GSS_KRB5 +#define AUTH_GSS_KRB5 390003 +#define AUTH_GSS_KRB5I 390004 +#define AUTH_GSS_KRB5P 390005 +#define AUTH_GSS_LKEY 390006 +#define AUTH_GSS_LKEYI 390007 +#define AUTH_GSS_LKEYP 390008 +#define AUTH_GSS_SPKM 390009 +#define AUTH_GSS_SPKMI 390010 +#define AUTH_GSS_SPKMP 390011 +#endif +