#include "nls.h"
#include "mount_constants.h"
+#include "nfs_mount.h"
#include "mount.h"
#include "error.h"
#include "network.h"
+#include "parse_opt.h"
+#include "parse_dev.h"
+#include "utils.h"
+
+#define MOUNTSFILE "/proc/mounts"
+#define LINELEN (4096)
#if !defined(MNT_FORCE)
/* dare not try to include <linux/mount.h> -- lots of errors */
int lazy;
int remount;
+
+static int try_remount(const char *spec, const char *node)
+{
+ int res;
+
+ res = mount(spec, node, NULL,
+ MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+ if (res == 0) {
+ struct mntent remnt;
+ nfs_error(_("%s: %s busy - remounted read-only"),
+ progname, spec);
+ remnt.mnt_type = remnt.mnt_fsname = NULL;
+ remnt.mnt_dir = xstrdup(node);
+ remnt.mnt_opts = xstrdup("ro");
+ if (!nomtab)
+ update_mtab(node, &remnt);
+ } else if (errno != EBUSY) { /* hmm ... */
+ perror(_("remount"));
+ nfs_error(_("%s: could not remount %s read-only"),
+ progname, spec);
+ }
+ return res;
+}
+
static int del_mtab(const char *spec, const char *node)
{
int umnt_err, res;
- umnt_err = 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 && remount && errno == EBUSY && spec) {
- /* Umount failed - let us try a remount */
- res = mount(spec, node, NULL,
- MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
- if (res == 0) {
- struct mntent remnt;
- nfs_error(_("%s: %s busy - remounted read-only"),
- progname, 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"));
- nfs_error(_("%s: could not remount %s read-only"),
- progname, spec);
- }
- }
-
- if (res >= 0) {
- /* Umount succeeded */
- if (verbose)
- printf(_("%s umounted\n"), spec ? spec : node);
- }
+ umnt_err = 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) {
+ if (remount && errno == EBUSY && spec) {
+ res = try_remount(spec, node);
+ if (res)
+ goto writemtab;
+ return EX_SUCCESS;
+ } else
+ umnt_err = errno;
+ }
+
+ if (res >= 0) {
+ /* Umount succeeded */
+ if (verbose)
+ printf(_("%s umounted\n"), spec ? spec : node);
+ }
writemtab:
- if (!nomtab &&
- (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) {
- update_mtab(node, NULL);
- }
+ if (!nomtab &&
+ (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) {
+ update_mtab(node, NULL);
+ }
- if (res >= 0)
- return 0;
+ if (res >= 0)
+ return EX_SUCCESS;
- if (umnt_err)
- umount_error(umnt_err, node);
- return EX_FILEIO;
+ if (umnt_err)
+ umount_error(umnt_err, node);
+ return EX_FILEIO;
}
/*
- * Pick up certain mount options used during the original mount
- * from /etc/mtab. The basics include the server's IP address and
- * the server pathname of the share to unregister.
+ * Detect NFSv4 mounts.
+ *
+ * Consult /proc/mounts to determine if the mount point
+ * is an NFSv4 mount. The kernel is authoritative about
+ * what type of mount this is.
*
- * These options might also describe the mount port, mount protocol
- * version, and transport protocol used to punch through a firewall.
- * We will need this information to get through the firewall again
- * to do the umount.
+ * Returns 1 if "mc" is an NFSv4 mount, zero if not, and
+ * -1 if some error occurred.
*/
-static int do_nfs_umount(const char *spec, char *opts)
+static int nfs_umount_is_vers4(const struct mntentchn *mc)
{
- char *hostname;
- char *dirname;
- clnt_addr_t mnt_server = { &hostname, };
- struct mntent mnt = { .mnt_opts = opts };
- struct pmap *pmap = &mnt_server.pmap;
- char *p;
-
- if (spec == NULL) {
- nfs_error(_("%s: No NFS export name was provided"),
- progname);
- return EX_USAGE;
- }
-
- p = strchr(spec, ':');
- if (p == NULL) {
- nfs_error(_("%s: '%s' is not a legal NFS export name"),
- progname, spec);
- return EX_USAGE;
+ char buffer[LINELEN], *next;
+ int retval;
+ FILE *f;
+
+ if ((f = fopen(MOUNTSFILE, "r")) == NULL) {
+ fprintf(stderr, "%s: %s\n",
+ MOUNTSFILE, strerror(errno));
+ return -1;
}
- 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_NFSV3;
- 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 && hasmntopt(&mnt, "v2"))
- pmap->pm_vers = nfsvers_to_mnt(2);
- if (opts && hasmntopt(&mnt, "v3"))
- pmap->pm_vers = nfsvers_to_mnt(3);
- if (opts && hasmntopt(&mnt, "v4"))
- pmap->pm_vers = nfsvers_to_mnt(4);
- if (opts && (p = strstr(opts, "vers=")) && isdigit(*(p+5)))
- pmap->pm_vers = nfsvers_to_mnt(atoi(p+5));
- if (opts && (p = strstr(opts, "mountvers=")) && isdigit(*(p+10)))
- pmap->pm_vers = atoi(p+10);
- if (opts && (hasmntopt(&mnt, "udp") || hasmntopt(&mnt, "proto=udp")))
- pmap->pm_prot = IPPROTO_UDP;
- if (opts && (hasmntopt(&mnt, "tcp") || hasmntopt(&mnt, "proto=tcp")))
- pmap->pm_prot = IPPROTO_TCP;
-
- if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) {
- nfs_error(_("%s: '%s' does not contain a recognized hostname"),
- progname, spec);
- return EX_USAGE;
- }
+ retval = -1;
+ while (fgets(buffer, sizeof(buffer), f) != NULL) {
+ char *device, *mntdir, *type, *flags;
+ struct mount_options *options;
+ char *line = buffer;
+
+ next = strchr(line, '\n');
+ if (next != NULL)
+ *next = '\0';
+
+ device = strtok(line, " \t");
+ if (device == NULL)
+ continue;
+ mntdir = strtok(NULL, " \t");
+ if (mntdir == NULL)
+ continue;
+ if (strcmp(device, mc->m.mnt_fsname) != 0 &&
+ strcmp(mntdir, mc->m.mnt_dir) != 0)
+ continue;
+
+ type = strtok(NULL, " \t");
+ if (type == NULL)
+ continue;
+ if (strcmp(type, "nfs4") == 0)
+ goto out_nfs4;
+
+ flags = strtok(NULL, " \t");
+ if (flags == NULL)
+ continue;
+ options = po_split(flags);
+ if (options != NULL) {
+ unsigned long version;
+ int rc;
+
+ rc = nfs_nfs_version(options, &version);
+ po_destroy(options);
+ if (rc && version == 4)
+ goto out_nfs4;
+ }
- if (!nfs_call_umount(&mnt_server, &dirname)) {
- nfs_error(_("%s: Server failed to unmount '%s'"),
- progname, spec);
- return EX_USAGE;
+ goto out_nfs;
}
-
- return EX_SUCCESS;
+ if (retval == -1)
+ fprintf(stderr, "%s was not found in %s\n",
+ mc->m.mnt_dir, MOUNTSFILE);
+
+out:
+ fclose(f);
+ return retval;
+
+out_nfs4:
+ if (verbose)
+ fprintf(stderr, "NFSv4 mount point detected\n");
+ retval = 1;
+ goto out;
+
+out_nfs:
+ if (verbose)
+ fprintf(stderr, "Legacy NFS mount point detected\n");
+ retval = 0;
+ goto out;
}
static struct option umount_longopts[] =
{ NULL, 0, 0, 0 }
};
-static void umount_usage(void)
-{
- printf(_("usage: %s dir [-fvnrlh]\n"), progname);
- printf(_("options:\n\t-f\t\tforce unmount\n"));
- printf(_("\t-v\tverbose\n"));
- printf(_("\t-n\tDo not update /etc/mtab\n"));
- printf(_("\t-r\tremount\n"));
- printf(_("\t-l\tlazy unmount\n"));
- printf(_("\t-h\tprint this help\n\n"));
-}
-
int nfsumount(int argc, char *argv[])
{
int c, ret;
char *opt = hasmntopt(&mc->m, "user");
struct passwd *pw;
char *comma;
- int len;
+ size_t len;
if (!opt)
goto only_root;
if (opt[4] != '=')
}
}
- ret = 0;
+ ret = EX_SUCCESS;
if (mc) {
- if (!lazy)
- ret = do_nfs_umount(mc->m.mnt_fsname, mc->m.mnt_opts);
- if (!ret || force)
- ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
+ if (!lazy) {
+ switch (nfs_umount_is_vers4(mc)) {
+ case 0:
+ /* We ignore the error from nfs_umount23.
+ * If the actual umount succeeds (in del_mtab),
+ * we don't want to signal an error, as that
+ * could cause /sbin/mount to retry!
+ */
+ nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
+ break;
+ case 1:
+ break;
+ default:
+ return EX_FAIL;
+ }
+ }
+ ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
} else if (*spec != '/') {
if (!lazy)
- ret = do_nfs_umount(spec, "tcp,v3");
+ ret = nfs_umount23(spec, "tcp,v3");
} else
ret = del_mtab(NULL, spec);