From: Arkadiusz Miƛkiewicz Date: Fri, 27 May 2005 14:37:41 +0000 (+0000) Subject: - nfs4 patches X-Git-Tag: auto/ac/util-linux-2_12q-3~3 X-Git-Url: http://git.pld-linux.org/?p=packages%2Futil-linux.git;a=commitdiff_plain;h=a5f7e22352c0852b361026dae52e9d0d927bcd9a - nfs4 patches Changed files: util-linux-2.12-01-nfs.dif -> 1.1 util-linux-2.12-02-base-nfsv4.dif -> 1.1 util-linux-2.12-03-krb5.dif -> 1.1 util-linux-2.12-04-modify_mount_to_support_multiple_security_flavors.dif -> 1.1 util-linux-2.12-05-mount_sloppy_fix.dif -> 1.1 util-linux-2.12-06-redhat-nfs-mount.dif -> 1.1 --- diff --git a/util-linux-2.12-01-nfs.dif b/util-linux-2.12-01-nfs.dif new file mode 100644 index 0000000..fb10ed8 --- /dev/null +++ b/util-linux-2.12-01-nfs.dif @@ -0,0 +1,1504 @@ + +Miscellaneous nfs mount changes from Trond. + +--- + + util-linux-2.12-bfields/mount/Makefile | 4 + util-linux-2.12-bfields/mount/nfsmount.c | 1235 +++++++++++++++++++------------ + util-linux-2.12-bfields/mount/sundries.h | 1 + util-linux-2.12-bfields/mount/umount.c | 4 + 4 files changed, 794 insertions(+), 450 deletions(-) + +diff -puN mount/Makefile~nfs mount/Makefile +--- util-linux-2.12/mount/Makefile~nfs 2004-10-13 13:51:44.000000000 -0400 ++++ util-linux-2.12-bfields/mount/Makefile 2004-10-13 14:13:30.000000000 -0400 +@@ -31,7 +31,7 @@ + + LO_OBJS = lomount.o $(LIB)/xstrncpy.o + CRYPT_OBJS = cryptsetup.o -lcryptsetup +-NFS_OBJS = nfsmount.o nfsmount_xdr.o nfsmount_clnt.o ++NFS_OBJS = nfsmount.o nfsmount_xdr.o + GEN_FILES = nfsmount.h nfsmount_xdr.c nfsmount_clnt.c + + all: $(PROGS) +@@ -55,7 +55,7 @@ + + umount: umount.o fstab.o sundries.o xmalloc.o realpath.o mntent.o \ + getusername.o get_label_uuid.o mount_by_label.o mount_blkid.o \ +- version.o $(LIB)/env.o $(LO_OBJS) $(CRYPT_OBJS) ++ version.o $(LIB)/env.o $(NFS_OBJS) $(LO_OBJS) $(CRYPT_OBJS) + $(LINK) $^ -o $@ $(BLKID_LIB) + + swapon: swapon.o version.o xmalloc.o \ +diff -puN mount/nfsmount.c~nfs mount/nfsmount.c +--- util-linux-2.12/mount/nfsmount.c~nfs 2004-10-13 13:51:44.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfsmount.c 2004-10-13 14:13:30.000000000 -0400 +@@ -34,6 +34,7 @@ + + #include "../defines.h" /* for HAVE_rpcsvc_nfs_prot_h and HAVE_inet_aton */ + ++#include + #include + #include + #include +@@ -72,11 +73,100 @@ + #define NFS_FHSIZE 32 + #endif + ++#define MNT_SENDBUFSIZE ((u_int)2048) ++#define MNT_RECVBUFSIZE ((u_int)1024) ++ + static char *nfs_strerror(int stat); + + #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r)) + + #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) ++#define MAX_MNTPROT ((nfs_mount_version >= 4) ? 3 : 2) ++#define HAVE_RELIABLE_TCP (nfs_mount_version >= 4) ++ ++#ifndef HAVE_inet_aton ++#define inet_aton(a,b) (0) ++#endif ++ ++typedef dirpath mnt2arg_t; ++typedef dirpath mnt3arg_t; ++typedef dirpath mntarg_t; ++ ++typedef struct fhstatus mnt2res_t; ++typedef struct mountres3 mnt3res_t; ++typedef union { ++ mnt2res_t nfsv2; ++ mnt3res_t nfsv3; ++} mntres_t; ++ ++typedef struct { ++ char **hostname; ++ struct sockaddr_in saddr; ++ struct pmap pmap; ++} clnt_addr_t; ++ ++/* RPC call timeout values */ ++static const struct timeval TIMEOUT = { 20, 0 }; ++static const struct timeval RETRY_TIMEOUT = { 3, 0 }; ++ ++/* Define the order in which to probe for UDP/TCP services */ ++static const u_int * ++proto_probelist(const int use_tcp) ++{ ++ static const u_int probe_both[] = { IPPROTO_TCP, IPPROTO_UDP, 0 }; ++ static const u_int probe_udponly[] = { IPPROTO_UDP, 0 }; ++ if (use_tcp) ++ return probe_both; ++ return probe_udponly; ++} ++ ++/* Define the order in which NFS versions are probed on portmapper */ ++static const u_long * ++nfs_probelist(const int vers) ++{ ++ static const u_long nfs2_probe[] = { 2, 0}; ++ static const u_long nfs3_probe[] = { 3, 2, 0}; ++ switch (vers) { ++ case 3: ++ return nfs3_probe; ++ default: ++ return nfs2_probe; ++ } ++} ++ ++/* Define the order in which Mountd versions are probed on portmapper */ ++static const u_long * ++mnt_probelist(const int vers) ++{ ++ static const u_long mnt1_probe[] = { 1, 2, 0 }; ++ static const u_long mnt3_probe[] = { 3, 1, 2, 0 }; ++ switch (vers) { ++ case 3: ++ return mnt3_probe; ++ default: ++ return mnt1_probe; ++ } ++} ++ ++/* Map an NFS version into the corresponding Mountd version */ ++static u_long ++nfsvers_to_mnt(const u_long vers) ++{ ++ static const u_long nfs_to_mnt[] = { 0, 0, 1, 3 }; ++ if (vers <= 3) ++ return nfs_to_mnt[vers]; ++ return 0; ++} ++ ++/* Map a Mountd version into the corresponding NFS version */ ++static u_long ++mntvers_to_nfs(const u_long vers) ++{ ++ static const u_long mnt_to_nfs[] = { 0, 2, 2, 3 }; ++ if (vers <= 3) ++ return mnt_to_nfs[vers]; ++ return 0; ++} + + static int + linux_version_code(void) { +@@ -102,123 +192,558 @@ linux_version_code(void) { + * NFS_MOUNT_VERSION: these nfsmount sources at compile time + * nfs_mount_version: version this source and running kernel can handle + */ ++static int nfs_mount_version = NFS_MOUNT_VERSION; ++ + static int + find_kernel_nfs_mount_version(void) { + static int kernel_version = -1; +- int nfs_mount_version = NFS_MOUNT_VERSION; ++ int mnt_version = NFS_MOUNT_VERSION; + + if (kernel_version == -1) + kernel_version = linux_version_code(); + + if (kernel_version) { + if (kernel_version < MAKE_VERSION(2,1,32)) +- nfs_mount_version = 1; ++ mnt_version = 1; + else if (kernel_version < MAKE_VERSION(2,2,18)) +- nfs_mount_version = 3; ++ mnt_version = 3; + else if (kernel_version < MAKE_VERSION(2,3,0)) +- nfs_mount_version = 4; /* since 2.2.18pre9 */ ++ mnt_version = 4; /* since 2.2.18pre9 */ + else if (kernel_version < MAKE_VERSION(2,3,99)) +- nfs_mount_version = 3; ++ mnt_version = 3; + else +- nfs_mount_version = 4; /* since 2.3.99pre4 */ ++ mnt_version = 4; /* since 2.3.99pre4 */ + } +- if (nfs_mount_version > NFS_MOUNT_VERSION) +- nfs_mount_version = NFS_MOUNT_VERSION; +- return nfs_mount_version; +-} +- +-static struct pmap * +-get_mountport(struct sockaddr_in *server_addr, +- long unsigned prog, +- long unsigned version, +- long unsigned proto, +- long unsigned port, +- int nfs_mount_version) +-{ +- struct pmaplist *pmap; +- static struct pmap p = {0, 0, 0, 0}; +- +- if (version > MAX_NFSPROT) +- version = MAX_NFSPROT; +- if (!prog) +- prog = MOUNTPROG; +- p.pm_prog = prog; +- p.pm_vers = version; +- p.pm_prot = proto; +- p.pm_port = port; +- +- server_addr->sin_port = PMAPPORT; +- pmap = pmap_getmaps(server_addr); +- +- while (pmap) { +- if (pmap->pml_map.pm_prog != prog) +- goto next; +- if (!version && p.pm_vers > pmap->pml_map.pm_vers) +- goto next; +- if (version > 2 && pmap->pml_map.pm_vers != version) +- goto next; +- if (version && version <= 2 && pmap->pml_map.pm_vers > 2) +- goto next; +- if (pmap->pml_map.pm_vers > MAX_NFSPROT || +- (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || +- (port && pmap->pml_map.pm_port != port)) +- goto next; +- memcpy(&p, &pmap->pml_map, sizeof(p)); +- next: +- pmap = pmap->pml_next; +- } +- if (!p.pm_vers) +- p.pm_vers = MOUNTVERS; +- if (!p.pm_prot) +- p.pm_prot = IPPROTO_TCP; +-#if 0 +- if (!p.pm_port) { +- p.pm_port = pmap_getport(server_addr, p.pm_prog, p.pm_vers, +- p.pm_prot); ++ if (mnt_version > NFS_MOUNT_VERSION) ++ mnt_version = NFS_MOUNT_VERSION; ++ return mnt_version; ++} ++ ++static int ++nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr) ++{ ++ struct hostent *hp; ++ ++ saddr->sin_family = AF_INET; ++ if (!inet_aton(hostname, &saddr->sin_addr)) { ++ if ((hp = gethostbyname(hostname)) == NULL) { ++ fprintf(stderr, _("mount: can't get address for %s\n"), ++ hostname); ++ return 0; ++ } else { ++ if (hp->h_length > sizeof(*saddr)) { ++ fprintf(stderr, ++ _("mount: got bad hp->h_length\n")); ++ hp->h_length = sizeof(*saddr); ++ } ++ memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length); ++ } ++ } ++ return 1; ++} ++ ++/* ++ * Sigh... pmap_getport() doesn't actually check the version number. ++ * In order to make sure that the server actually supports the service ++ * we're requesting, we open and RPC client, and fire off a NULL ++ * RPC call. ++ */ ++static int ++clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers, ++ const u_int prot) ++{ ++ CLIENT *clnt; ++ int sock, stat; ++ static char clnt_res; ++ ++ 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; ++} ++ ++/* ++ * Use the portmapper to discover whether or not the service we want is ++ * available. The lists 'versions' and 'protos' define ordered sequences ++ * of service versions and udp/tcp protocols to probe for. ++ */ ++static int ++probe_port(clnt_addr_t *server, ++ const u_long *versions, ++ const u_int *protos) ++{ ++ struct sockaddr_in *saddr = &server->saddr; ++ struct pmap *pmap = &server->pmap; ++ const u_long prog = pmap->pm_prog, ++ vers = pmap->pm_vers, ++ *p_vers; ++ const u_int prot = (u_int)pmap->pm_prot, ++ *p_prot; ++ const u_short port = (u_short) pmap->pm_port; ++ u_short p_port; ++ ++ p_prot = prot ? &prot : protos; ++ p_vers = vers ? &vers : versions; ++ for (;;) { ++ saddr->sin_port = htons(PMAPPORT); ++ p_port = pmap_getport(saddr, prog, *p_vers, *p_prot); ++ if (p_port) { ++ if (!port || port == p_port) { ++ saddr->sin_port = htons(port); ++ if (clnt_ping(saddr, prog, *p_vers, *p_prot)) ++ goto out_ok; ++ } ++ } else if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) ++ break; ++ if (!prot) { ++ if (*++p_prot) ++ continue; ++ p_prot = protos; ++ } ++ if (vers || !*++p_vers) ++ break; ++ } ++ return 0; ++ out_ok: ++ if (!vers) ++ pmap->pm_vers = *p_vers; ++ if (!prot) ++ pmap->pm_prot = *p_prot; ++ if (!port) ++ pmap->pm_port = p_port; ++ return 1; ++} ++ ++static int ++probe_nfsport(clnt_addr_t *nfs_server) ++{ ++ const struct pmap *pmap = &nfs_server->pmap; ++ const u_long *probe_vers; ++ const u_int *probe_prot; ++ ++ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) ++ return 1; ++ probe_vers = nfs_probelist(MAX_NFSPROT); ++ probe_prot = proto_probelist(HAVE_RELIABLE_TCP); ++ return probe_port(nfs_server, probe_vers, probe_prot); ++} ++ ++static int ++probe_mntport(clnt_addr_t *mnt_server) ++{ ++ const struct pmap *pmap = &mnt_server->pmap; ++ const u_long *probe_vers; ++ const u_int *probe_prot; ++ ++ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) ++ return 1; ++ probe_vers = mnt_probelist(MAX_MNTPROT); ++ probe_prot = proto_probelist(HAVE_RELIABLE_TCP); ++ return probe_port(mnt_server, probe_vers, probe_prot); ++} ++ ++static int ++probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) ++{ ++ struct pmap *nfs_pmap = &nfs_server->pmap; ++ struct pmap *mnt_pmap = &mnt_server->pmap; ++ struct pmap save_nfs, save_mnt; ++ int res; ++ const u_long *probe_vers; ++ ++ if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers) ++ nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers); ++ else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers) ++ mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers); ++ if (nfs_pmap->pm_vers) ++ goto version_fixed; ++ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs)); ++ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt)); ++ for (probe_vers = mnt_probelist(MAX_MNTPROT); *probe_vers; probe_vers++) { ++ nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers); ++ if ((res = probe_nfsport(nfs_server) != 0)) { ++ mnt_pmap->pm_vers = *probe_vers; ++ if ((res = probe_mntport(mnt_server)) != 0) ++ return 1; ++ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); ++ } ++ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); + } ++ out_bad: ++ return 0; ++ version_fixed: ++ if (!probe_nfsport(nfs_server)) ++ goto out_bad; ++ return probe_mntport(mnt_server); ++} ++ ++static CLIENT * ++mnt_openclnt(clnt_addr_t *mnt_server, int *msock, const int report_errs) ++{ ++ struct sockaddr_in *mnt_saddr = &mnt_server->saddr; ++ struct pmap *mnt_pmap = &mnt_server->pmap; ++ CLIENT *clnt; ++ ++ /* contact the mount daemon via TCP */ ++ mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port); ++ *msock = RPC_ANYSOCK; ++ ++ switch (mnt_pmap->pm_prot) { ++ case IPPROTO_UDP: ++ clnt = clntudp_bufcreate(mnt_saddr, ++ mnt_pmap->pm_prog, mnt_pmap->pm_vers, ++ RETRY_TIMEOUT, msock, ++ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); ++ break; ++ case IPPROTO_TCP: ++ clnt = clnttcp_create(mnt_saddr, ++ mnt_pmap->pm_prog, mnt_pmap->pm_vers, ++ msock, ++ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); ++ break; ++ default: ++ goto out_bad; ++ } ++ if (!clnt) ++ goto report_err; ++ /* try to mount hostname:dirname */ ++ clnt->cl_auth = authunix_create_default(); ++ return clnt; ++ report_err: ++ if (report_errs) ++ clnt_pcreateerror("mount"); ++ out_bad: ++ return NULL; ++} ++ ++static inline void ++mnt_closeclnt(CLIENT *clnt, int msock) ++{ ++ auth_destroy(clnt->cl_auth); ++ clnt_destroy(clnt); ++ close(msock); ++} ++ ++static inline enum clnt_stat ++nfs3_mount(CLIENT *clnt, mnt3arg_t *mnt3arg, mnt3res_t *mnt3res) ++{ ++ return clnt_call(clnt, MOUNTPROC3_MNT, ++ (xdrproc_t) xdr_dirpath, (caddr_t) mnt3arg, ++ (xdrproc_t) xdr_mountres3, (caddr_t) mnt3res, ++ TIMEOUT); ++} ++ ++static inline enum clnt_stat ++nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg, mnt2res_t *mnt2res) ++{ ++ return clnt_call(clnt, MOUNTPROC_MNT, ++ (xdrproc_t) xdr_dirpath, (caddr_t) mnt2arg, ++ (xdrproc_t) xdr_fhstatus, (caddr_t) mnt2res, ++ TIMEOUT); ++} ++ ++static int ++nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server, ++ mntarg_t *mntarg, mntres_t *mntres, const int report_errs) ++{ ++ CLIENT *clnt; ++ enum clnt_stat stat; ++ int msock; ++ ++ ++ if (!probe_bothports(mnt_server, nfs_server)) { ++ if (report_errs) ++ fprintf(stderr,_("mount: failed to probe ports on NFS server %s\n"), ++ *nfs_server->hostname); ++ goto out_bad; ++ } ++ ++ clnt = mnt_openclnt(mnt_server, &msock, report_errs); ++ if (!clnt) ++ goto out_bad; ++ /* make pointers in xdr_mountres3 NULL so ++ * that xdr_array allocates memory for us ++ */ ++ memset(mntres, 0, sizeof(*mntres)); ++ switch (mnt_server->pmap.pm_vers) { ++ case 3: ++ stat = nfs3_mount(clnt, mntarg, &mntres->nfsv3); ++ break; ++ case 2: ++ case 1: ++ stat = nfs2_mount(clnt, mntarg, &mntres->nfsv2); ++ break; ++ default: ++ goto out_bad; ++ } ++ if (stat != RPC_SUCCESS && report_errs) ++ clnt_perror(clnt, "mount"); ++ mnt_closeclnt(clnt, msock); ++ if (stat == RPC_SUCCESS) ++ return 1; ++ out_bad: ++ return 0; ++} ++ ++static int ++parse_options(char *old_opts, struct nfs_mount_data *data, ++ int *bg, int *retry, clnt_addr_t *mnt_server, ++ clnt_addr_t *nfs_server, char *new_opts, const int opt_size) ++{ ++ struct sockaddr_in *mnt_saddr = &mnt_server->saddr; ++ struct pmap *mnt_pmap = &mnt_server->pmap; ++ struct pmap *nfs_pmap = &nfs_server->pmap; ++ int len; ++ char *opt, *opteq; ++ char *mounthost = NULL; ++ char cbuf[128]; ++ ++ data->flags = 0; ++ *bg = 0; ++ ++ len = strlen(new_opts); ++ for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { ++ if (strlen(opt) >= sizeof(cbuf)) ++ goto bad_parameter; ++ if ((opteq = strchr(opt, '=')) && isdigit(opteq[1])) { ++ int 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")) ++ nfs_pmap->pm_port = val; ++ else if (!strcmp(opt, "mountport")) ++ mnt_pmap->pm_port = val; ++ else if (!strcmp(opt, "mountprog")) ++ mnt_pmap->pm_prog = val; ++ else if (!strcmp(opt, "mountvers")) ++ mnt_pmap->pm_vers = val; ++ else if (!strcmp(opt, "nfsprog")) ++ nfs_pmap->pm_prog = val; ++ else if (!strcmp(opt, "nfsvers") || ++ !strcmp(opt, "vers")) { ++ nfs_pmap->pm_vers = val; ++ opt = "nfsvers"; ++#if NFS_MOUNT_VERSION >= 2 ++ } else if (!strcmp(opt, "namlen")) { ++ if (nfs_mount_version >= 2) ++ data->namlen = val; ++ else if (!sloppy) ++ goto bad_parameter; + #endif +-#if 0 +-#define MOUNTPORT 635 +- /* HJLu wants to remove all traces of the old default port. +- Are there still people running a mount RPC service on this +- port without having a portmapper? */ +- if (!p.pm_port) +- p.pm_port = MOUNTPORT; +-#endif +- return &p; ++ } else if (!strcmp(opt, "addr")) { ++ /* ignore */; ++ continue; ++ } else if (!sloppy) ++ goto bad_parameter; ++ sprintf(cbuf, "%s=%s,", opt, opteq+1); ++ } else if (opteq) { ++ *opteq = '\0'; ++ if (!strcmp(opt, "proto")) { ++ if (!strcmp(opteq+1, "udp")) { ++ nfs_pmap->pm_prot = IPPROTO_UDP; ++#if NFS_MOUNT_VERSION >= 2 ++ data->flags &= ~NFS_MOUNT_TCP; ++ } else if (!strcmp(opteq+1, "tcp") && ++ nfs_mount_version >= 2) { ++ nfs_pmap->pm_prot = IPPROTO_TCP; ++ data->flags |= NFS_MOUNT_TCP; ++#endif ++ } else if (!sloppy) ++ goto bad_parameter; ++ } else if (!strcmp(opt, "mounthost")) ++ mounthost=xstrndup(opteq+1, ++ strcspn(opteq+1," \t\n\r,")); ++ else ++ goto bad_parameter; ++ sprintf(cbuf, "%s=%s,", opt, opteq+1); ++ } else { ++ int 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")) { ++ data->flags &= ~NFS_MOUNT_SOFT; ++ if (val) ++ data->flags |= NFS_MOUNT_SOFT; ++ } else if (!strcmp(opt, "hard")) { ++ data->flags &= ~NFS_MOUNT_SOFT; ++ if (!val) ++ data->flags |= NFS_MOUNT_SOFT; ++ } else if (!strcmp(opt, "intr")) { ++ data->flags &= ~NFS_MOUNT_INTR; ++ if (val) ++ data->flags |= NFS_MOUNT_INTR; ++ } else if (!strcmp(opt, "posix")) { ++ data->flags &= ~NFS_MOUNT_POSIX; ++ if (val) ++ data->flags |= NFS_MOUNT_POSIX; ++ } else if (!strcmp(opt, "cto")) { ++ data->flags &= ~NFS_MOUNT_NOCTO; ++ if (!val) ++ data->flags |= NFS_MOUNT_NOCTO; ++ } else if (!strcmp(opt, "ac")) { ++ data->flags &= ~NFS_MOUNT_NOAC; ++ if (!val) ++ data->flags |= NFS_MOUNT_NOAC; ++#if NFS_MOUNT_VERSION >= 2 ++ } else if (!strcmp(opt, "tcp")) { ++ data->flags &= ~NFS_MOUNT_TCP; ++ if (val) { ++ if (nfs_mount_version < 2) ++ goto bad_option; ++ nfs_pmap->pm_prot = IPPROTO_TCP; ++ data->flags |= NFS_MOUNT_TCP; ++ } else ++ nfs_pmap->pm_prot = IPPROTO_UDP; ++ } else if (!strcmp(opt, "udp")) { ++ data->flags &= ~NFS_MOUNT_TCP; ++ if (!val) { ++ if (nfs_mount_version < 2) ++ goto bad_option; ++ nfs_pmap->pm_prot = IPPROTO_TCP; ++ data->flags |= NFS_MOUNT_TCP; ++ } else ++ nfs_pmap->pm_prot = IPPROTO_UDP; ++#endif ++#if NFS_MOUNT_VERSION >= 3 ++ } else if (!strcmp(opt, "lock")) { ++ data->flags &= ~NFS_MOUNT_NONLM; ++ if (!val) { ++ if (nfs_mount_version < 3) ++ goto bad_option; ++ data->flags |= NFS_MOUNT_NONLM; ++ } ++#endif ++#if NFS_MOUNT_VERSION >= 4 ++ } else if (!strcmp(opt, "broken_suid")) { ++ data->flags &= ~NFS_MOUNT_BROKEN_SUID; ++ if (val) { ++ if (nfs_mount_version < 4) ++ goto bad_option; ++ data->flags |= NFS_MOUNT_BROKEN_SUID; ++ } ++#endif ++ } else { ++ bad_option: ++ printf(_("Unsupported nfs mount option: " ++ "%s%s\n"), val ? "" : "no", opt); ++ goto out_bad; ++ } ++ sprintf(cbuf, val ? "%s,":"no%s,", opt); ++ } ++ len += strlen(cbuf); ++ if (len >= opt_size) { ++ printf(_("mount: excessively long option argument\n")); ++ goto out_bad; ++ } ++ strcat(new_opts, cbuf); ++ } ++ /* See if the nfs host = mount host. */ ++ if (mounthost) { ++ if (!nfs_gethostbyname(mounthost, mnt_saddr)) ++ goto out_bad; ++ *mnt_server->hostname = mounthost; ++ } ++ return 1; ++ bad_parameter: ++ printf(_("Bad nfs mount parameter: %s\n"), opt); ++ out_bad: ++ return 0; ++} ++ ++static inline int ++nfsmnt_check_compat(const struct pmap *nfs_pmap, const struct pmap *mnt_pmap) ++{ ++ if (nfs_pmap->pm_vers > MAX_NFSPROT) { ++ fprintf(stderr, _("NFSv%ld not supported!\n"), nfs_pmap->pm_vers); ++ goto out_bad; ++ } ++ if (mnt_pmap->pm_vers > MAX_MNTPROT) { ++ fprintf(stderr, _("NFS mount v%ld not supported!\n"), mnt_pmap->pm_vers); ++ goto out_bad; ++ } ++ return 1; ++ out_bad: ++ return 0; + } + +-int nfsmount(const char *spec, const char *node, int *flags, +- char **extra_opts, char **mount_opts, int *nfs_mount_vers, +- int running_bg) ++int ++nfsmount(const char *spec, const char *node, int *flags, ++ char **extra_opts, char **mount_opts, int *nfs_mount_vers, ++ int running_bg) + { + static char *prev_bg_host; + char hostdir[1024]; +- CLIENT *mclient; + char *hostname, *dirname, *old_opts, *mounthost = NULL; +- char new_opts[1024]; +- struct timeval total_timeout; +- enum clnt_stat clnt_stat; ++ char new_opts[1024], cbuf[20]; + static struct nfs_mount_data data; +- char *opt, *opteq; +- int nfs_mount_version; + int val; +- struct hostent *hp; +- struct sockaddr_in server_addr; +- struct sockaddr_in mount_server_addr; +- struct pmap *pm_mnt; +- int msock, fsock; +- struct timeval retry_timeout; +- union { +- struct fhstatus nfsv2; +- struct mountres3 nfsv3; +- } status; ++ ++ clnt_addr_t mnt_server = { &mounthost, }; ++ clnt_addr_t nfs_server = { &hostname, }; ++ struct sockaddr_in *nfs_saddr = &nfs_server.saddr; ++ struct pmap *mnt_pmap = &mnt_server.pmap, ++ *nfs_pmap = &nfs_server.pmap; ++ struct pmap save_mnt, save_nfs; ++ ++ int fsock; ++ ++ mntres_t mntres; ++ + struct stat statbuf; +- char *s; +- int port, mountport, proto, bg, soft, intr; +- int posix, nocto, noac, nolock, broken_suid; +- int retry, tcp; +- int mountprog, mountvers, nfsprog, nfsvers; ++ char *s, *p; ++ int bg, retry; + int retval; + time_t t; + time_t prevt; +@@ -231,8 +756,7 @@ int nfsmount(const char *spec, const cha + nfs_mount_version = *nfs_mount_vers; + + retval = EX_FAIL; +- msock = fsock = -1; +- mclient = NULL; ++ fsock = -1; + if (strlen(spec) >= sizeof(hostdir)) { + fprintf(stderr, _("mount: " + "excessively long host:dir argument\n")); +@@ -258,49 +782,23 @@ int nfsmount(const char *spec, const cha + goto fail; + } + +- server_addr.sin_family = AF_INET; +-#ifdef HAVE_inet_aton +- if (!inet_aton(hostname, &server_addr.sin_addr)) +-#endif +- { +- if ((hp = gethostbyname(hostname)) == NULL) { +- fprintf(stderr, _("mount: can't get address for %s\n"), +- hostname); +- goto fail; +- } else { +- 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(&server_addr.sin_addr, +- hp->h_addr, hp->h_length); +- } +- } +- +- memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr)); ++ if (!nfs_gethostbyname(hostname, nfs_saddr)) ++ goto fail; ++ mounthost = hostname; ++ memcpy (&mnt_server.saddr, nfs_saddr, sizeof (mnt_server.saddr)); + + /* add IP address to mtab options for use when unmounting */ + +- s = inet_ntoa(server_addr.sin_addr); ++ s = inet_ntoa(nfs_saddr->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; +- } +- sprintf(new_opts, "%s%saddr=%s", +- old_opts, *old_opts ? "," : "", s); +- *extra_opts = xstrdup(new_opts); + + /* Set default options. + * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to + * let the kernel decide. + * timeo is filled in after we know whether it'll be TCP or UDP. */ + memset(&data, 0, sizeof(data)); +- data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; +@@ -310,167 +808,21 @@ int nfsmount(const char *spec, const cha + #endif + + bg = 0; +- soft = 0; +- intr = 0; +- posix = 0; +- nocto = 0; +- nolock = 0; +- broken_suid = 0; +- noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ +- tcp = 0; + +- mountprog = MOUNTPROG; +- mountvers = 0; +- port = 0; +- mountport = 0; +- nfsprog = NFS_PROGRAM; +- nfsvers = 0; ++ memset(mnt_pmap, 0, sizeof(*mnt_pmap)); ++ mnt_pmap->pm_prog = MOUNTPROG; ++ memset(nfs_pmap, 0, sizeof(*nfs_pmap)); ++ nfs_pmap->pm_prog = NFS_PROGRAM; + + /* parse options */ ++ new_opts[0] = 0; ++ if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server, ++ new_opts, sizeof(new_opts))) ++ goto fail; ++ if (!nfsmnt_check_compat(nfs_pmap, mnt_pmap)) ++ goto fail; + +- 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")) +- port = val; +- else if (!strcmp(opt, "mountport")) +- mountport = val; +- else if (!strcmp(opt, "mounthost")) +- mounthost=xstrndup(opteq+1, +- strcspn(opteq+1," \t\n\r,")); +- else if (!strcmp(opt, "mountprog")) +- mountprog = val; +- else if (!strcmp(opt, "mountvers")) +- mountvers = val; +- else if (!strcmp(opt, "nfsprog")) +- nfsprog = val; +- else if (!strcmp(opt, "nfsvers") || +- !strcmp(opt, "vers")) +- nfsvers = val; +- else if (!strcmp(opt, "proto")) { +- if (!strncmp(opteq+1, "tcp", 3)) +- tcp = 1; +- else if (!strncmp(opteq+1, "udp", 3)) +- tcp = 0; +- else +- printf(_("Warning: Unrecognized proto= option.\n")); +- } else if (!strcmp(opt, "namlen")) { +-#if NFS_MOUNT_VERSION >= 2 +- if (nfs_mount_version >= 2) +- data.namlen = val; +- else +-#endif +- printf(_("Warning: Option namlen is not supported.\n")); +- } 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, "posix")) +- posix = val; +- else if (!strcmp(opt, "cto")) +- nocto = !val; +- else if (!strcmp(opt, "ac")) +- noac = !val; +- else if (!strcmp(opt, "tcp")) +- tcp = val; +- else if (!strcmp(opt, "udp")) +- tcp = !val; +- else if (!strcmp(opt, "lock")) { +- if (nfs_mount_version >= 3) +- nolock = !val; +- else +- printf(_("Warning: option nolock is not supported.\n")); +- } else if (!strcmp(opt, "broken_suid")) { +- broken_suid = val; +- } else { +- if (!sloppy) { +- printf(_("unknown nfs mount option: " +- "%s%s\n"), val ? "" : "no", opt); +- goto fail; +- } +- } +- } +- } +- proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; +- +- data.flags = (soft ? NFS_MOUNT_SOFT : 0) +- | (intr ? NFS_MOUNT_INTR : 0) +- | (posix ? NFS_MOUNT_POSIX : 0) +- | (nocto ? NFS_MOUNT_NOCTO : 0) +- | (noac ? NFS_MOUNT_NOAC : 0); +-#if NFS_MOUNT_VERSION >= 2 +- if (nfs_mount_version >= 2) +- data.flags |= (tcp ? NFS_MOUNT_TCP : 0); +-#endif +-#if NFS_MOUNT_VERSION >= 3 +- if (nfs_mount_version >= 3) +- data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); +-#endif +-#if NFS_MOUNT_VERSION >= 4 +- if (nfs_mount_version >= 4) +- data.flags |= (broken_suid ? NFS_MOUNT_BROKEN_SUID : 0); +-#endif +- if (nfsvers > MAX_NFSPROT) { +- fprintf(stderr, "NFSv%d not supported!\n", nfsvers); +- return 0; +- } +- if (mountvers > MAX_NFSPROT) { +- fprintf(stderr, "NFSv%d not supported!\n", nfsvers); +- return 0; +- } +- if (nfsvers && !mountvers) +- mountvers = (nfsvers < 3) ? 1 : nfsvers; +- if (nfsvers && nfsvers < mountvers) +- mountvers = nfsvers; +- +- /* Adjust options if none specified */ +- if (!data.timeo) +- data.timeo = tcp ? 70 : 7; + + #ifdef NFS_MOUNT_DEBUG + printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", +@@ -478,9 +830,10 @@ int nfsmount(const char *spec, const cha + 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", +- port, bg, retry, data.flags); ++ nfs_pmap->pm_port, bg, retry, data.flags); + printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", +- mountprog, mountvers, nfsprog, nfsvers); ++ mnt_pmap->pm_prog, mnt_pmap->pm_vers, ++ nfs_pmap->pm_prog, nfs_pmap->pm_vers); + printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", + (data.flags & NFS_MOUNT_SOFT) != 0, + (data.flags & NFS_MOUNT_INTR) != 0, +@@ -497,7 +850,7 @@ int nfsmount(const char *spec, const cha + *mount_opts = (char *) &data; + + if (*flags & MS_REMOUNT) +- return 0; ++ goto out_ok; + + /* + * If the previous mount operation on the same host was +@@ -512,28 +865,6 @@ int nfsmount(const char *spec, const cha + } + + /* create mount deamon client */ +- /* See if the nfs host = mount host. */ +- if (mounthost) { +- if (mounthost[0] >= '0' && mounthost[0] <= '9') { +- mount_server_addr.sin_family = AF_INET; +- mount_server_addr.sin_addr.s_addr = inet_addr(hostname); +- } else { +- if ((hp = gethostbyname(mounthost)) == NULL) { +- fprintf(stderr, _("mount: can't get address for %s\n"), +- mounthost); +- goto fail; +- } else { +- if (hp->h_length > sizeof(struct in_addr)) { +- fprintf(stderr, +- _("mount: got bad hp->h_length?\n")); +- hp->h_length = sizeof(struct in_addr); +- } +- mount_server_addr.sin_family = AF_INET; +- memcpy(&mount_server_addr.sin_addr, +- hp->h_addr, hp->h_length); +- } +- } +- } + + /* + * The following loop implements the mount retries. On the first +@@ -551,15 +882,13 @@ int nfsmount(const char *spec, const cha + * + * Only the first error message will be displayed. + */ +- retry_timeout.tv_sec = 3; +- retry_timeout.tv_usec = 0; +- total_timeout.tv_sec = 20; +- total_timeout.tv_usec = 0; + timeout = time(NULL) + 60 * retry; + prevt = 0; + t = 30; + val = 1; + ++ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs)); ++ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt)); + for (;;) { + if (bg && stat(node, &statbuf) == -1) { + /* no mount point yet - sleep */ +@@ -570,89 +899,18 @@ int nfsmount(const char *spec, const cha + val = 30; + } + } else { ++ int stat; + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + +- pm_mnt = get_mountport(&mount_server_addr, +- mountprog, +- mountvers, +- proto, +- mountport, +- nfs_mount_version); +- +- /* contact the mount daemon via TCP */ +- mount_server_addr.sin_port = htons(pm_mnt->pm_port); +- msock = RPC_ANYSOCK; +- +- switch (pm_mnt->pm_prot) { +- case IPPROTO_UDP: +- mclient = clntudp_create(&mount_server_addr, +- pm_mnt->pm_prog, +- pm_mnt->pm_vers, +- retry_timeout, +- &msock); +- if (mclient) +- break; +- mount_server_addr.sin_port = +- htons(pm_mnt->pm_port); +- msock = RPC_ANYSOCK; +- case IPPROTO_TCP: +- mclient = clnttcp_create(&mount_server_addr, +- pm_mnt->pm_prog, +- pm_mnt->pm_vers, +- &msock, 0, 0); ++ stat = nfs_call_mount(&mnt_server, &nfs_server, ++ &dirname, &mntres, ++ !running_bg && prevt == 0); ++ if (stat) + break; +- default: +- mclient = 0; +- } +- +- if (mclient) { +- /* try to mount hostname:dirname */ +- mclient->cl_auth = authunix_create_default(); +- +- /* make pointers in xdr_mountres3 NULL so +- * that xdr_array allocates memory for us +- */ +- memset(&status, 0, sizeof(status)); +- +- if (pm_mnt->pm_vers == 3) +- clnt_stat = clnt_call(mclient, +- MOUNTPROC3_MNT, +- (xdrproc_t) xdr_dirpath, +- (caddr_t) &dirname, +- (xdrproc_t) xdr_mountres3, +- (caddr_t) &status, +- total_timeout); +- else +- clnt_stat = clnt_call(mclient, +- MOUNTPROC_MNT, +- (xdrproc_t) xdr_dirpath, +- (caddr_t) &dirname, +- (xdrproc_t) xdr_fhstatus, +- (caddr_t) &status, +- total_timeout); +- +- if (clnt_stat == RPC_SUCCESS) +- break; /* we're done */ +-#if 0 +- /* errno? who sets errno? */ +- /* this fragment breaks bg mounting */ +- if (errno != ECONNREFUSED) { +- clnt_perror(mclient, "mount"); +- goto fail; /* don't retry */ +- } +-#endif +- if (!running_bg && prevt == 0) +- clnt_perror(mclient, "mount"); +- auth_destroy(mclient->cl_auth); +- clnt_destroy(mclient); +- mclient = 0; +- close(msock); +- } else { +- if (!running_bg && prevt == 0) +- clnt_pcreateerror("mount"); +- } ++ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); ++ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); + prevt = t; + } + +@@ -668,36 +926,35 @@ int nfsmount(const char *spec, const cha + if (t >= timeout) + goto fail; + } +- nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; + +- if (nfsvers == 2) { +- if (status.nfsv2.fhs_status != 0) { ++ if (nfs_pmap->pm_vers == 2) { ++ if (mntres.nfsv2.fhs_status != 0) { + fprintf(stderr, +- "mount: %s:%s failed, reason given by server: %s\n", ++ _("mount: %s:%s failed, reason given by server: %s\n"), + hostname, dirname, +- nfs_strerror(status.nfsv2.fhs_status)); ++ nfs_strerror(mntres.nfsv2.fhs_status)); + goto fail; + } + memcpy(data.root.data, +- (char *) status.nfsv2.fhstatus_u.fhs_fhandle, ++ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); + #if NFS_MOUNT_VERSION >= 4 + data.root.size = NFS_FHSIZE; + memcpy(data.old_root.data, +- (char *) status.nfsv2.fhstatus_u.fhs_fhandle, ++ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); + #endif + } else { + #if NFS_MOUNT_VERSION >= 4 + fhandle3 *fhandle; +- if (status.nfsv3.fhs_status != 0) { ++ if (mntres.nfsv3.fhs_status != 0) { + fprintf(stderr, +- "mount: %s:%s failed, reason given by server: %s\n", ++ _("mount: %s:%s failed, reason given by server: %s\n"), + hostname, dirname, +- nfs_strerror(status.nfsv3.fhs_status)); ++ nfs_strerror(mntres.nfsv3.fhs_status)); + goto fail; + } +- fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; ++ fhandle = &mntres.nfsv3.mountres3_u.mountinfo.fhandle; + memset(data.old_root.data, 0, NFS_FHSIZE); + memset(&data.root, 0, sizeof(data.root)); + data.root.size = fhandle->fhandle3_len; +@@ -711,13 +968,9 @@ int nfsmount(const char *spec, const cha + + /* create nfs socket for kernel */ + +- if (tcp) { +- if (nfs_mount_version < 3) { +- printf(_("NFS over TCP is not supported.\n")); +- goto fail; +- } ++ if (nfs_pmap->pm_prot == IPPROTO_TCP) + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +- } else ++ else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + perror(_("nfs socket")); +@@ -727,72 +980,162 @@ int nfsmount(const char *spec, const cha + perror(_("nfs bindresvport")); + goto fail; + } +- if (port == 0) { +- server_addr.sin_port = PMAPPORT; +- port = pmap_getport(&server_addr, nfsprog, nfsvers, +- tcp ? IPPROTO_TCP : IPPROTO_UDP); +-#if 1 +- /* Here we check to see if user is mounting with the +- * tcp option. If so, and if the portmap returns a +- * '0' for port (service unavailable), we then exit, +- * notifying the user, rather than hanging up mount. +- */ +- if (port == 0 && tcp == 1) { +- perror(_("nfs server reported service unavailable")); +- goto fail; +- } +-#endif +- +- if (port == 0) +- port = NFS_PORT; + #ifdef NFS_MOUNT_DEBUG +- else +- printf(_("used portmapper to find NFS port\n")); ++ printf(_("using port %d for nfs deamon\n"), nfs_pmap->pm_port); + #endif +- } +-#ifdef NFS_MOUNT_DEBUG +- printf(_("using port %d for nfs deamon\n"), port); +-#endif +- server_addr.sin_port = htons(port); ++ nfs_saddr->sin_port = htons(nfs_pmap->pm_port); + /* + * connect() the socket for kernels 1.3.10 and below only, + * to avoid problems with multihomed hosts. + * --Swen + */ + if (linux_version_code() <= 66314 +- && connect(fsock, (struct sockaddr *) &server_addr, +- sizeof (server_addr)) < 0) { ++ && connect(fsock, (struct sockaddr *) nfs_saddr, ++ sizeof (*nfs_saddr)) < 0) { + perror(_("nfs connect")); + goto fail; + } + ++#if NFS_MOUNT_VERSION >= 2 ++ if (nfs_pmap->pm_prot == IPPROTO_TCP) ++ data.flags |= NFS_MOUNT_TCP; ++ else ++ data.flags &= ~NFS_MOUNT_TCP; ++#endif ++ + /* prepare data structure for kernel */ + + data.fd = fsock; +- memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); ++ memcpy((char *) &data.addr, (char *) nfs_saddr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)); + +- /* clean up */ ++ out_ok: ++ /* Ensure we have enough padding for the following strcat()s */ ++ if (strlen(new_opts) + strlen(s) + 30 >= sizeof(new_opts)) { ++ fprintf(stderr, _("mount: " ++ "excessively long option argument\n")); ++ goto fail; ++ } + +- auth_destroy(mclient->cl_auth); +- clnt_destroy(mclient); +- close(msock); ++ sprintf(cbuf, "addr=%s", s); ++ strcat(new_opts, cbuf); ++ ++ *extra_opts = xstrdup(new_opts); + return 0; + + /* abort */ +- + fail: +- if (msock != -1) { +- if (mclient) { +- auth_destroy(mclient->cl_auth); +- clnt_destroy(mclient); +- } +- close(msock); +- } + if (fsock != -1) + close(fsock); + return retval; +-} ++} ++ ++static inline enum clnt_stat ++nfs3_umount(dirpath *argp, CLIENT *clnt) ++{ ++ static char clnt_res; ++ memset (&clnt_res, 0, sizeof(clnt_res)); ++ return clnt_call(clnt, MOUNTPROC_UMNT, ++ (xdrproc_t) xdr_dirpath, (caddr_t)argp, ++ (xdrproc_t) xdr_void, (caddr_t) &clnt_res, ++ TIMEOUT); ++} ++ ++static inline enum clnt_stat ++nfs2_umount(dirpath *argp, CLIENT *clnt) ++{ ++ static char clnt_res; ++ memset (&clnt_res, 0, sizeof(clnt_res)); ++ return clnt_call(clnt, MOUNTPROC_UMNT, ++ (xdrproc_t) xdr_dirpath, (caddr_t)argp, ++ (xdrproc_t) xdr_void, (caddr_t) &clnt_res, ++ TIMEOUT); ++} ++ ++static int ++nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) ++{ ++ CLIENT *clnt; ++ enum clnt_stat res = 0; ++ int msock; ++ ++ clnt = mnt_openclnt(mnt_server, &msock, 1); ++ if (!clnt) ++ goto out_bad; ++ switch (mnt_server->pmap.pm_vers) { ++ case 3: ++ res = nfs3_umount(argp, clnt); ++ break; ++ case 2: ++ case 1: ++ res = nfs2_umount(argp, clnt); ++ break; ++ default: ++ } ++ mnt_closeclnt(clnt, msock); ++ if (res == RPC_SUCCESS) ++ return 1; ++ out_bad: ++ return 0; ++} ++ ++int ++nfsumount(const char *spec, const char *opts) ++{ ++ char *hostname; ++ char *dirname; ++ clnt_addr_t mnt_server = { &hostname, }; ++ struct pmap *pmap = &mnt_server.pmap; ++ char *p; ++ ++ nfs_mount_version = find_kernel_nfs_mount_version(); ++ if (spec == NULL || (p = strchr(spec,':')) == NULL) ++ goto out_bad; ++ hostname = xstrndup(spec, p-spec); ++ dirname = xstrdup(p+1); ++#ifdef NFS_MOUNT_DEBUG ++ printf(_("host: %s, directory: %s\n"), hostname, dirname); ++#endif ++ ++ if (opts && (p = strstr(opts, "addr="))) { ++ char *q; ++ ++ free(hostname); ++ p += 5; ++ q = p; ++ while (*q && *q != ',') q++; ++ hostname = xstrndup(p,q-p); ++ } ++ ++ if (opts && (p = strstr(opts, "mounthost="))) { ++ char *q; ++ ++ free(hostname); ++ p += 10; ++ q = p; ++ while (*q && *q != ',') q++; ++ hostname = xstrndup(p,q-p); ++ } ++ ++ pmap->pm_prog = MOUNTPROG; ++ pmap->pm_vers = MOUNTVERS; ++ if (opts && (p = strstr(opts, "mountprog=")) && isdigit(*(p+10))) ++ pmap->pm_prog = atoi(p+10); ++ if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10))) ++ pmap->pm_port = atoi(p+10); ++ if (opts && (p = strstr(opts, "nfsvers=")) && isdigit(*(p+8))) ++ pmap->pm_vers = nfsvers_to_mnt(atoi(p+8)); ++ if (opts && (p = strstr(opts, "mountvers=")) && isdigit(*(p+10))) ++ pmap->pm_vers = atoi(p+10); ++ ++ if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) ++ goto out_bad; ++ if (!probe_mntport(&mnt_server)) ++ goto out_bad; ++ return nfs_call_umount(&mnt_server, &dirname); ++ out_bad: ++ return 0; ++} + + /* + * We need to translate between nfs status return values and +diff -puN mount/sundries.h~nfs mount/sundries.h +--- util-linux-2.12/mount/sundries.h~nfs 2004-10-13 13:51:44.000000000 -0400 ++++ util-linux-2.12-bfields/mount/sundries.h 2004-10-13 14:13:30.000000000 -0400 +@@ -37,6 +37,7 @@ void die (int errcode, const char *fmt, + #ifdef HAVE_NFS + int nfsmount (const char *spec, const char *node, int *flags, + char **orig_opts, char **opt_args, int *version, int running_bg); ++int nfsumount(const char *spec, const char *opts); + #endif + + /* exit status - bits below are ORed */ +diff -puN mount/umount.c~nfs mount/umount.c +--- util-linux-2.12/mount/umount.c~nfs 2004-10-13 13:51:44.000000000 -0400 ++++ util-linux-2.12-bfields/mount/umount.c 2004-10-13 14:13:30.000000000 -0400 +@@ -122,7 +122,7 @@ check_special_umountprog() { + } + #endif + +-#ifdef HAVE_NFS ++#if 0 + static int xdr_dir(XDR *xdrsp, char *dirp) + { + return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); +@@ -265,7 +265,7 @@ umount_one (const char *spec, const char + /* Ignore any RPC errors, so that you can umount the filesystem + if the server is down. */ + if (strcasecmp(type, "nfs") == 0) +- nfs_umount_rpc_call(spec, opts); ++ nfsumount(spec, opts); + #endif + + umnt_err = umnt_err2 = 0; +_ diff --git a/util-linux-2.12-02-base-nfsv4.dif b/util-linux-2.12-02-base-nfsv4.dif new file mode 100644 index 0000000..5fce319 --- /dev/null +++ b/util-linux-2.12-02-base-nfsv4.dif @@ -0,0 +1,866 @@ + +Add basic nfsv4 support + +--- + + util-linux-2.12-bfields/mount/Makefile | 2 + util-linux-2.12-bfields/mount/fstab.5 | 1 + util-linux-2.12-bfields/mount/mount.8 | 75 ++++++ + util-linux-2.12-bfields/mount/mount.c | 13 + + util-linux-2.12-bfields/mount/nfs.5 | 200 +++++++++++++++++ + util-linux-2.12-bfields/mount/nfs4_mount.h | 82 +++++++ + util-linux-2.12-bfields/mount/nfs4mount.c | 335 +++++++++++++++++++++++++++++ + util-linux-2.12-bfields/mount/nfsmount.c | 5 + util-linux-2.12-bfields/mount/sundries.h | 2 + util-linux-2.12-bfields/mount/umount.c | 3 + 10 files changed, 710 insertions(+), 8 deletions(-) + +diff -puN mount/fstab.5~base-nfsv4 mount/fstab.5 +--- util-linux-2.12/mount/fstab.5~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/fstab.5 2004-10-13 13:51:50.000000000 -0400 +@@ -115,6 +115,7 @@ of filesystem types, such as + .IR msdos , + .IR ncpfs , + .IR nfs , ++.IR nfs4 , + .IR ntfs , + .IR proc , + .IR qnx4 , +diff -puN mount/Makefile~base-nfsv4 mount/Makefile +--- util-linux-2.12/mount/Makefile~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/Makefile 2004-10-13 13:51:50.000000000 -0400 +@@ -31,7 +31,7 @@ + + LO_OBJS = lomount.o $(LIB)/xstrncpy.o + CRYPT_OBJS = cryptsetup.o -lcryptsetup +-NFS_OBJS = nfsmount.o nfsmount_xdr.o ++NFS_OBJS = nfsmount.o nfsmount_xdr.o nfs4mount.o + GEN_FILES = nfsmount.h nfsmount_xdr.c nfsmount_clnt.c + + all: $(PROGS) +diff -puN mount/mount.8~base-nfsv4 mount/mount.8 +--- util-linux-2.12/mount/mount.8~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/mount.8 2004-10-13 13:51:50.000000000 -0400 +@@ -370,6 +370,7 @@ currently supported are: + .IR msdos , + .IR ncpfs , + .IR nfs , ++.IR nfs4 , + .IR ntfs , + .IR proc , + .IR qnx4 , +@@ -403,7 +404,7 @@ For most types all the + program has to do is issue a simple + .IR mount (2) + system call, and no detailed knowledge of the filesystem type is required. +-For a few types however (like nfs, smbfs, ncpfs) ad hoc code is ++For a few types however (like nfs, nfs4, smbfs, ncpfs) ad hoc code is + necessary. The nfs ad hoc code is built in, but smbfs and ncpfs + have a separate mount program. In order to make it possible to + treat all types in a uniform way, mount will execute the program +@@ -451,9 +452,10 @@ or, if that does not exist, + All of the filesystem types listed there will be tried, + except for those that are labeled "nodev" (e.g., + .IR devpts , +-.I proc ++.IR proc , ++.IR nfs , + and +-.IR nfs ). ++.IR nfs4 ). + If + .I /etc/filesystems + ends in a line with a single * only, mount will read +@@ -1243,6 +1245,73 @@ Usually it just causes lots of trouble. + .B nolock + Do not use locking. Do not start lockd. + ++.SH "Mount options for nfs4" ++Instead of a textual option string, parsed by the kernel, the ++.I nfs4 ++file system expects a binary argument of type ++.IR "struct nfs4_mount_data" . ++The program ++.B mount ++itself parses the following options of the form `tag=value', ++and puts them in the structure mentioned: ++.BI rsize= n, ++.BI wsize= n, ++.BI timeo= n, ++.BI retrans= n, ++.BI acregmin= n, ++.BI acregmax= n, ++.BI acdirmin= n, ++.BI acdirmax= n, ++.BI actimeo= n, ++.BI retry= n, ++.BI port= n, ++.BI proto= n, ++.BI clientaddr= n, ++.BI sec= n. ++The option ++.BI addr= n ++is accepted but ignored. ++Also the following Boolean options, possibly preceded by ++.B no ++are recognized: ++.BR bg , ++.BR fg , ++.BR soft , ++.BR hard , ++.BR intr , ++.BR cto , ++.BR ac , ++For details, see ++.BR nfs (5). ++ ++Especially useful options include ++.TP ++.B rsize=32768,wsize=32768 ++This will make your NFS connection faster than with the default ++buffer size of 4096. ++.TP ++.B hard ++The program accessing a file on a NFS mounted file system will hang ++when the server crashes. The process cannot be interrupted or ++killed unless you also specify ++.BR intr . ++When the NFS server is back online the program will continue undisturbed ++from where it was. This is probably what you want. ++.TP ++.B soft ++This option allows the kernel to time out if the NFS server is not ++responding for some time. The time can be ++specified with ++.BR timeo=time . ++This timeout value is expressed in tenths of a second. ++The ++.BR soft ++option might be useful if your NFS server sometimes doesn't respond ++or will be rebooted while some process tries to get a file from the server. ++Avoid using this option with ++.BR proto=udp ++or with a short timeout. ++ + .SH "Mount options for ntfs" + .TP + .BI iocharset= name +diff -puN mount/mount.c~base-nfsv4 mount/mount.c +--- util-linux-2.12/mount/mount.c~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/mount.c 2004-10-13 13:51:50.000000000 -0400 +@@ -821,6 +821,19 @@ retry_nfs: + "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; ++#else ++ die (EX_SOFTWARE, _("mount: this version was compiled " ++ "without support for the type `nfs4'")); ++#endif ++ } + + block_signals (SIG_BLOCK); + +diff -puN /dev/null mount/nfs4mount.c +--- /dev/null 2004-08-19 17:44:20.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs4mount.c 2004-10-13 13:52:38.000000000 -0400 +@@ -0,0 +1,335 @@ ++/* ++ * 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 "sundries.h" ++ ++#include "mount_constants.h" ++#include "nfs4_mount.h" ++ ++#include "nls.h" ++ ++#ifndef NFS_PORT ++#define NFS_PORT 2049 ++#endif ++ ++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; ++} ++ ++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 = 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 = 0; ++ 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")) { ++ if (!strcmp(opteq+1, "krb5")) ++ pseudoflavour = 390003; ++ else if (!strcmp(opteq+1, "krb5i")) ++ pseudoflavour = 390004; ++ else if (!strcmp(opteq+1, "krb5p")) ++ pseudoflavour = 390005; ++ else if (!strcmp(opteq+1, "lipkey")) ++ pseudoflavour = 390006; ++ else if (!strcmp(opteq+1, "lipkey-i")) ++ pseudoflavour = 390007; ++ else if (!strcmp(opteq+1, "lipkey-p")) ++ pseudoflavour = 390008; ++ else if (!strcmp(opteq+1, "spkm3")) ++ pseudoflavour = 390009; ++ else if (!strcmp(opteq+1, "spkm3i")) ++ pseudoflavour = 390010; ++ else if (!strcmp(opteq+1, "spkm3p")) ++ pseudoflavour = 390011; ++ else { ++ printf(_("unknown security type %s\n"), ++ opteq+1); ++ 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 (pseudoflavour != 0) { ++ data.auth_flavourlen = 1; ++ 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); ++ printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp"); ++#endif ++ ++ data.version = NFS4_MOUNT_VERSION; ++ ++ *mount_opts = (char *) &data; ++ /* clean up */ ++ return 0; ++ ++fail: ++ return retval; ++} +diff -puN /dev/null mount/nfs4_mount.h +--- /dev/null 2004-08-19 17:44:20.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs4_mount.h 2004-10-13 13:51:50.000000000 -0400 +@@ -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 +diff -puN mount/nfs.5~base-nfsv4 mount/nfs.5 +--- util-linux-2.12/mount/nfs.5~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs.5 2004-10-13 13:51:50.000000000 -0400 +@@ -3,7 +3,7 @@ + .\" patches. " + .TH NFS 5 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" + .SH NAME +-nfs \- nfs fstab format and options ++nfs \- nfs and nfs4 fstab format and options + .SH SYNOPSIS + .B /etc/fstab + .SH DESCRIPTION +@@ -17,14 +17,51 @@ the local directory that is the mount po + and the NFS specific options that control + the way the filesystem is mounted. + .P +-Here is an example from an \fI/etc/fstab\fP file from an NFS mount. ++Three different versions of the NFS protocol are ++supported by the Linux NFS client: ++NFS version 2, NFS version 3, and NFS version 4. ++To mount via NFS version 2, use the ++.BR nfs ++file system type and specify ++.BR nfsvers=2 . ++Version 2 is the default protocol version for the ++.BR nfs ++file system type when ++.BR nfsvers= ++is not specified on the mount command. ++To mount via NFS version 3, use the ++.BR nfs ++file system type and specify ++.BR nfsvers=3 . ++To mount via NFS version 4, use the ++.BR nfs4 ++file system type. ++The ++.BR nfsvers= ++keyword is not supported for the ++.BR nfs4 ++file system type. ++.P ++These file system types share similar mount options; ++the differences are listed below. ++.P ++Here is an example from an \fI/etc/fstab\fP file for an NFSv2 mount ++over UDP. + .sp + .nf + .ta 2.5i +0.75i +0.75i +1.0i + server:/usr/local/pub /pub nfs rsize=8192,wsize=8192,timeo=14,intr + .fi ++.P ++Here is an example for an NFSv4 mount over TCP using Kerberos ++5 mutual authentication. ++.sp ++.nf ++.ta 2.5i +0.75i +0.75i +1.0i ++server:/usr/local/pub /pub nfs4 proto=tcp,sec=krb5,hard,intr ++.fi + .DT +-.SS Options ++.SS Options for the nfs file system type + .TP 1.5i + .I rsize=n + The number of bytes NFS uses when reading files from an NFS server. +@@ -208,6 +245,163 @@ is the default. + All of the non-value options have corresponding nooption forms. + For example, nointr means don't allow file operations to be + interrupted. ++.SS Options for the nfs4 file system type ++.TP 1.5i ++.I rsize=n ++The number of bytes NFS uses when reading files from an NFS server. ++The default value is dependent on the kernel, currently 4096 bytes. ++(However, throughput is improved greatly by asking for ++.IR rsize=32768 .) ++This value is negotiated with the server. ++.TP 1.5i ++.I wsize=n ++The number of bytes NFS uses when writing files to an NFS server. ++The default value is dependent on the kernel, currently 4096 bytes. ++(However, throughput is improved greatly by asking for ++.IR wsize=32768 .) ++This value is negotiated with the server. ++.TP 1.5i ++.I timeo=n ++The value in tenths of a second before sending the ++first retransmission after an RPC timeout. ++The default value depends on whether ++.IR proto=udp ++or ++.IR proto=tcp ++is in effect (see below). ++The default value for UDP is 7 tenths of a second. ++The default value for TCP is 60 seconds. ++After the first timeout, ++the timeout is doubled after each successive timeout until a maximum ++timeout of 60 seconds is reached or the enough retransmissions ++have occured to cause a major timeout. Then, if the filesystem ++is hard mounted, each new timeout cascade restarts at twice the ++initial value of the previous cascade, again doubling at each ++retransmission. The maximum timeout is always 60 seconds. ++.TP 1.5i ++.I retrans=n ++The number of minor timeouts and retransmissions that must occur before ++a major timeout occurs. The default is 5 timeouts for ++.IR proto=udp ++and 2 timeouts for ++.IR proto=tcp . ++When a major timeout ++occurs, the file operation is either aborted or a "server not responding" ++message is printed on the console. ++.TP 1.5i ++.I acregmin=n ++The minimum time in seconds that attributes of a regular file should ++be cached before requesting fresh information from a server. ++The default is 3 seconds. ++.TP 1.5i ++.I acregmax=n ++The maximum time in seconds that attributes of a regular file can ++be cached before requesting fresh information from a server. ++The default is 60 seconds. ++.TP 1.5i ++.I acdirmin=n ++The minimum time in seconds that attributes of a directory should ++be cached before requesting fresh information from a server. ++The default is 30 seconds. ++.TP 1.5i ++.I acdirmax=n ++The maximum time in seconds that attributes of a directory can ++be cached before requesting fresh information from a server. ++The default is 60 seconds. ++.TP 1.5i ++.I actimeo=n ++Using actimeo sets all of ++.I acregmin, ++.I acregmax, ++.I acdirmin, ++and ++.I acdirmax ++to the same value. ++There is no default value. ++.TP 1.5i ++.I retry=n ++The number of minutes to retry an NFS mount operation ++in the foreground or background before giving up. ++The default value is 10000 minutes, which is roughly one week. ++.TP 1.5i ++.I port=n ++The numeric value of the port to connect to the NFS server on. ++If the port number is 0 (the default) then query the ++remote host's portmapper for the port number to use. ++If the remote host's NFS daemon is not registered with ++its portmapper, the standard NFS port number 2049 is ++used instead. ++.TP 1.5i ++.I proto=n ++Mount the NFS filesystem using a specific network protocol ++instead of the default UDP protocol. ++Many NFS version 4 servers only support TCP. ++Valid protocol types are ++.IR udp ++and ++.IR tcp . ++.TP 1.5i ++.I clientaddr=n ++On a multi-homed client, this ++causes the client to use a specific callback address when ++communicating with an NFS version 4 server. ++This option is currently ignored. ++.TP 1.5i ++.I sec=n ++Specify an advanced security mechanism for this file system. ++To use Kerberos 5 mutual authentication, specify ++.IR krb5 . ++Kerberos 5 integrity checking is specified with ++.IR krb5i , ++and Kerberos 5 privacy is specified with ++.IR krb5p . ++Other security mechanisms may become available in the future. ++.TP 1.5i ++.I bg ++If an NFS mount attempt times out, retry the mount ++in the background. ++After a mount operation is backgrounded, all subsequent mounts ++on the same NFS server will be backgrounded immediately, without ++first attempting the mount. ++A missing mount point is treated as a timeout, ++to allow for nested NFS mounts. ++.TP 1.5i ++.I fg ++If the first NFS mount attempt times out, retry the mount ++in the foreground. ++This is the complement of the ++.I bg ++option, and also the default behavior. ++.TP 1.5i ++.I soft ++If an NFS file operation has a major timeout then report an I/O error to ++the calling program. ++The default is to continue retrying NFS file operations indefinitely. ++.TP 1.5i ++.I hard ++If an NFS file operation has a major timeout then report ++"server not responding" on the console and continue retrying indefinitely. ++This is the default. ++.TP 1.5i ++.I intr ++If an NFS file operation has a major timeout and it is hard mounted, ++then allow signals to interupt the file operation and cause it to ++return EINTR to the calling program. The default is to not ++allow file operations to be interrupted. ++.TP 1.5i ++.I nocto ++Suppress the retrieval of new attributes when creating a file. ++.TP 1.5i ++.I noac ++Disable attribute caching, and force synchronous writes. ++This extracts a ++server performance penalty but it allows two different NFS clients ++to get reasonable good results when both clients are actively ++writing to common filesystem on the server. ++.P ++All of the non-value options have corresponding nooption forms. ++For example, nointr means don't allow file operations to be ++interrupted. + .SH FILES + .I /etc/fstab + .SH "SEE ALSO" +diff -puN mount/nfsmount.c~base-nfsv4 mount/nfsmount.c +--- util-linux-2.12/mount/nfsmount.c~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfsmount.c 2004-10-13 13:51:50.000000000 -0400 +@@ -706,7 +706,10 @@ static inline int + nfsmnt_check_compat(const struct pmap *nfs_pmap, const struct pmap *mnt_pmap) + { + if (nfs_pmap->pm_vers > MAX_NFSPROT) { +- fprintf(stderr, _("NFSv%ld not supported!\n"), nfs_pmap->pm_vers); ++ if (nfs_pmap->pm_vers == 4) ++ fprintf(stderr, _("'vers=4' is not supported. Use '-t nfs4' instead.\n")); ++ else ++ fprintf(stderr, _("NFSv%ld not supported!\n"), nfs_pmap->pm_vers); + goto out_bad; + } + if (mnt_pmap->pm_vers > MAX_MNTPROT) { +diff -puN mount/sundries.h~base-nfsv4 mount/sundries.h +--- util-linux-2.12/mount/sundries.h~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/sundries.h 2004-10-13 13:51:50.000000000 -0400 +@@ -37,6 +37,8 @@ void die (int errcode, const char *fmt, + #ifdef HAVE_NFS + int nfsmount (const char *spec, const char *node, int *flags, + char **orig_opts, char **opt_args, int *version, int running_bg); ++int nfs4mount (const char *spec, const char *node, int *flags, ++ char **orig_opts, char **opt_args, int running_bg); + int nfsumount(const char *spec, const char *opts); + #endif + +diff -puN mount/umount.c~base-nfsv4 mount/umount.c +--- util-linux-2.12/mount/umount.c~base-nfsv4 2004-10-13 13:51:50.000000000 -0400 ++++ util-linux-2.12-bfields/mount/umount.c 2004-10-13 13:51:50.000000000 -0400 +@@ -89,6 +89,9 @@ + /* True if ruid != euid. */ + int suid = 0; + ++/* Needed by nfs4mount.c */ ++int sloppy = 0; ++ + /* + * check_special_umountprog() + * If there is a special umount program for this type, exec it. + diff --git a/util-linux-2.12-03-krb5.dif b/util-linux-2.12-03-krb5.dif new file mode 100644 index 0000000..a2eff07 --- /dev/null +++ b/util-linux-2.12-03-krb5.dif @@ -0,0 +1,255 @@ + +Allow nfs versions 2 and 3 to mount with -osec=krb5. + + +--- + + util-linux-2.12-bfields/mount/nfs.5 | 37 +++++++++----- + util-linux-2.12-bfields/mount/nfs_mount4.h | 18 ++++++- + util-linux-2.12-bfields/mount/nfsmount.c | 74 ++++++++++++++++++++++++++++- + 3 files changed, 112 insertions(+), 17 deletions(-) + +diff -puN mount/nfs.5~krb5 mount/nfs.5 +--- util-linux-2.12/mount/nfs.5~krb5 2004-10-13 14:15:03.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs.5 2004-10-13 14:15:03.000000000 -0400 +@@ -165,7 +165,7 @@ mount daemon program number. + Use an alternate RPC version number to contact the + mount daemon on the remote host. This option is useful + for hosts that can run multiple NFS servers. +-The default value is version 1. ++The default value depends on which kernel you are using. + .TP 1.5i + .I nfsprog=n + Use an alternate RPC program number to contact the +@@ -178,7 +178,7 @@ NFS daemon program number. + Use an alternate RPC version number to contact the + NFS daemon on the remote host. This option is useful + for hosts that can run multiple NFS servers. +-The default value is version 2. ++The default value depends on which kernel you are using. + .TP 1.5i + .I nolock + Disable NFS locking. Do not start lockd. +@@ -230,9 +230,25 @@ Suppress the retrieval of new attributes + .TP 1.5i + .I noac + Disable all forms of attribute caching entirely. This extracts a +-server performance penalty but it allows two different NFS clients +-to get reasonable good results when both clients are actively +-writing to common filesystem on the server. ++significant performance penalty but it allows two different NFS clients ++to get reasonable results when both clients are actively ++writing to a common export on the server. ++.TP 1.5i ++.I sec=mode ++Set the security flavor for this mount to "mode". ++The default setting is \f3sec=sys\f1, which uses local ++unix uids and gids to authenticate NFS operations (AUTH_SYS). ++Other currently supported settings are: ++\f3sec=krb5\f1, which uses Kerberos V5 instead of local unix uids ++and gids to authenticate users; ++\f3sec=krb5i\f1, which uses Kerberos V5 for user authentication ++and performs integrity checking of NFS operations using secure ++checksums to prevent data tampering; and ++\f3sec=krb5p\f1, which uses Kerberos V5 for user authentication ++and integrity checking, and encrypts NFS traffic to prevent ++traffic sniffing (this is the most secure setting). ++Note that there is a performance penalty when using integrity ++or privacy. + .TP 1.5i + .I tcp + Mount the NFS filesystem using the TCP protocol instead of the +@@ -347,15 +363,8 @@ causes the client to use a specific call + communicating with an NFS version 4 server. + This option is currently ignored. + .TP 1.5i +-.I sec=n +-Specify an advanced security mechanism for this file system. +-To use Kerberos 5 mutual authentication, specify +-.IR krb5 . +-Kerberos 5 integrity checking is specified with +-.IR krb5i , +-and Kerberos 5 privacy is specified with +-.IR krb5p . +-Other security mechanisms may become available in the future. ++.I sec=mode ++Same as \f3sec=mode\f1 for the nfs filesystem type (see above). + .TP 1.5i + .I bg + If an NFS mount attempt times out, retry the mount +diff -puN mount/nfs_mount4.h~krb5 mount/nfs_mount4.h +--- util-linux-2.12/mount/nfs_mount4.h~krb5 2004-10-13 14:15:03.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs_mount4.h 2004-10-13 14:16:45.000000000 -0400 +@@ -8,7 +8,7 @@ + * so it is easiest to ignore the kernel altogether (at compile time). + */ + +-#define NFS_MOUNT_VERSION 4 ++#define NFS_MOUNT_VERSION 5 + + struct nfs2_fh { + char data[32]; +@@ -36,6 +36,7 @@ struct nfs_mount_data { + int namlen; /* 2 */ + unsigned int bsize; /* 3 */ + struct nfs3_fh root; /* 4 */ ++ int pseudoflavor; /* 5 */ + }; + + /* bits in the flags field */ +@@ -51,4 +52,19 @@ struct nfs_mount_data { + #define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ + #define NFS_MOUNT_NONLM 0x0200 /* 3 */ + #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 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 + +diff -puN mount/nfsmount.c~krb5 mount/nfsmount.c +--- util-linux-2.12/mount/nfsmount.c~krb5 2004-10-13 14:15:03.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfsmount.c 2004-10-13 14:16:46.000000000 -0400 +@@ -109,6 +109,8 @@ typedef struct { + static const struct timeval TIMEOUT = { 20, 0 }; + static const struct timeval RETRY_TIMEOUT = { 3, 0 }; + ++static int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp); ++ + /* Define the order in which to probe for UDP/TCP services */ + static const u_int * + proto_probelist(const int use_tcp) +@@ -211,8 +213,10 @@ find_kernel_nfs_mount_version(void) { + mnt_version = 4; /* since 2.2.18pre9 */ + else if (kernel_version < MAKE_VERSION(2,3,99)) + mnt_version = 3; ++ else if (kernel_version < MAKE_VERSION(2,6,0)) ++ mnt_version = 4; + else +- mnt_version = 4; /* since 2.3.99pre4 */ ++ mnt_version = 5; + } + if (mnt_version > NFS_MOUNT_VERSION) + mnt_version = NFS_MOUNT_VERSION; +@@ -596,6 +600,40 @@ parse_options(char *old_opts, struct nfs + #endif + } else if (!sloppy) + goto bad_parameter; ++#if NFS_MOUNT_VERSION >= 5 ++ } else if (!strcmp(opt, "sec")) { ++ char *secflavor = opteq+1; ++ /* see RFC 2623 */ ++ if (nfs_mount_version < 5) { ++ printf(_("Warning: ignoring sec=%s option\n"), secflavor); ++ continue; ++ } else if (!strcmp(secflavor, "sys")) ++ data->pseudoflavor = AUTH_SYS; ++ else if (!strcmp(secflavor, "krb5")) ++ data->pseudoflavor = AUTH_GSS_KRB5; ++ else if (!strcmp(secflavor, "krb5i")) ++ data->pseudoflavor = AUTH_GSS_KRB5I; ++ else if (!strcmp(secflavor, "krb5p")) ++ data->pseudoflavor = AUTH_GSS_KRB5P; ++ else if (!strcmp(secflavor, "lipkey")) ++ data->pseudoflavor = AUTH_GSS_LKEY; ++ else if (!strcmp(secflavor, "lipkey-i")) ++ data->pseudoflavor = AUTH_GSS_LKEYI; ++ else if (!strcmp(secflavor, "lipkey-p")) ++ data->pseudoflavor = AUTH_GSS_LKEYP; ++ else if (!strcmp(secflavor, "spkm3")) ++ data->pseudoflavor = AUTH_GSS_SPKM; ++ else if (!strcmp(secflavor, "spkm3i")) ++ data->pseudoflavor = AUTH_GSS_SPKMI; ++ else if (!strcmp(secflavor, "spkm3p")) ++ data->pseudoflavor = AUTH_GSS_SPKMP; ++ else if (!sloppy) { ++ printf(_("Warning: Unrecognized security flavor %s.\n"), ++ secflavor); ++ goto bad_parameter; ++ } ++ data->flags |= NFS_MOUNT_SECFLAVOUR; ++#endif + } else if (!strcmp(opt, "mounthost")) + mounthost=xstrndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); +@@ -745,7 +783,7 @@ nfsmount(const char *spec, const char *n + mntres_t mntres; + + struct stat statbuf; +- char *s, *p; ++ char *s; + int bg, retry; + int retval; + time_t t; +@@ -809,6 +847,7 @@ nfsmount(const char *spec, const char *n + #if NFS_MOUNT_VERSION >= 2 + data.namlen = NAME_MAX; + #endif ++ data.pseudoflavor = AUTH_SYS; + + bg = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ +@@ -847,6 +886,9 @@ nfsmount(const char *spec, const char *n + printf("tcp = %d\n", + (data.flags & NFS_MOUNT_TCP) != 0); + #endif ++#if NFS_MOUNT_VERSION >= 5 ++ printf("sec = %u\n", data.pseudoflavor); ++#endif + #endif + + data.version = nfs_mount_version; +@@ -949,7 +991,9 @@ nfsmount(const char *spec, const char *n + #endif + } else { + #if NFS_MOUNT_VERSION >= 4 ++ mountres3_ok *mountres; + fhandle3 *fhandle; ++ int i, *flavor, yum = 0; + if (mntres.nfsv3.fhs_status != 0) { + fprintf(stderr, + _("mount: %s:%s failed, reason given by server: %s\n"), +@@ -957,6 +1001,32 @@ nfsmount(const char *spec, const char *n + nfs_strerror(mntres.nfsv3.fhs_status)); + goto fail; + } ++#if NFS_MOUNT_VERSION >= 5 ++ mountres = &mntres.nfsv3.mountres3_u.mountinfo; ++ i = mountres->auth_flavours.auth_flavours_len; ++ if (i <= 0) ++ goto noauth_flavours; ++ ++ flavor = mountres->auth_flavours.auth_flavours_val; ++ while (--i >= 0) { ++ if (flavor[i] == data.pseudoflavor) ++ yum = 1; ++#ifdef NFS_MOUNT_DEBUG ++ printf("auth flavor %d: %d\n", ++ i, flavor[i]); ++#endif ++ } ++ if (!yum) { ++ fprintf(stderr, ++ "mount: %s:%s failed, " ++ "security flavor not supported\n", ++ hostname, dirname); ++ /* server has registered us in mtab, send umount */ ++ nfs_call_umount(&mnt_server, &dirname); ++ goto fail; ++ } ++noauth_flavours: ++#endif + fhandle = &mntres.nfsv3.mountres3_u.mountinfo.fhandle; + memset(data.old_root.data, 0, NFS_FHSIZE); + memset(&data.root, 0, sizeof(data.root)); +_ diff --git a/util-linux-2.12-04-modify_mount_to_support_multiple_security_flavors.dif b/util-linux-2.12-04-modify_mount_to_support_multiple_security_flavors.dif new file mode 100644 index 0000000..b10f87c --- /dev/null +++ b/util-linux-2.12-04-modify_mount_to_support_multiple_security_flavors.dif @@ -0,0 +1,172 @@ + +This patch modifies the NFSv4 'mount' command to accept multiple +authentication flavors. This list of flavors will be used during security +negotiation to determine which flavors the user is willing to use (most +preferred flavor is listed first). + +This patch applies on top of CITI's version 2.11z-3 of util-linux. + +The format for passing one flavor is unchanged: + mount -tnfs4 -osec=krb5 server:/ /mnt/nfs4 + +The format for passing multiple flavors is: + mount -tnfs4 -osec=krb5:spkm3p:unix server:/ /mnt/nfs4 + +If no sec= option is given, we assume AUTH_UNIX. + +From Nick Wilson + +--- + + util-linux-2.12-bfields/mount/nfs4mount.c | 102 ++++++++++++++++++++++-------- + 1 files changed, 75 insertions(+), 27 deletions(-) + +diff -puN mount/nfs4mount.c~modify_mount_to_support_multiple_security_flavors mount/nfs4mount.c +--- util-linux-2.12/mount/nfs4mount.c~modify_mount_to_support_multiple_security_flavors 2004-10-13 14:18:23.000000000 -0400 ++++ util-linux-2.12-bfields/mount/nfs4mount.c 2004-10-13 14:21:18.000000000 -0400 +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include "sundries.h" + +@@ -48,6 +49,57 @@ + #define NFS_PORT 2049 + #endif + ++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; +@@ -117,7 +169,8 @@ int nfs4mount(const char *spec, const ch + static char hostdir[1024]; + static char ip_addr[16] = "127.0.0.1"; + static struct sockaddr_in server_addr; +- static int pseudoflavour = 0; ++ static int pseudoflavour[MAX_USER_FLAVOUR]; ++ int num_flavour = 0; + + char *hostname, *dirname, *old_opts; + char new_opts[1024]; +@@ -228,29 +281,9 @@ int nfs4mount(const char *spec, const ch + strncpy(ip_addr,opteq+1, sizeof(ip_addr)); + ip_addr[sizeof(ip_addr)-1] = '\0'; + } else if (!strcmp(opt, "sec")) { +- if (!strcmp(opteq+1, "krb5")) +- pseudoflavour = 390003; +- else if (!strcmp(opteq+1, "krb5i")) +- pseudoflavour = 390004; +- else if (!strcmp(opteq+1, "krb5p")) +- pseudoflavour = 390005; +- else if (!strcmp(opteq+1, "lipkey")) +- pseudoflavour = 390006; +- else if (!strcmp(opteq+1, "lipkey-i")) +- pseudoflavour = 390007; +- else if (!strcmp(opteq+1, "lipkey-p")) +- pseudoflavour = 390008; +- else if (!strcmp(opteq+1, "spkm3")) +- pseudoflavour = 390009; +- else if (!strcmp(opteq+1, "spkm3i")) +- pseudoflavour = 390010; +- else if (!strcmp(opteq+1, "spkm3p")) +- pseudoflavour = 390011; +- else { +- printf(_("unknown security type %s\n"), +- opteq+1); ++ num_flavour = parse_sec(opteq+1, pseudoflavour); ++ if (!num_flavour) + goto fail; +- } + } else if (!strcmp(opt, "addr")) { + /* ignore */; + } else { +@@ -293,10 +326,10 @@ int nfs4mount(const char *spec, const ch + | (nocto ? NFS4_MOUNT_NOCTO : 0) + | (noac ? NFS4_MOUNT_NOAC : 0); + +- if (pseudoflavour != 0) { +- data.auth_flavourlen = 1; +- data.auth_flavours = &pseudoflavour; +- } ++ 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); +@@ -321,6 +354,21 @@ int nfs4mount(const char *spec, const ch + (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 + +_ diff --git a/util-linux-2.12-05-mount_sloppy_fix.dif b/util-linux-2.12-05-mount_sloppy_fix.dif new file mode 100644 index 0000000..56a2c83 --- /dev/null +++ b/util-linux-2.12-05-mount_sloppy_fix.dif @@ -0,0 +1,20 @@ + + +--- + + util-linux-2.12-bfields/mount/nfsmount.c | 2 +- + 1 files changed, 1 insertion(+), 1 deletion(-) + +diff -puN mount/nfsmount.c~mount_sloppy_fix mount/nfsmount.c +--- util-linux-2.12/mount/nfsmount.c~mount_sloppy_fix 2004-12-13 12:21:52.000000000 -0500 ++++ util-linux-2.12-bfields/mount/nfsmount.c 2004-12-13 12:21:52.000000000 -0500 +@@ -712,7 +712,7 @@ parse_options(char *old_opts, struct nfs + data->flags |= NFS_MOUNT_BROKEN_SUID; + } + #endif +- } else { ++ } else if (!sloppy) { + bad_option: + printf(_("Unsupported nfs mount option: " + "%s%s\n"), val ? "" : "no", opt); +_ diff --git a/util-linux-2.12-06-redhat-nfs-mount.dif b/util-linux-2.12-06-redhat-nfs-mount.dif new file mode 100644 index 0000000..d2c3fde --- /dev/null +++ b/util-linux-2.12-06-redhat-nfs-mount.dif @@ -0,0 +1,228 @@ + + +Changes from redhat srpm. + +--- + + util-linux-2.12-bfields/mount/mount.c | 2 + util-linux-2.12-bfields/mount/nfs4mount.c | 20 +++++++++ + util-linux-2.12-bfields/mount/nfsmount.c | 61 +++++++++++++++++++++++++----- + 3 files changed, 72 insertions(+), 11 deletions(-) + +diff -puN mount/mount.c~redhat-nfs-mount mount/mount.c +--- util-linux-2.12/mount/mount.c~redhat-nfs-mount 2004-12-13 12:34:18.000000000 -0500 ++++ util-linux-2.12-bfields/mount/mount.c 2004-12-13 12:34:18.000000000 -0500 +@@ -829,6 +829,7 @@ retry_nfs: + 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'")); +@@ -836,6 +837,7 @@ retry_nfs: + } + + block_signals (SIG_BLOCK); ++nosigblock: + + if (!fake) + mnt5_res = guess_fstype_and_mount (spec, node, &types, flags & ~MS_NOSYS, +diff -puN mount/nfs4mount.c~redhat-nfs-mount mount/nfs4mount.c +--- util-linux-2.12/mount/nfs4mount.c~redhat-nfs-mount 2004-12-13 12:34:18.000000000 -0500 ++++ util-linux-2.12-bfields/mount/nfs4mount.c 2004-12-13 12:34:18.000000000 -0500 +@@ -37,6 +37,12 @@ + #include + #include + #include ++#ifdef HAVE_rpcsvc_nfs_prot_h ++#include ++#else ++#include ++#define nfsstat nfs_stat ++#endif + + #include "sundries.h" + +@@ -49,6 +55,9 @@ + #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; +@@ -225,7 +234,7 @@ int nfs4mount(const char *spec, const ch + + bg = 0; + soft = 0; +- intr = 0; ++ intr = NFS4_MOUNT_INTR; + nocto = 0; + noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ +@@ -374,10 +383,19 @@ int nfs4mount(const char *spec, const ch + + 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; + } +diff -puN mount/nfsmount.c~redhat-nfs-mount mount/nfsmount.c +--- util-linux-2.12/mount/nfsmount.c~redhat-nfs-mount 2004-12-13 12:34:18.000000000 -0500 ++++ util-linux-2.12-bfields/mount/nfsmount.c 2004-12-13 12:34:18.000000000 -0500 +@@ -111,6 +111,25 @@ static const struct timeval RETRY_TIMEOU + + static int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp); + ++int clnt_ping(struct sockaddr_in *, const u_long, const u_long, const u_int); ++ ++/* 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)); ++ } ++} ++ + /* Define the order in which to probe for UDP/TCP services */ + static const u_int * + proto_probelist(const int use_tcp) +@@ -252,14 +271,15 @@ nfs_gethostbyname(const char *hostname, + * we're requesting, we open and RPC client, and fire off a NULL + * RPC call. + */ +-static int ++int + clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers, + const u_int prot) + { +- CLIENT *clnt; ++ CLIENT *clnt=NULL; + int sock, stat; + static char clnt_res; + ++ rpc_createerr.cf_stat = stat = 0; + sock = RPC_ANYSOCK; + switch(prot) { + case IPPROTO_UDP: +@@ -285,6 +305,7 @@ clnt_ping(struct sockaddr_in *saddr, con + close(sock); + if (stat != RPC_PROGVERSMISMATCH) + return 1; ++ + out_bad: + return 0; + } +@@ -308,9 +329,9 @@ probe_port(clnt_addr_t *server, + *p_prot; + const u_short port = (u_short) pmap->pm_port; + u_short p_port; +- + p_prot = prot ? &prot : protos; + p_vers = vers ? &vers : versions; ++ rpc_createerr.cf_stat = 0; + for (;;) { + saddr->sin_port = htons(PMAPPORT); + p_port = pmap_getport(saddr, prog, *p_vers, *p_prot); +@@ -338,6 +359,7 @@ probe_port(clnt_addr_t *server, + pmap->pm_prot = *p_prot; + if (!port) + pmap->pm_port = p_port; ++ rpc_createerr.cf_stat = 0; + return 1; + } + +@@ -394,6 +416,8 @@ probe_bothports(clnt_addr_t *mnt_server, + return 1; + memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); + } ++ if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) ++ break; + memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); + } + out_bad: +@@ -477,11 +501,23 @@ nfs_call_mount(clnt_addr_t *mnt_server, + enum clnt_stat stat; + int msock; + +- + if (!probe_bothports(mnt_server, nfs_server)) { +- if (report_errs) +- fprintf(stderr,_("mount: failed to probe ports on NFS server %s\n"), ++ if (report_errs) { ++ fprintf(stderr, "mount to NFS server '%s' failed", + *nfs_server->hostname); ++ if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) { ++ fprintf(stderr, ": server is down.\n"); ++ } else if (nfs_server->pmap.pm_prot) { ++ fprintf(stderr, ": possible invalid protocol.\n"); ++ } else if (nfs_server->pmap.pm_port) { ++ fprintf(stderr, ": possible invalid port.\n"); ++ } else { ++ fprintf(stderr, ".\n"); ++ } ++ if (verbose) { ++ rpc_strerror(); ++ } ++ } + goto out_bad; + } + +@@ -743,15 +779,19 @@ parse_options(char *old_opts, struct nfs + static inline int + nfsmnt_check_compat(const struct pmap *nfs_pmap, const struct pmap *mnt_pmap) + { +- if (nfs_pmap->pm_vers > MAX_NFSPROT) { ++ if (nfs_pmap->pm_vers && ++ (nfs_pmap->pm_vers > MAX_NFSPROT || nfs_pmap->pm_vers < 2)) { + if (nfs_pmap->pm_vers == 4) +- fprintf(stderr, _("'vers=4' is not supported. Use '-t nfs4' instead.\n")); ++ fprintf(stderr, _("'vers=4' is not supported. " ++ "Use '-t nfs4' instead.\n")); + else +- fprintf(stderr, _("NFSv%ld not supported!\n"), nfs_pmap->pm_vers); ++ fprintf(stderr, _("NFS version %ld is not supported.\n"), ++ nfs_pmap->pm_vers); + goto out_bad; + } + if (mnt_pmap->pm_vers > MAX_MNTPROT) { +- fprintf(stderr, _("NFS mount v%ld not supported!\n"), mnt_pmap->pm_vers); ++ fprintf(stderr, _("NFS mount version %ld s not supported.\n"), ++ mnt_pmap->pm_vers); + goto out_bad; + } + return 1; +@@ -1144,6 +1184,7 @@ nfs_call_umount(clnt_addr_t *mnt_server, + res = nfs2_umount(argp, clnt); + break; + default: ++ break; + } + mnt_closeclnt(clnt, msock); + if (res == RPC_SUCCESS) +_