From: Amit Gud <agud@redhat.com>
To: Neil Brown <neilb@suse.de>
Cc: gud@eth.net, nfs@lists.sourceforge.net,
Steve Dickson <SteveD@redhat.com>
Subject: [PATCH 1 / 1] Move NFS mount code from util-linux to nfs-utils - take2
Date: Mon, 12 Jun 2006 19:06:36 -0400 [thread overview]
Message-ID: <448DF37C.4000109@redhat.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 221 bytes --]
Neil,
It avoids code duplication (of parsing the options and error checking)
if we don't branch two different binaries for the NFSv4 and older versions.
AG
--
May the source be with you.
http://www.cis.ksu.edu/~gud
[-- Attachment #2: nfsmount-migration-to-nfsutils-v4.patch --]
[-- Type: text/plain, Size: 95854 bytes --]
Adds the mount directory and the code to mount and umount the NFS file system.
Signed-off-by: Amit Gud <agud@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
diff -uprN nfs-utils-1.0.8/utils/Makefile.am nfs-utils-1.0.8-ag/utils/Makefile.am
--- nfs-utils-1.0.8/utils/Makefile.am 2006-03-26 19:30:08.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/Makefile.am 2006-06-12 17:56:53.725636000 -0400
@@ -14,6 +14,10 @@ if CONFIG_GSS
OPTDIRS += gssd
endif
+if CONFIG_NOMOUNT
+OPTDIRS += mount
+endif
+
SUBDIRS = \
exportfs \
lockd \
diff -uprN nfs-utils-1.0.8/utils/mount/Makefile.am nfs-utils-1.0.8-ag/utils/mount/Makefile.am
--- nfs-utils-1.0.8/utils/mount/Makefile.am 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/Makefile.am 2006-06-12 15:09:22.646612000 -0400
@@ -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 -uprN nfs-utils-1.0.8/utils/mount/mount.c nfs-utils-1.0.8-ag/utils/mount/mount.c
--- nfs-utils-1.0.8/utils/mount/mount.c 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/mount.c 2006-06-12 15:09:22.652611000 -0400
@@ -0,0 +1,387 @@
+/*
+ * mount.c -- Linux NFS mount
+ *
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - 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 <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <getopt.h>
+#include <mntent.h>
+
+#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 -uprN nfs-utils-1.0.8/utils/mount/mount_constants.h nfs-utils-1.0.8-ag/utils/mount/mount_constants.h
--- nfs-utils-1.0.8/utils/mount/mount_constants.h 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/mount_constants.h 2006-06-12 15:09:22.658611000 -0400
@@ -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 -uprN nfs-utils-1.0.8/utils/mount/mount.nfs.man nfs-utils-1.0.8-ag/utils/mount/mount.nfs.man
--- nfs-utils-1.0.8/utils/mount/mount.nfs.man 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/mount.nfs.man 2006-06-12 15:09:22.663612000 -0400
@@ -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 <agud@redhat.com>
+
diff -uprN nfs-utils-1.0.8/utils/mount/nfs4mount.c nfs-utils-1.0.8-ag/utils/mount/nfs4mount.c
--- nfs-utils-1.0.8/utils/mount/nfs4mount.c 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfs4mount.c 2006-06-12 15:09:22.670611000 -0400
@@ -0,0 +1,444 @@
+/*
+ * nfs4mount.c -- Linux NFS mount
+ * Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ * 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 <agud@redhat.com>
+ * - Moved to nfs-utils/utils/mount from util-linux/mount.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/auth.h>
+#include <rpc/rpc.h>
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#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 -uprN nfs-utils-1.0.8/utils/mount/nfs4_mount.h nfs-utils-1.0.8-ag/utils/mount/nfs4_mount.h
--- nfs-utils-1.0.8/utils/mount/nfs4_mount.h 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfs4_mount.h 2006-06-12 15:09:22.675612000 -0400
@@ -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 -uprN nfs-utils-1.0.8/utils/mount/nfsmount.c nfs-utils-1.0.8-ag/utils/mount/nfsmount.c
--- nfs-utils-1.0.8/utils/mount/nfsmount.c 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfsmount.c 2006-06-12 15:09:22.685612000 -0400
@@ -0,0 +1,1244 @@
+/*
+ * nfsmount.c -- Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * 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 <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - 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 <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <paths.h>
+#include <syslog.h>
+
+#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 <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#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 <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> 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 -uprN nfs-utils-1.0.8/utils/mount/nfs_mount.h nfs-utils-1.0.8-ag/utils/mount/nfs_mount.h
--- nfs-utils-1.0.8/utils/mount/nfs_mount.h 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfs_mount.h 2006-06-12 15:09:22.691611000 -0400
@@ -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 <netinet/in.h>
+#include <arpa/inet.h>
+
+#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 -uprN nfs-utils-1.0.8/utils/mount/nfsmount.x nfs-utils-1.0.8-ag/utils/mount/nfsmount.x
--- nfs-utils-1.0.8/utils/mount/nfsmount.x 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfsmount.x 2006-06-12 15:09:22.697611000 -0400
@@ -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 <string.h> /* for memset() */
+#endif
+%#include <asm/types.h>
+
+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<FHSIZE3>;
+
+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<MNTPATHLEN>;
+
+/*
+ * The type name is used for arbitrary names (hostnames, groupnames)
+ */
+typedef string name<MNTNAMLEN>;
+
+/*
+ * 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 -uprN nfs-utils-1.0.8/utils/mount/nfsumount.c nfs-utils-1.0.8-ag/utils/mount/nfsumount.c
--- nfs-utils-1.0.8/utils/mount/nfsumount.c 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfsumount.c 2006-06-12 15:09:22.704611000 -0400
@@ -0,0 +1,392 @@
+/*
+ * nfsumount.c -- Linux NFS umount
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - 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 <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <ctype.h>
+
+#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 <linux/mount.h> -- 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 -uprN nfs-utils-1.0.8/utils/mount/nfsumount.h nfs-utils-1.0.8-ag/utils/mount/nfsumount.h
--- nfs-utils-1.0.8/utils/mount/nfsumount.h 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/nfsumount.h 2006-06-12 15:09:22.709611000 -0400
@@ -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 -uprN nfs-utils-1.0.8/utils/mount/umount.nfs.man nfs-utils-1.0.8-ag/utils/mount/umount.nfs.man
--- nfs-utils-1.0.8/utils/mount/umount.nfs.man 1969-12-31 19:00:00.000000000 -0500
+++ nfs-utils-1.0.8-ag/utils/mount/umount.nfs.man 2006-06-12 15:09:22.714612000 -0400
@@ -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 <agud@redhat.com>
+
--- nfs-utils-1.0.8/configure.in 2006-04-11 22:55:51.000000000 -0400
+++ nfs-utils-1.0.8-ag/configure.in 2006-06-12 18:14:08.534639000 -0400
@@ -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
@@ -313,6 +320,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
--- nfs-utils-1.0.8/ChangeLog 2006-04-11 22:55:50.000000000 -0400
+++ nfs-utils-1.0.8-ag/ChangeLog 2006-06-12 18:21:33.277295000 -0400
@@ -1,3 +1,7 @@
+2006-06-12 Amit Gud <agud@redhat.com>
+ Added the mount functionality from util-linux.
+ Added --without-mount configure option.
+
2006-04-12 NeilBrown <neilb@suse.de>
Set version to 1.0.8,
aclocal -I aclocal ; autoheader ; automake ; autoconf
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
[-- Attachment #4: Type: text/plain, Size: 140 bytes --]
_______________________________________________
NFS maillist - NFS@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/nfs
next reply other threads:[~2006-06-12 23:03 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-06-12 23:06 Amit Gud [this message]
2006-06-13 2:24 ` [PATCH 1 / 1] Move NFS mount code from util-linux to nfs-utils - take2 Ian Kent
2006-06-13 3:15 ` Amit Gud
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=448DF37C.4000109@redhat.com \
--to=agud@redhat.com \
--cc=SteveD@redhat.com \
--cc=gud@eth.net \
--cc=neilb@suse.de \
--cc=nfs@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.