Allow fake mounts to succeed on non-legacy filesystems.
[zfs.git] / cmd / mount_zfs / mount_zfs.c
index 204f9fa..9a82a2b 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <libzfs.h>
+#include <locale.h>
 #ifdef HAVE_LIBSELINUX
 #include <selinux/selinux.h>
 #endif /* HAVE_LIBSELINUX */
@@ -96,6 +97,7 @@ static const option_map_t option_map[] = {
        { MNTOPT_QUIET,         MS_SILENT,      ZS_COMMENT      },
 #endif
        /* Custom zfs options */
+       { MNTOPT_XATTR,         MS_COMMENT,     ZS_COMMENT      },
        { MNTOPT_NOXATTR,       MS_COMMENT,     ZS_COMMENT      },
        { MNTOPT_ZFSUTIL,       MS_COMMENT,     ZS_ZFSUTIL      },
        { NULL,                 0,              0               } };
@@ -120,6 +122,7 @@ parse_option(char *mntopt, unsigned long *mntflags,
                if (*ptr == '=') {
                        *ptr = '\0';
                        value = ptr+1;
+                       VERIFY3P(value, !=, NULL);
                        break;
                }
        }
@@ -228,7 +231,9 @@ parse_dataset(char *dataset)
        char cwd[PATH_MAX];
        int len;
 
-       (void) getcwd(cwd, PATH_MAX);
+       if (getcwd(cwd, PATH_MAX) == NULL)
+               return (dataset);
+
        len = strlen(cwd);
 
        /* Do not add one when cwd already ends in a trailing '/' */
@@ -249,7 +254,7 @@ mtab_is_writeable(void)
        struct stat st;
        int error, fd;
 
-       error = stat(MNTTAB, &st);
+       error = lstat(MNTTAB, &st);
        if (error || S_ISLNK(st.st_mode))
                return (0);
 
@@ -306,8 +311,9 @@ main(int argc, char **argv)
        char mntopts[MNT_LINE_MAX] = { '\0' };
        char badopt[MNT_LINE_MAX] = { '\0' };
        char mtabopt[MNT_LINE_MAX] = { '\0' };
-       char *dataset, *mntpoint;
-       unsigned long mntflags = 0, zfsflags = 0, remount_ro = 0;
+       char mntpoint[PATH_MAX];
+       char *dataset;
+       unsigned long mntflags = 0, zfsflags = 0, remount = 0;
        int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0;
        int error, c;
 
@@ -362,7 +368,13 @@ main(int argc, char **argv)
        }
 
        dataset = parse_dataset(argv[0]);
-       mntpoint = argv[1];
+
+       /* canonicalize the mount point */
+       if (realpath(argv[1], mntpoint) == NULL) {
+               (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
+                   "mounted due to a canonicalization failure.\n"), dataset);
+               return (MOUNT_SYSERR);
+       }
 
        /* validate mount options and set mntflags */
        error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
@@ -412,11 +424,10 @@ main(int argc, char **argv)
                    "  mountopts:  \"%s\"\n  mtabopts:   \"%s\"\n"),
                    dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
 
-       if (mntflags & MS_REMOUNT)
+       if (mntflags & MS_REMOUNT) {
                nomtab = 1;
-
-       if ((mntflags & MS_REMOUNT) && (mntflags & MS_RDONLY))
-               remount_ro = 1;
+               remount = 1;
+       }
 
        if (zfsflags & ZS_ZFSUTIL)
                zfsutil = 1;
@@ -425,15 +436,20 @@ main(int argc, char **argv)
                return (MOUNT_SYSERR);
 
        /* try to open the dataset to access the mount point */
-       if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) {
+       if ((zhp = zfs_open(g_zfs, dataset,
+           ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) {
                (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
                    "mounted, unable to open the dataset\n"), dataset);
                libzfs_fini(g_zfs);
                return (MOUNT_USAGE);
        }
 
-       (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
-           sizeof (legacy), NULL, NULL, 0, B_FALSE);
+       /* treat all snapshots as legacy mount points */
+       if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
+               (void) strlcpy(legacy, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
+       else
+               (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
+                   sizeof (legacy), NULL, NULL, 0, B_FALSE);
 
        zfs_close(zhp);
        libzfs_fini(g_zfs);
@@ -444,9 +460,10 @@ main(int argc, char **argv)
         * we differentiate the two cases using the 'zfsutil' mount option.
         * This mount option should only be supplied by the 'zfs mount' util.
         *
-        * The only exception to the above rule is '-o remount,ro'.  This is
-        * always allowed for non-legacy datasets for rc.sysinit/umountroot
-        * to safely remount the root filesystem and flush its cache.
+        * The only exception to the above rule is '-o remount' which is
+        * always allowed for non-legacy datasets.  This is done because when
+        * using zfs as your root file system both rc.sysinit/umountroot and
+        * systemd depend on 'mount -o remount <mountpoint>' to work.
         */
        if (zfsutil && !strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
                (void) fprintf(stderr, gettext(
@@ -457,7 +474,8 @@ main(int argc, char **argv)
                return (MOUNT_USAGE);
        }
 
-       if (!zfsutil && strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) && !remount_ro) {
+       if (!zfsutil && !(remount || fake) &&
+           strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
                (void) fprintf(stderr, gettext(
                    "filesystem '%s' cannot be mounted using 'mount'.\n"
                    "Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n"
@@ -471,6 +489,10 @@ main(int argc, char **argv)
                    mntflags, mntopts);
                if (error) {
                        switch (errno) {
+                       case ENOENT:
+                               (void) fprintf(stderr, gettext("mount point "
+                                   "'%s' does not exist\n"), mntpoint);
+                               return (MOUNT_SYSERR);
                        case EBUSY:
                                (void) fprintf(stderr, gettext("filesystem "
                                    "'%s' is already mounted\n"), dataset);