From a0520fa1a41bd33815b331b660b4545f2723495c Mon Sep 17 00:00:00 2001 From: Amit Gud Date: Mon, 12 Jun 2006 19:06:36 -0400 Subject: [PATCH] Move NFS mount code from util-linux to nfs-utils - part 1 Adds the mount directory and the code to mount and umount the NFS file system. Signed-off-by: Amit Gud Signed-off-by: Steve Dickson --- ChangeLog | 4 + configure.in | 8 + utils/Makefile.am | 4 + utils/mount/Makefile.am | 38 + utils/mount/mount.c | 387 ++++++++++ utils/mount/mount.nfs.man | 100 +++ utils/mount/mount_constants.h | 53 ++ utils/mount/nfs4_mount.h | 85 +++ utils/mount/nfs4mount.c | 444 ++++++++++++ utils/mount/nfs_mount.h | 84 +++ utils/mount/nfsmount.c | 1244 +++++++++++++++++++++++++++++++++ utils/mount/nfsmount.x | 336 +++++++++ utils/mount/nfsumount.c | 392 +++++++++++ utils/mount/nfsumount.h | 11 + utils/mount/umount.nfs.man | 74 ++ 15 files changed, 3264 insertions(+) create mode 100644 utils/mount/Makefile.am create mode 100644 utils/mount/mount.c create mode 100644 utils/mount/mount.nfs.man create mode 100644 utils/mount/mount_constants.h create mode 100644 utils/mount/nfs4_mount.h create mode 100644 utils/mount/nfs4mount.c create mode 100644 utils/mount/nfs_mount.h create mode 100644 utils/mount/nfsmount.c create mode 100644 utils/mount/nfsmount.x create mode 100644 utils/mount/nfsumount.c create mode 100644 utils/mount/nfsumount.h create mode 100644 utils/mount/umount.nfs.man diff --git a/ChangeLog b/ChangeLog index 87bc0d0..35de983 100644 --- a/ChangeLog +++ b/ChangeLog @@ -41,6 +41,10 @@ Old glibc's don't have ifaddrs.h +2006-06-12 Amit Gud + Added the mount functionality from util-linux. + Added --without-mount configure option. + 2006-04-12 NeilBrown Set version to 1.0.8, aclocal -I aclocal ; autoheader ; automake ; autoconf diff --git a/configure.in b/configure.in index 7733077..5eb1d79 100644 --- a/configure.in +++ b/configure.in @@ -107,6 +107,13 @@ AC_ARG_ENABLE(rquotad, fi AM_CONDITIONAL(CONFIG_RQUOTAD, [test "$enable_rquotad" = "yes"]) +AC_ARG_WITH(mount, + [AC_HELP_STRING([--without-mount], + [Create mount.nfs and do not use the util-linux mount(8) functionality. By default it doesn't.])], + use_mount=$withval, + use_mount=yes) + AM_CONDITIONAL(CONFIG_NOMOUNT, [test "$use_mount" = "no"]) + # Check whether user wants TCP wrappers support AC_TCP_WRAPPERS @@ -314,6 +321,7 @@ AC_CONFIG_FILES([ utils/gssd/Makefile utils/idmapd/Makefile utils/lockd/Makefile + utils/mount/Makefile utils/mountd/Makefile utils/nfsd/Makefile utils/nfsstat/Makefile diff --git a/utils/Makefile.am b/utils/Makefile.am index 1d2e776..259bec3 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -14,6 +14,10 @@ if CONFIG_GSS OPTDIRS += gssd endif +if CONFIG_NOMOUNT +OPTDIRS += mount +endif + SUBDIRS = \ exportfs \ lockd \ diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am new file mode 100644 index 0000000..084a5db --- /dev/null +++ b/utils/mount/Makefile.am @@ -0,0 +1,38 @@ +## Process this file with automake to produce Makefile.in + +man8_MANS = mount.nfs.man umount.nfs.man + +sbin_PROGRAMS = mount.nfs +EXTRA_DIST = nfsmount.x $(man8_MANS) +mount_nfs_SOURCES = mount.c nfsmount.c nfs4mount.c nfsumount.c \ + nfsmount_xdr.c mount_constants.h nfs4_mount.h nfsmount.h \ + nfs_mount4.h + +mount_nfs_LDADD = ../../support/nfs/libnfs.a + +MAINTAINERCLEANFILES = Makefile.in + +install-exec-hook: + (cd $(DESTDIR)$(sbindir) && \ + ln -sf $(sbin_PROGRAMS) mount.nfs4 && \ + ln -sf $(sbin_PROGRAMS) umount.nfs && \ + ln -sf $(sbin_PROGRAMS) umount.nfs4) +uninstall-hook: + (cd $(DESTDIR)$(sbindir) && \ + rm -f mount.nfs4 umount.nfs umount.nfs4) + + +install-man-links: + (cd $(DESTDIR)$(man8dir) && \ + for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \ + inst=`echo $$m | sed -e 's/man$$/8/'`; \ + rm -f $$inst ; \ + done) + +uninstall-man-links: + (cd $(DESTDIR)$(man8dir) && \ + for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \ + inst=`echo $$m | sed -e 's/man$$/8/'`; \ + rm -f $$inst ; \ + done) + diff --git a/utils/mount/mount.c b/utils/mount/mount.c new file mode 100644 index 0000000..270a179 --- /dev/null +++ b/utils/mount/mount.c @@ -0,0 +1,387 @@ +/* + * mount.c -- Linux NFS mount + * + * Copyright (C) 2006 Amit Gud + * + * - Basic code and wrapper around mount and umount code of NFS. + * Based on util-linux/mount/mount.c. + * + * 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. + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fstab.h" +#include "xcommon.h" +#include "mount_constants.h" +#include "nfs_paths.h" + +#include "nfs_mount.h" +#include "nfs4_mount.h" +#include "nfsumount.h" +#include "nfsmount.h" + +char *progname; +int nomtab; +int verbose; +int mounttype; + +static struct option longopts[] = { + { "fake", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "nfsvers", 1, 0, 't' }, + { "bind", 0, 0, 128 }, + { "replace", 0, 0, 129 }, + { "after", 0, 0, 130 }, + { "before", 0, 0, 131 }, + { "over", 0, 0, 132 }, + { "move", 0, 0, 133 }, + { "rbind", 0, 0, 135 }, + { NULL, 0, 0, 0 } +}; + +/* Map from -o and fstab option strings to the flag argument to mount(2). */ +struct opt_map { + const char *opt; /* option name */ + int skip; /* skip in mtab option string */ + int inv; /* true if flag value should be inverted */ + int mask; /* flag mask value */ +}; + +static const struct opt_map opt_map[] = { + { "defaults", 0, 0, 0 }, /* default options */ + { "ro", 1, 0, MS_RDONLY }, /* read-only */ + { "rw", 1, 1, MS_RDONLY }, /* read-write */ + { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ + { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ + { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ + { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ + { "dev", 0, 1, MS_NODEV }, /* interpret device files */ + { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ + { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ + { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ + { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */ + { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ + { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */ + { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */ + + /* add new options here */ +#ifdef MS_NOSUB + { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ + { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ +#endif +#ifdef MS_SILENT + { "quiet", 0, 0, MS_SILENT }, /* be quiet */ + { "loud", 0, 1, MS_SILENT }, /* print out messages. */ +#endif +#ifdef MS_MANDLOCK + { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ + { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ +#endif +#ifdef MS_NOATIME + { "atime", 0, 1, MS_NOATIME }, /* Update access time */ + { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ +#endif +#ifdef MS_NODIRATIME + { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ + { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ +#endif + { NULL, 0, 0, 0 } +}; + +/* Try to build a canonical options string. */ +static char * fix_opts_string (int flags, const char *extra_opts) { + const struct opt_map *om; + char *new_opts; + + new_opts = xstrdup((flags & MS_RDONLY) ? "ro" : "rw"); + for (om = opt_map; om->opt != NULL; om++) { + if (om->skip) + continue; + if (om->inv || !om->mask || (flags & om->mask) != om->mask) + continue; + new_opts = xstrconcat3(new_opts, ",", om->opt); + flags &= ~om->mask; + } + if (extra_opts && *extra_opts) { + new_opts = xstrconcat3(new_opts, ",", extra_opts); + } + return new_opts; +} + + +int add_mtab(char *fsname, char *mount_point, char *fstype, int flags, char *opts, int freq, int passno) +{ + struct mntent ment; + int fd; + FILE *mtab; + + ment.mnt_fsname = fsname; + ment.mnt_dir = mount_point; + ment.mnt_type = fstype; + ment.mnt_opts = fix_opts_string(flags, opts); + ment.mnt_freq = 0; + ment.mnt_passno= 0; + + if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) { + fprintf(stderr, "Can't get "MOUNTED"~ lock file"); + return 1; + } + close(fd); + + if ((mtab = setmntent(MOUNTED, "a+")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED); + return 1; + } + + if (addmntent(mtab, &ment) == 1) { + fprintf(stderr, "Can't write mount entry"); + return 1; + } + + if (fchmod(fileno(mtab), 0644) == -1) { + fprintf(stderr, "Can't set perms on " MOUNTED); + return 1; + } + + endmntent(mtab); + + if (unlink(MOUNTED"~") == -1) { + fprintf(stderr, "Can't remove "MOUNTED"~"); + return 1; + } + + return 0; +} + +int do_mount_syscall(char *spec, char *node, char *type, int flags, void *data) +{ + return mount(spec, node, type, flags, data); +} + +void mount_usage() +{ + printf("usage: %s remotetarget dir [-rvVwfnh] [-t version] [-o nfsoptions]\n", progname); + printf("options:\n\t-r\t\tMount file system readonly\n"); + printf("\t-v\t\tVerbose\n"); + printf("\t-V\t\tPrint version\n"); + printf("\t-w\t\tMount file system read-write\n"); + printf("\t-f\t\tFake mount, don't actually mount\n"); + printf("\t-n\t\tDo not update /etc/mtab\n"); + printf("\t-h\t\tPrint this help\n"); + printf("\tversion\t\tnfs4 - NFS version 4, nfs - older NFS version supported\n"); + printf("\tnfsoptions\tRefer mount.nfs(8) or nfs(5)\n\n"); +} + +static inline void +parse_opt(const char *opt, int *mask, char *extra_opts, int len) { + const struct opt_map *om; + + for (om = opt_map; om->opt != NULL; om++) { + if (!strcmp (opt, om->opt)) { + if (om->inv) + *mask &= ~om->mask; + else + *mask |= om->mask; + return; + } + } + + len -= strlen(extra_opts); + + if (*extra_opts && --len > 0) + strcat(extra_opts, ","); + + if ((len -= strlen(opt)) > 0) + strcat(extra_opts, opt); +} + +/* Take -o options list and compute 4th and 5th args to mount(2). flags + gets the standard options (indicated by bits) and extra_opts all the rest */ +static void parse_opts (const char *options, int *flags, char **extra_opts) +{ + if (options != NULL) { + char *opts = xstrdup(options); + char *opt; + int len = strlen(opts) + 20; + + *extra_opts = xmalloc(len); + **extra_opts = '\0'; + + for (opt = strtok(opts, ","); opt; opt = strtok(NULL, ",")) + parse_opt(opt, flags, *extra_opts, len); + + free(opts); + } + +} + +static void mount_error(char *node) +{ + switch(errno) { + case ENOTDIR: + printf("%s: mount point %s is not a directory\n", progname, node); + break; + case EBUSY: + printf("%s: %s is already mounted or busy\n", progname, node); + break; + case ENOENT: + printf("%s: mount point %s does not exist\n", progname, node); + break; + default: + printf("%s: %s\n", progname, strerror(errno)); + } +} + +int main(int argc, char *argv[]) +{ + int c, flags = 0, nfs_mount_vers = 0, mnt_err = 1, fake = 0; + char *spec, *mount_point, *extra_opts = NULL; + char *mount_opts = NULL, *p; + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + if (getuid() != 0) { + printf("%s: only root can do that.\n", progname); + exit(1); + } + + if(!strncmp(progname, "umount", strlen("umount"))) { + if(argc < 2) { + umount_usage(); + exit(1); + } + return(nfsumount(argc, argv)); + } + + if ((argc < 2)) { + mount_usage(); + exit(1); + } + + if(argv[1][0] == '-') { + if(argv[1][1] == 'V') + printf("%s ("PACKAGE_STRING")\n", progname); + else + mount_usage(); + return 0; + } + + while ((c = getopt_long (argc - 2, argv + 2, "rt:vVwfno:h", + longopts, NULL)) != -1) { + switch (c) { + case 'r': + flags |= MS_RDONLY; + break; + case 't': + nfs_mount_vers = (strncmp(optarg, "nfs4", 4)) ? 0 : 4; + break; + case 'v': + ++verbose; + break; + case 'V': + printf("%s: ("PACKAGE_STRING")\n", progname); + return 0; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'f': + ++fake; + break; + case 'n': + ++nomtab; + break; + case 'o': /* specify mount options */ + if (mount_opts) + mount_opts = xstrconcat3(mount_opts, ",", optarg); + else + mount_opts = xstrdup(optarg); + break; + case 128: /* bind */ + mounttype = MS_BIND; + break; + case 129: /* replace */ + mounttype = MS_REPLACE; + break; + case 130: /* after */ + mounttype = MS_AFTER; + break; + case 131: /* before */ + mounttype = MS_BEFORE; + break; + case 132: /* over */ + mounttype = MS_OVER; + break; + case 133: /* move */ + mounttype = MS_MOVE; + break; + case 135: /* rbind */ + mounttype = MS_BIND | MS_REC; + break; + case 'h': + default: + mount_usage(); + exit(1); + } + } + + spec = argv[1]; + mount_point = canonicalize(argv[2]); + + parse_opts(mount_opts, &flags, &extra_opts); + + if (!strcmp(progname, "mount.nfs4") || nfs_mount_vers == 4) { + nfs_mount_vers = 4; + mnt_err = nfs4mount(spec, mount_point, &flags, &extra_opts, &mount_opts, 0); + } + else { + if (!strcmp(progname, "mount.nfs")) { + mnt_err = nfsmount(spec, mount_point, &flags, + &extra_opts, &mount_opts, &nfs_mount_vers, 0); + } + } + + if (!mnt_err && !fake) { + mnt_err = do_mount_syscall(spec, mount_point, nfs_mount_vers == 4 ? "nfs4" : "nfs", flags, mount_opts); + + if(mnt_err) { + mount_error(mount_point); + exit(-1); + } + + if(!nomtab) + add_mtab(spec, mount_point, nfs_mount_vers == 4 ? "nfs4" : "nfs", + flags, extra_opts, 0, 0); + } + + return 0; +} + diff --git a/utils/mount/mount.nfs.man b/utils/mount/mount.nfs.man new file mode 100644 index 0000000..231e04f --- /dev/null +++ b/utils/mount/mount.nfs.man @@ -0,0 +1,100 @@ +.\"@(#)mount.nfs.8" +.TH MOUNT.NFS 8 "5 Jun 2006" +.SH NAME +mount.nfs, mount.nfs4 \- mount a Network File System +.SH SYNOPSIS +.BI "mount.nfs" " remotetarget dir" " [\-rvVwfnh ] [\-t " version "] [\-o " options "] +.SH DESCRIPTION +.BR mount.nfs +is a part of +.BR nfs (5) +utilities package, which provides NFS client functionality. + +.BR mount.nfs +is meant to be used by the +.BR mount (8) +command for mounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality. + +.BR mount.nfs4 +is used for mounting NFSv4 file system, while +.BR mount.nfs +is used to mount NFS file systems versions 3 or 2. +.I remotetarget +is a server share usually in the form of +.BR servername:/path/to/share. +.I dir +is the directory on which the file system is to be mounted. + +.SH OPTIONS +.TP +.BI "\-r" +Mount file system readonly. +.TP +.BI "\-v" +Be verbose. +.TP +.BI "\-V" +Print version. +.TP +.BI "\-w" +Mount file system read-write. +.TP +.BI "\-f" +Fake mount. Don't actually call the mount system call. +.TP +.BI "\-n" +Do not update +.I /etc/mtab. +By default, an entry is created in +.I /etc/mtab +for every mounted file system. Use this option to skip making an entry. +.TP +.BI "\-h" +Print help message. +.TP +.BI "version" +Specify NFS file system version. Either +.BR nfs4 +or +.BR nfs. +If the command is +.BR mount.nfs4 +or the +.I vfstype +option of +.BR mount(8) +command is nfs4, then version 4 is used, else version 3 or 2 is used. +.TP +.BI "nfsoptions" +Refer to +.BR nfs(5) +or +.BR mount(8) +manual pages. + +.SH NOTE +For further information please refer +.BR nfs (5) +and +.BR mount (8) +manual pages. + +.SH FILES +.TP 18n +.I /etc/fstab +file system table +.TP +.I /etc/mtab +table of mounted file systems + +.PD +.SH "SEE ALSO" +.BR nfs (5), +.BR mount (8), + +.SH BUGS +Please notify current developers of NFS of any bugs in the current software or mail nfs@lists.sourceforge.net + +.SH "AUTHOR" +Amit Gud + diff --git a/utils/mount/mount_constants.h b/utils/mount/mount_constants.h new file mode 100644 index 0000000..19a7bf5 --- /dev/null +++ b/utils/mount/mount_constants.h @@ -0,0 +1,53 @@ +#ifndef _NFS_MOUNT_CONSTANTS_H +#define _NFS_MOUNT_CONSTANTS_H + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#endif + +#ifndef MS_ACTION_MASK +#define MS_ACTION_MASK 0x380 +/* Remount, but new filesystem may be different from old. Atomic + (i.e. there is no interval when nothing is mounted at the mountpoint). + If new fs differs from the old one and old is busy - -EBUSY. */ +#define MS_REPLACE 0x080 /* 128 */ +/* After, Before: as soon as we get unions these will add a new member + in the end or beginning of the chain. Fail if there is a stack + on the mountpoint. */ +#define MS_AFTER 0x100 /* 256 */ +#define MS_BEFORE 0x180 +/* Over: if nothing mounted on a mountpoint - same as if none of these +flags had been set; if we have a union with more than one element - fail; +if we have a stack or plain mount - mount atop of it, forming a stack. */ +#define MS_OVER 0x200 /* 512 */ +#endif +#ifndef MS_NOATIME +#define MS_NOATIME 0x400 /* 1024: Do not update access times. */ +#endif +#ifndef MS_NODIRATIME +#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ +#endif +#ifndef MS_BIND +#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ +#endif +#ifndef MS_MOVE +#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#endif +#ifndef MS_REC +#define MS_REC 0x4000 /* 16384: Recursive loopback */ +#endif +#ifndef MS_VERBOSE +#define MS_VERBOSE 0x8000 /* 32768 */ +#endif +/* + * Magic mount flag number. Had to be or-ed to the flag values. + */ +#ifndef MS_MGC_VAL +#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ +#endif +#ifndef MS_MGC_MSK +#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ +#endif + +#endif /* _NFS_MOUNT_CONSTANTS_H */ + diff --git a/utils/mount/nfs4_mount.h b/utils/mount/nfs4_mount.h new file mode 100644 index 0000000..74c9b95 --- /dev/null +++ b/utils/mount/nfs4_mount.h @@ -0,0 +1,85 @@ +#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 + +int nfs4mount(const char *, const char *, int *, char **, + char **, int); + +#endif diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c new file mode 100644 index 0000000..b59c27c --- /dev/null +++ b/utils/mount/nfs4mount.c @@ -0,0 +1,444 @@ +/* + * 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 + * + * 2006-06-06 Amit Gud + * - Moved to nfs-utils/utils/mount from util-linux/mount. + */ + +#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 "nls.h" +#include "conn.h" +#include "xcommon.h" + +#include "nfs4_mount.h" +#include "nfs_mount.h" + +#if defined(VAR_LOCK_DIR) +#define DEFAULT_DIR VAR_LOCK_DIR +#else +#define DEFAULT_DIR "/var/lock/subsys" +#endif + +extern int verbose; + +char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd"; +#define idmapd_check() do { \ + if (access(IDMAPLCK, F_OK)) { \ + printf(_("Warning: rpc.idmapd appears not to be running.\n" \ + " All uids will be mapped to the nobody uid.\n")); \ + } \ +} while(0); + +char *GSSDLCK = DEFAULT_DIR "/rpcgssd"; +#define gssd_check() do { \ + if (access(GSSDLCK, F_OK)) { \ + printf(_("Warning: rpc.gssd appears not to be running.\n")); \ + } \ +} while(0); + +#ifndef NFS_PORT +#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; + + 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[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; + time_t timeout, t; + + 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 { + 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); + + /* + * Give a warning if the rpc.idmapd daemon is not running + */ + idmapd_check(); + + if (num_flavour == 0) + pseudoflavour[num_flavour++] = AUTH_UNIX; + else { + /* + * ditto with rpc.gssd daemon + */ + gssd_check(); + } + 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 + + timeout = time(NULL) + 60 * retry; + data.version = NFS4_MOUNT_VERSION; + for (;;) { + if (verbose) { + fprintf(stderr, + "mount: pinging: prog %d vers %d prot %s port %d\n", + NFS_PROGRAM, 4, data.proto == IPPROTO_UDP ? "udp" : "tcp", + ntohs(server_addr.sin_port)); + } + clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto); + if (rpc_createerr.cf_stat == RPC_SUCCESS) + break; + + switch(rpc_createerr.cf_stat){ + case RPC_TIMEDOUT: + break; + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; + default: + mount_errors(hostname, 0, bg); + goto fail; + } + t = time(NULL); + if (t >= timeout) { + mount_errors(hostname, 0, bg); + goto fail; + } + mount_errors(hostname, 1, bg); + continue; + } + + *mount_opts = (char *) &data; + /* clean up */ + return 0; + +fail: + return retval; +} diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h new file mode 100644 index 0000000..1fd74c8 --- /dev/null +++ b/utils/mount/nfs_mount.h @@ -0,0 +1,84 @@ +/* + * We want to be able to compile mount on old kernels in such a way + * that the binary will work well on more recent kernels. + * Thus, if necessary we teach nfsmount.c the structure of new fields + * that will come later. + * + * Moreover, the new kernel includes conflict with glibc includes + * so it is easiest to ignore the kernel altogether (at compile time). + */ + +#ifndef _NFS_MOUNT_H +#define _NFS_MOUNT_H + +#include +#include + +#define NFS_MOUNT_VERSION 6 +#define NFS_MAX_CONTEXT_LEN 256 + +struct nfs2_fh { + char data[32]; +}; +struct nfs3_fh { + unsigned short size; + unsigned char data[64]; +}; + +struct nfs_mount_data { + int version; /* 1 */ + int fd; /* 1 */ + struct nfs2_fh old_root; /* 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 */ + struct sockaddr_in addr; /* 1 */ + char hostname[256]; /* 1 */ + 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 */ + +#define NFS_MOUNT_SOFT 0x0001 /* 1 */ +#define NFS_MOUNT_INTR 0x0002 /* 1 */ +#define NFS_MOUNT_SECURE 0x0004 /* 1 */ +#define NFS_MOUNT_POSIX 0x0008 /* 1 */ +#define NFS_MOUNT_NOCTO 0x0010 /* 1 */ +#define NFS_MOUNT_NOAC 0x0020 /* 1 */ +#define NFS_MOUNT_TCP 0x0040 /* 2 */ +#define NFS_MOUNT_VER3 0x0080 /* 3 */ +#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ +#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 + +int nfsmount(const char *, const char *, int *, char **, char **, int *, int); +void mount_errors(char *, int, int); + +#endif /* _NFS_MOUNT_H */ diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c new file mode 100644 index 0000000..1266660 --- /dev/null +++ b/utils/mount/nfsmount.c @@ -0,0 +1,1244 @@ +/* + * nfsmount.c -- Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * 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. + * + * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port + * numbers to be specified on the command line. + * + * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - added Native Language Support + * + * Modified by Olaf Kirch and Trond Myklebust for new NFS code, + * plus NFSv3 stuff. + * + * 2006-06-06 Amit Gud + * - Moved with modifcations to nfs-utils/utils/mount from util-linux/mount. + */ + +/* + * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "conn.h" +#include "xcommon.h" +#include "nfsmount.h" +#include "nfsumount.h" +#include "nfs_mount.h" +#include "mount_constants.h" +#include "nls.h" + +#ifdef HAVE_RPCSVC_NFS_PROT_H +#include +#else +#include +#define nfsstat nfs_stat +#endif + +#ifndef NFS_PORT +#define NFS_PORT 2049 +#endif +#ifndef NFS_FHSIZE +#define NFS_FHSIZE 32 +#endif + +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; + +static char errbuf[BUFSIZ]; +static char *erreob = &errbuf[BUFSIZ]; +extern int verbose; + +/* Convert RPC errors into strings */ +int rpc_strerror(int); +int rpc_strerror(int spos) +{ + int cf_stat = rpc_createerr.cf_stat; + int pos=0, cf_errno = rpc_createerr.cf_error.re_errno; + char *ptr, *estr = clnt_sperrno(cf_stat); + char *tmp; + + if (estr) { + if ((ptr = index(estr, ':'))) + estr = ++ptr; + + tmp = &errbuf[spos]; + if (cf_stat == RPC_SYSTEMERROR) + pos = snprintf(tmp, (erreob - tmp), + "System Error: %s", strerror(cf_errno)); + else + pos = snprintf(tmp, (erreob - tmp), "RPC Error:%s", estr); + } + return (pos); +} +void mount_errors(char *, int, int); +void mount_errors(char *server, int will_retry, int bg) +{ + int pos = 0; + char *tmp; + static int onlyonce = 0; + + tmp = &errbuf[pos]; + if (bg) + pos = snprintf(tmp, (erreob - tmp), + "mount to NFS server '%s' failed: ", server); + else + pos = snprintf(tmp, (erreob - tmp), + "mount: mount to NFS server '%s' failed: ", server); + + tmp = &errbuf[pos]; + if (rpc_createerr.cf_stat == RPC_TIMEDOUT) { + pos = snprintf(tmp, (erreob - tmp), "timed out %s", + will_retry ? "(retrying)" : "(giving up)"); + } else { + pos += rpc_strerror(pos); + tmp = &errbuf[pos]; + if (bg) { + pos = snprintf(tmp, (erreob - tmp), " %s", + will_retry ? "(retrying)" : "(giving up)"); + } + } + if (bg) { + if (onlyonce++ < 1) + openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH); + syslog(LOG_ERR, "%s.", errbuf); + } else + fprintf(stderr, "%s.\n", errbuf); +} + +/* Define the order in which to probe for UDP/TCP services */ +enum plist { + use_tcp = 0, + udp_tcp, + udp_only, +}; +static const u_int * +proto_probelist(enum plist list) +{ + static const u_int probe_udp_tcp[] = { IPPROTO_UDP, IPPROTO_TCP, 0 }; + static const u_int probe_both[] = { IPPROTO_TCP, IPPROTO_UDP, 0 }; + static const u_int probe_udponly[] = { IPPROTO_UDP, 0 }; + + if (list == use_tcp) + return probe_both; + if (list == udp_tcp) + return probe_udp_tcp; + 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; + } +} + +static int +linux_version_code(void) { + struct utsname my_utsname; + int p, q, r; + + if (uname(&my_utsname) == 0) { + p = atoi(strtok(my_utsname.release, ".")); + q = atoi(strtok(NULL, ".")); + r = atoi(strtok(NULL, ".")); + return MAKE_VERSION(p,q,r); + } + return 0; +} + +/* + * Unfortunately, the kernel prints annoying console messages + * in case of an unexpected nfs mount version (instead of + * just returning some error). Therefore we'll have to try + * and figure out what version the kernel expects. + * + * Variables: + * NFS_MOUNT_VERSION: these nfsmount sources at compile time + * nfs_mount_version: version this source and running kernel can handle + */ +int nfs_mount_version = NFS_MOUNT_VERSION; + +int +find_kernel_nfs_mount_version(void) { + static int kernel_version = -1; + 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)) + mnt_version = 1; + else if (kernel_version < MAKE_VERSION(2,2,18)) + mnt_version = 3; + else if (kernel_version < MAKE_VERSION(2,3,0)) + 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,3)) + mnt_version = 4; + else + mnt_version = 6; + } + if (mnt_version > NFS_MOUNT_VERSION) + mnt_version = NFS_MOUNT_VERSION; + return mnt_version; +} + +int nfs_gethostbyname(const char *, struct sockaddr_in *); +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; +} + +/* + * getport() is very similar to pmap_getport() with + * the exception this version uses a non-reserve ports + * instead of reserve ports since reserve ports + * are not needed for pmap requests. + */ +static u_short +getport( + struct sockaddr_in *saddr, + u_long prog, + u_long vers, + u_int prot) +{ + u_short port; + int socket; + CLIENT *clnt = NULL; + struct pmap parms; + enum clnt_stat stat; + + saddr->sin_port = htons (PMAPPORT); + socket = get_socket(saddr, prot, FALSE); + + switch (prot) { + case IPPROTO_UDP: + clnt = clntudp_bufcreate(saddr, + PMAPPROG, PMAPVERS, TIMEOUT, &socket, + UDPMSGSIZE, UDPMSGSIZE); + break; + case IPPROTO_TCP: + clnt = clnttcp_create(saddr, + PMAPPROG, PMAPVERS, &socket, 50, 500); + break; + } + if (clnt != NULL) { + parms.pm_prog = prog; + parms.pm_vers = vers; + parms.pm_prot = prot; + parms.pm_port = 0; /* not needed or used */ + + stat = clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap, + (caddr_t)&parms, (xdrproc_t)xdr_u_short, (caddr_t)&port, TIMEOUT); + if (stat) { + clnt_geterr(clnt, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = stat; + } + clnt_destroy(clnt); + if (stat != RPC_SUCCESS) + port = 0; + else if (port == 0) + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + } + if (socket != 1) + close(socket); + + return port; +} + +/* + * 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, *p_vers; + const u_int prot = (u_int)pmap->pm_prot, + *p_prot; + const u_short port = (u_short) pmap->pm_port; + u_long vers = pmap->pm_vers; + 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 = getport(saddr, prog, *p_vers, *p_prot); + if (p_port) { + if (!port || port == p_port) { + saddr->sin_port = htons(p_port); + if (verbose) { + fprintf(stderr, + "mount: trying %s prog %ld vers %ld prot %s port %d\n", + inet_ntoa(saddr->sin_addr), prog, *p_vers, + *p_prot == IPPROTO_UDP ? "udp" : "tcp", p_port); + } + if (clnt_ping(saddr, prog, *p_vers, *p_prot)) + goto out_ok; + if (rpc_createerr.cf_stat == RPC_TIMEDOUT) + goto out_bad; + } + } + if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) + goto out_bad; + + if (!prot) { + if (*++p_prot) + continue; + p_prot = protos; + } + if (vers == pmap->pm_vers) { + p_vers = versions; + vers = 0; + } + if (vers || !*++p_vers) + break; + } +out_bad: + 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; + rpc_createerr.cf_stat = 0; + 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 ? use_tcp : udp_only); + return probe_port(nfs_server, probe_vers, probe_prot); +} + +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 ? udp_tcp : udp_only); + 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 = nfsvers_to_mnt(nfs_pmap->pm_vers); + if ((res = probe_mntport(mnt_server)) != 0) + return 1; + memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); + } + switch (rpc_createerr.cf_stat) { + case RPC_PROGVERSMISMATCH: + case RPC_PROGNOTREGISTERED: + break; + default: + goto out_bad; + } + 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 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) +{ + CLIENT *clnt; + enum clnt_stat stat; + int msock; + + if (!probe_bothports(mnt_server, nfs_server)) + goto out_bad; + + clnt = mnt_openclnt(mnt_server, &msock); + 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) { + clnt_geterr(clnt, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = stat; + } + 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'; +/* printf("opt=%s\n", opt); */ + 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, "mounthost")) + mounthost=xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,")); + 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 + goto bad_parameter; +#endif + } else if (!strcmp(opt, "addr")) { + /* ignore */; + continue; + } else + 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; + mnt_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; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; +#endif + } else + 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 { + 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,")); + else if (!strcmp(opt, "context")) { + char *context = opteq + 1; + + if (strlen(context) > NFS_MAX_CONTEXT_LEN) { + printf(_("context parameter exceeds limit of %d\n"), + NFS_MAX_CONTEXT_LEN); + goto bad_parameter; + } + strncpy(data->context, context, NFS_MAX_CONTEXT_LEN); + } 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; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; + } else { + mnt_pmap->pm_prot = IPPROTO_UDP; + 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; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; + } else { + nfs_pmap->pm_prot = IPPROTO_UDP; + mnt_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; + } + } else if (!strcmp(opt, "acl")) { + data->flags &= ~NFS_MOUNT_NOACL; + if (!val) + data->flags |= NFS_MOUNT_NOACL; +#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 && + (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")); + else + 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 version %ld s 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) +{ + static char *prev_bg_host; + char hostdir[1024]; + char *hostname, *dirname, *old_opts, *mounthost = NULL; + char new_opts[1024], cbuf[1024]; + static struct nfs_mount_data data; + int val; + static int doonce = 0; + + 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 bg, retry; + int retval; + time_t t; + time_t prevt; + time_t timeout; + + /* The version to try is either specified or 0 + In case it is 0 we tell the caller what we tried */ + if (!*nfs_mount_vers) + *nfs_mount_vers = find_kernel_nfs_mount_version(); + nfs_mount_version = *nfs_mount_vers; + + retval = EX_FAIL; + fsock = -1; + if (strlen(spec) >= sizeof(hostdir)) { + fprintf(stderr, _("mount: " + "excessively long host:dir argument\n")); + goto fail; + } + strcpy(hostdir, spec); + if ((s = strchr(hostdir, ':'))) { + 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")); + } + } else { + fprintf(stderr, + _("mount: " + "directory to mount not in host:dir format\n")); + goto fail; + } + + 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(nfs_saddr->sin_addr); + old_opts = *extra_opts; + if (!old_opts) + old_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.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; +#if NFS_MOUNT_VERSION >= 2 + data.namlen = NAME_MAX; +#endif + data.pseudoflavor = AUTH_SYS; + + bg = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ + + 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; + + if (retry == 10000 && !bg) + retry = 2; /* reset for fg mounts */ + + +#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", + nfs_pmap->pm_port, bg, retry, data.flags); + printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", + 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 ", + (data.flags & NFS_MOUNT_SOFT) != 0, + (data.flags & NFS_MOUNT_INTR) != 0, + (data.flags & NFS_MOUNT_POSIX) != 0, + (data.flags & NFS_MOUNT_NOCTO) != 0, + (data.flags & NFS_MOUNT_NOAC) != 0); +#if NFS_MOUNT_VERSION >= 2 + printf("tcp = %d ", + (data.flags & NFS_MOUNT_TCP) != 0); +#endif +#if NFS_MOUNT_VERSION >= 4 + printf("noacl = %d ", (data.flags & NFS_MOUNT_NOACL) != 0); +#endif +#if NFS_MOUNT_VERSION >= 5 + printf("sec = %u ", data.pseudoflavor); +#endif + printf("\n"); +#endif + + data.version = nfs_mount_version; + *mount_opts = (char *) &data; + + if (*flags & MS_REMOUNT) + goto out_ok; + + /* + * If the previous mount operation on the same host was + * backgrounded, and the "bg" for this mount is also set, + * give up immediately, to avoid the initial timeout. + */ + if (bg && !running_bg && + prev_bg_host && strcmp(hostname, prev_bg_host) == 0) { + if (retry > 0) + retval = EX_BG; + return retval; + } + + /* create mount deamon client */ + + /* + * The following loop implements the mount retries. On the first + * call, "running_bg" is 0. When the mount times out, and the + * "bg" option is set, the exit status EX_BG will be returned. + * For a backgrounded mount, there will be a second call by the + * child process with "running_bg" set to 1. + * + * The case where the mount point is not present and the "bg" + * option is set, is treated as a timeout. This is done to + * support nested mounts. + * + * The "retry" count specified by the user is the number of + * minutes to retry before giving up. + * + * Only the first error message will be displayed. + */ + 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 */ + if (running_bg) { + sleep(val); /* 1, 2, 4, 8, 16, 30, ... */ + val *= 2; + if (val > 30) + val = 30; + } + } else { + int stat; + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + + stat = nfs_call_mount(&mnt_server, &nfs_server, + &dirname, &mntres); + if (stat) + break; + memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); + memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); + prevt = t; + } + if (!bg) { + switch(rpc_createerr.cf_stat){ + case RPC_TIMEDOUT: + break; + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; + default: + mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + t = time(NULL); + if (t >= timeout) { + mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + mount_errors(*nfs_server.hostname, 1, bg); + continue; + } + if (!running_bg) { + prev_bg_host = xstrdup(hostname); + if (retry > 0) + retval = EX_BG; + goto fail; + } + t = time(NULL); + if (t >= timeout) { + mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + if (doonce++ < 1) + mount_errors(*nfs_server.hostname, 1, bg); + } + + if (nfs_pmap->pm_vers == 2) { + if (mntres.nfsv2.fhs_status != 0) { + fprintf(stderr, + _("mount: %s:%s failed, reason given by server: %s\n"), + hostname, dirname, + nfs_strerror(mntres.nfsv2.fhs_status)); + goto fail; + } + memcpy(data.root.data, + (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 *) mntres.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); +#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"), + hostname, dirname, + 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)); + data.root.size = fhandle->fhandle3_len; + memcpy(data.root.data, + (char *) fhandle->fhandle3_val, + fhandle->fhandle3_len); + + data.flags |= NFS_MOUNT_VER3; +#endif + } + + /* create nfs socket for kernel */ + + if (nfs_pmap->pm_prot == IPPROTO_TCP) + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + perror(_("nfs socket")); + goto fail; + } + if (bindresvport(fsock, 0) < 0) { + perror(_("nfs bindresvport")); + goto fail; + } +#ifdef NFS_MOUNT_DEBUG + printf(_("using port %d for nfs deamon\n"), nfs_pmap->pm_port); +#endif + 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 *) 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 *) nfs_saddr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)); + + 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; + } + + snprintf(cbuf, sizeof(cbuf)-1, "addr=%s", s); + strcat(new_opts, cbuf); + + *extra_opts = xstrdup(new_opts); + return 0; + + /* abort */ + fail: + if (fsock != -1) + close(fsock); + return retval; +} + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + * + * Andreas Schwab : change errno: + * "after #include the symbol errno is reserved for any use, + * it cannot even be used as a struct tag or field name". + */ + +#ifndef EDQUOT +#define EDQUOT ENOSPC +#endif + +static struct { + enum nfsstat stat; + int errnum; +} nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, EIO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, +#ifdef NFSERR_INVAL + { NFSERR_INVAL, EINVAL }, /* that Sun forgot */ +#endif + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, + { NFSERR_DQUOT, EDQUOT }, + { NFSERR_STALE, ESTALE }, +#ifdef EWFLUSH + { NFSERR_WFLUSH, EWFLUSH }, +#endif + /* Throw in some NFSv3 values for even more fun (HP returns these) */ + { 71, EREMOTE }, + + { -1, EIO } +}; + +static char *nfs_strerror(int stat) +{ + int i; + static char buf[256]; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return strerror(nfs_errtbl[i].errnum); + } + sprintf(buf, _("unknown nfs status return value: %d"), stat); + return buf; +} diff --git a/utils/mount/nfsmount.x b/utils/mount/nfsmount.x new file mode 100644 index 0000000..ad4bf99 --- /dev/null +++ b/utils/mount/nfsmount.x @@ -0,0 +1,336 @@ +%/* +% * Sun RPC is a product of Sun Microsystems, Inc. and is provided for +% * unrestricted use provided that this legend is included on all tape +% * media and as a part of the software program in whole or part. Users +% * may copy or modify Sun RPC without charge, but are not authorized +% * to license or distribute it to anyone else except as part of a product or +% * program developed by the user or with the express written consent of +% * Sun Microsystems, Inc. +% * +% * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE +% * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR +% * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. +% * +% * Sun RPC is provided with no support and without any obligation on the +% * part of Sun Microsystems, Inc. to assist in its use, correction, +% * modification or enhancement. +% * +% * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE +% * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC +% * OR ANY PART THEREOF. +% * +% * In no event will Sun Microsystems, Inc. be liable for any lost revenue +% * or profits or other special, indirect and consequential damages, even if +% * Sun has been advised of the possibility of such damages. +% * +% * Sun Microsystems, Inc. +% * 2550 Garcia Avenue +% * Mountain View, California 94043 +% */ + +%/* +% * Copyright (c) 1985, 1990 by Sun Microsystems, Inc. +% */ +% +%/* from @(#)mount.x 1.3 91/03/11 TIRPC 1.0 */ + +/* + * Protocol description for the mount program + */ + +#ifdef RPC_HDR +%#ifndef _rpcsvc_mount_h +%#define _rpcsvc_mount_h +#endif + +#ifdef RPC_CLNT +%#include /* for memset() */ +#endif +%#include + +const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */ +const MNTNAMLEN = 255; /* maximum bytes in a name argument */ +const FHSIZE = 32; /* size in bytes of a file handle */ +const FHSIZE3 = 64; /* size in bytes of a file handle */ + +/* + * The fhandle is the file handle that the server passes to the client. + * All file operations are done using the file handles to refer to a file + * or a directory. The file handle can contain whatever information the + * server needs to distinguish an individual file. + */ +typedef opaque fhandle[FHSIZE]; +typedef opaque fhandle3; + +enum mountstat3 { + MNT_OK = 0, /* no error */ + MNT3ERR_PERM = 1, /* not owner */ + MNT3ERR_NOENT = 2, /* No such file or directory */ + MNT3ERR_IO = 5, /* I/O error */ + MNT3ERR_ACCES = 13, /* Permission denied */ + MNT3ERR_NOTDIR = 20, /* Not a directory */ + MNT3ERR_INVAL = 22, /* Invalid argument */ + MNT3ERR_NAMETOOLONG = 63, /* File name too long */ + MNT3ERR_NOTSUPP = 10004, /* Operation not supported */ + MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */ +}; + +/* + * If a status of zero is returned, the call completed successfully, and + * a file handle for the directory follows. A non-zero status indicates + * some sort of error. The status corresponds with UNIX error numbers. + */ +union fhstatus switch (unsigned fhs_status) { +case 0: + fhandle fhs_fhandle; +default: + void; +}; + +struct mountres3_ok { + fhandle3 fhandle; + int auth_flavours<>; +}; + +union mountres3 switch (mountstat3 fhs_status) { +case MNT_OK: + mountres3_ok mountinfo; +default: + void; +}; + +/* + * The type dirpath is the pathname of a directory + */ +typedef string dirpath; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * A list of who has what mounted + */ +typedef struct mountbody *mountlist; +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +/* + * A list of netgroups + */ +typedef struct groupnode *groups; +struct groupnode { + name gr_name; + groups gr_next; +}; + +/* + * A list of what is exported and to whom + */ +typedef struct exportnode *exports; +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +/* + * POSIX pathconf information + */ +struct ppathcnf { + int pc_link_max; /* max links allowed */ + short pc_max_canon; /* max line len for a tty */ + short pc_max_input; /* input a tty can eat all at once */ + short pc_name_max; /* max file name length (dir entry) */ + short pc_path_max; /* max path name length (/x/y/x/.. ) */ + short pc_pipe_buf; /* size of a pipe (bytes) */ + u_char pc_vdisable; /* safe char to turn off c_cc[i] */ + char pc_xxx; /* alignment padding; cc_t == char */ + short pc_mask[2]; /* validity and boolean bits */ +}; + +program MOUNTPROG { + /* + * Version one of the mount protocol communicates with version two + * of the NFS protocol. The only connecting point is the fhandle + * structure, which is the same for both protocols. + */ + version MOUNTVERS { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + } = 1; + + /* + * Version two of the mount protocol communicates with version two + * of the NFS protocol. + * The only difference from version one is the addition of a POSIX + * pathconf call. + */ + version MOUNTVERS_POSIX { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + + /* + * POSIX pathconf info (Sun hack) + */ + ppathcnf + MOUNTPROC_PATHCONF(dirpath) = 7; + } = 2; + version MOUNT_V3 { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC3_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + mountres3 + MOUNTPROC3_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC3_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC3_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC3_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC3_EXPORT(void) = 5; + + } = 3; +} = 100005; + +#ifdef RPC_HDR +%#endif /*!_rpcsvc_mount_h*/ +#endif diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c new file mode 100644 index 0000000..d1d476a --- /dev/null +++ b/utils/mount/nfsumount.c @@ -0,0 +1,392 @@ +/* + * nfsumount.c -- Linux NFS umount + * Copyright (C) 2006 Amit Gud + * + * - Basic code and wrapper around NFS umount code originally + * in util-linux/mount/nfsmount.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "xcommon.h" +#include "fstab.h" +#include "nls.h" +#include "conn.h" + +#include "mount_constants.h" +#include "nfsmount.h" +#include "nfsumount.h" + +#if !defined(MNT_FORCE) +/* dare not try to include -- lots of errors */ +#define MNT_FORCE 1 +#endif + +#if !defined(MNT_DETACH) +#define MNT_DETACH 2 +#endif + +extern char *progname; +extern int nfs_mount_version; +extern int nomtab; +extern int verbose; +int force; +int lazy; +int remount; + +extern int find_kernel_nfs_mount_version(void); +extern int probe_mntport(clnt_addr_t *); +extern int nfs_gethostbyname(const char *, struct sockaddr_in *); + +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); +} + +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); + 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: + break; + } + mnt_closeclnt(clnt, msock); + if (res == RPC_SUCCESS) + return 1; + out_bad: + return 0; +} + +u_int get_mntproto(const char *); +u_int +get_mntproto(const char *dirname) +{ + FILE *mtab; + struct mntent mntbuf; + char tmpbuf[BUFSIZ]; + u_int proto = IPPROTO_TCP; /* assume tcp */ + + mtab = setmntent ("/proc/mounts", "r"); + if (mtab == NULL) + mtab = setmntent (_PATH_MOUNTED, "r"); + if (mtab == NULL) + return proto; + + while(getmntent_r(mtab, &mntbuf, tmpbuf, sizeof (tmpbuf))) { + if (strcmp(mntbuf.mnt_type, "nfs")) + continue; + if (strcmp(dirname, mntbuf.mnt_fsname)) + continue; + if (hasmntopt(&mntbuf, "udp")) + proto = IPPROTO_UDP; + break; + } + endmntent (mtab); + + return proto; +} + +/* complain about a failed umount */ +static void complain(int err, const char *dev) { + switch (err) { + case ENXIO: + nfs_error (_("umount: %s: invalid block device"), dev); break; + case EINVAL: + nfs_error (_("umount: %s: not mounted"), dev); break; + case EIO: + nfs_error (_("umount: %s: can't write superblock"), dev); break; + case EBUSY: + /* Let us hope fstab has a line "proc /proc ..." + and not "none /proc ..."*/ + nfs_error (_("umount: %s: device is busy"), dev); break; + case ENOENT: + nfs_error (_("umount: %s: not found"), dev); break; + case EPERM: + nfs_error (_("umount: %s: must be superuser to umount"), dev); break; + case EACCES: + nfs_error (_("umount: %s: block devices not permitted on fs"), dev); break; + default: + nfs_error (_("umount: %s: %s"), dev, strerror (err)); break; + } +} + +int add_mtab2(const char *spec, const char *node, const char *type, + const char *opts, struct mntentchn *mc) +{ + int umnt_err, umnt_err2, res; + + umnt_err = umnt_err2 = 0; + if (lazy) { + res = umount2 (node, MNT_DETACH); + if (res < 0) + umnt_err = errno; + goto writemtab; + } + + if (force) { + res = umount2 (node, MNT_FORCE); + if (res == -1) { + int errsv = errno; + perror("umount2"); + errno = errsv; + if (errno == ENOSYS) { + if (verbose) + printf(_("no umount2, trying umount...\n")); + res = umount (node); + } + } + } else + res = umount (node); + + if (res < 0) { + umnt_err = errno; + /* A device might have been mounted on a node that has since + been deleted or renamed, so if node fails, also try spec. */ + /* Note that this is incorrect in case spec was mounted + several times. */ + /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ + if (umnt_err != EBUSY && strcmp(node, spec)) { + if (verbose) + printf (_("could not umount %s - trying %s instead\n"), + node, spec); + res = umount (spec); + if (res < 0) + umnt_err2 = errno; + /* Do not complain about remote NFS mount points */ + if (errno == ENOENT && index(spec, ':')) + umnt_err2 = 0; + } + } + + if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { + /* Umount failed - let us try a remount */ + res = mount(spec, node, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (res == 0) { + nfs_mntent_t remnt; + fprintf(stderr, + _("umount: %s busy - remounted read-only\n"), + spec); + remnt.mnt_type = remnt.mnt_fsname = NULL; + remnt.mnt_dir = xstrdup(node); + remnt.mnt_opts = xstrdup("ro"); + if (!nomtab) + update_mtab(node, &remnt); + return 0; + } else if (errno != EBUSY) { /* hmm ... */ + perror("remount"); + fprintf(stderr, + _("umount: could not remount %s read-only\n"), + spec); + } + } + + if (res >= 0) { + /* Umount succeeded */ + if (verbose) + printf (_("%s umounted\n"), spec); + } + + writemtab: + if (!nomtab && + (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { + update_mtab (node, NULL); + } + + if (res >= 0) + return 0; + + if (umnt_err2) + complain(umnt_err2, spec); + if (umnt_err && umnt_err != umnt_err2) + complain(umnt_err, node); + return 1; +} + +/* + * Returns 1 if everything went well, else 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; + pmap->pm_prot = get_mntproto(spec); + 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: + printf("%s: %s: not found or not mounted\n", progname, spec); + return 0; +} + +static struct option umount_longopts[] = +{ + { "force", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "verbose", 0, 0, 'v' }, + { "read-only", 0, 0, 'r' }, + { NULL, 0, 0, 0 } +}; + +void umount_usage() +{ + printf("usage: %s dir [-fvnrlh]\n", progname); + printf("options:\n\t-f\t\tforce unmount\n"); + printf("\t-v\t\tverbose\n"); + printf("\t-n\t\tDo not update /etc/mtab\n"); + printf("\t-r\t\tremount\n"); + printf("\t-l\t\tlazy unmount\n"); + printf("\t-h\t\tprint this help\n\n"); +} + +int nfsumount(int argc, char *argv[]) +{ + int c, ret; + char *spec; + struct mntentchn *mc; + + spec = argv[1]; + + argv += 1; + argc -= 1; + + while ((c = getopt_long (argc, argv, "fvnrlh", + umount_longopts, NULL)) != -1) { + + switch (c) { + case 'f': + ++force; + break; + case 'v': + ++verbose; + break; + case 'n': + ++nomtab; + break; + case 'r': + ++remount; + break; + case 'l': + ++lazy; + break; + case 'h': + default: + umount_usage(); + return 0; + } + } + + mc = getmntdirbackward(spec, NULL); + if (!mc) + mc = getmntdevbackward(spec, NULL); + if (!mc && verbose) + printf(_("Could not find %s in mtab\n"), spec); + + if(mc) { + ret = _nfsumount(mc->m.mnt_fsname, mc->m.mnt_opts); + if(ret) + ret = add_mtab2(mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc->m.mnt_opts, mc); + } + else { + ret = _nfsumount(spec, NULL); + if(ret) + ret = add_mtab2(spec, spec, spec, spec, NULL); + } + + return(ret); +} + diff --git a/utils/mount/nfsumount.h b/utils/mount/nfsumount.h new file mode 100644 index 0000000..c089275 --- /dev/null +++ b/utils/mount/nfsumount.h @@ -0,0 +1,11 @@ +#ifndef _NFS_UMOUNT_H +#define _NFS_UMOUNT_H + +#include "conn.h" +#include "nfsmount.h" + +int nfsumount(int, char **); +int nfs_call_umount(clnt_addr_t *, dirpath *); +void umount_usage(); + +#endif diff --git a/utils/mount/umount.nfs.man b/utils/mount/umount.nfs.man new file mode 100644 index 0000000..8214c9a --- /dev/null +++ b/utils/mount/umount.nfs.man @@ -0,0 +1,74 @@ +.\"@(#)mount.nfs.8" +.TH UMOUNT.NFS 8 "6 Jun 2006" +.SH NAME +umount.nfs, umount.nfs4 \- unmount a Network File System +.SH SYNOPSIS +.BI "umount.nfs" " dir" " [\-fvnrlh ]" +.SH DESCRIPTION +.BR umount.nfs +and +.BR umount.nfs4 +are a part of +.BR nfs (5) +utilities package, which provides NFS client functionality. + +.BR umount.nfs4 +and +.BR umount.nfs +are meant to be used by the +.BR umount (8) +command for unmounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality. + +.I dir +is the directory on which the file system is mounted. + +.SH OPTIONS +.TP +.BI "\-f" +Force unmount the file system in case of unreachable NFS system. +.TP +.BI "\-v" +Be verbose. +.TP +.BI "\-n" +Do not update +.I /etc/mtab. +By default, an entry is created in +.I /etc/mtab +for every mounted file system. Use this option to skip deleting an entry. +.TP +.BI "\-r" +In case unmounting fails, try to mount read-only. +.TP +.BI "\-l" +Lazy unmount. Detach the file system from the file system hierarchy now, and cleanup all references to the file system as soon as it is not busy anymore. +.TP +.BI "\-h" +Print help message. + +.SH NOTE +For further information please refer +.BR nfs (5) +and +.BR umount (8) +manual pages. + +.SH FILES +.TP 18n +.I /etc/fstab +file system table +.TP +.I /etc/mtab +table of mounted file systems + +.PD +.SH "SEE ALSO" +.BR nfs (5), +.BR umount (8), + +.SH BUGS +Please notify current developers of NFS of any bugs in the current software or mail nfs@lists.sourceforge.net + +.SH "AUTHOR" +Amit Gud + -- 2.39.5