#include <sys/mount.h>
#include <sys/stat.h>
#include <libzfs.h>
+#include <locale.h>
#ifdef HAVE_LIBSELINUX
#include <selinux/selinux.h>
#endif /* HAVE_LIBSELINUX */
{ MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT },
{ MNTOPT_USER, MS_USERS, ZS_COMMENT },
{ MNTOPT_USERS, MS_USERS, ZS_COMMENT },
+ /* acl flags passed with util-linux-2.24 mount command */
+ { MNTOPT_ACL, MS_POSIXACL, ZS_COMMENT },
+ { MNTOPT_NOACL, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_POSIXACL, MS_POSIXACL, ZS_COMMENT },
#ifdef MS_NOATIME
{ MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT },
#endif
#ifdef MS_STRICTATIME
{ MNTOPT_DFRATIME, MS_STRICTATIME, ZS_COMMENT },
#endif
-#ifdef HAVE_SELINUX
{ MNTOPT_CONTEXT, MS_COMMENT, ZS_NOCONTEXT },
{ MNTOPT_NOCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
{ MNTOPT_FSCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
{ MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
{ MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
-#endif
#ifdef MS_I_VERSION
{ MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT },
#endif
{ 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 } };
if (*ptr == '=') {
*ptr = '\0';
value = ptr+1;
+ VERIFY3P(value, !=, NULL);
break;
}
}
if (strncmp(name, opt->name, strlen(name)) == 0) {
*mntflags |= opt->mntmask;
*zfsflags |= opt->zfsmask;
-
- /* MS_USERS implies default user options */
- if (opt->mntmask & (MS_USERS))
- *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV);
-
- /* MS_OWNER|MS_GROUP imply default owner options */
- if (opt->mntmask & (MS_OWNER | MS_GROUP))
- *mntflags |= (MS_NOSUID|MS_NODEV);
-
error = 0;
goto out;
}
* otherwise they are considered fatal are copied in to badopt.
*/
static int
-parse_options(char *mntopts, unsigned long *mntflags,
- unsigned long *zfsflags, int sloppy, char *badopt)
+parse_options(char *mntopts, unsigned long *mntflags, unsigned long *zfsflags,
+ int sloppy, char *badopt, char *mtabopt)
{
- int error = 0, quote = 0, flag = 0;
+ int error = 0, quote = 0, flag = 0, count = 0;
char *ptr, *opt, *opts;
opts = strdup(mntopts);
if (error) {
strcpy(badopt, opt);
goto out;
+
+ }
+
+ if (!(*mntflags & MS_REMOUNT) &&
+ !(*zfsflags & ZS_ZFSUTIL)) {
+ if (count > 0)
+ strlcat(mtabopt, ",", MNT_LINE_MAX);
+
+ strlcat(mtabopt, opt, MNT_LINE_MAX);
+ count++;
}
opt = NULL;
}
/*
- * If a file or directory in your current working directory is named
- * 'dataset' then mount(8) will prepend your current working directory
- * to dataset. The is no way to prevent this behavior so we simply
- * check for it and strip the prepended patch when it is added.
+ * Return the pool/dataset to mount given the name passed to mount. This
+ * is expected to be of the form pool/dataset, however may also refer to
+ * a block device if that device contains a valid zfs label.
*/
static char *
parse_dataset(char *dataset)
{
char cwd[PATH_MAX];
+ struct stat64 statbuf;
+ int error;
int len;
- (void) getcwd(cwd, PATH_MAX);
+ /*
+ * We expect a pool/dataset to be provided, however if we're
+ * given a device which is a member of a zpool we attempt to
+ * extract the pool name stored in the label. Given the pool
+ * name we can mount the root dataset.
+ */
+ error = stat64(dataset, &statbuf);
+ if (error == 0) {
+ nvlist_t *config;
+ char *name;
+ int fd;
+
+ fd = open(dataset, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ error = zpool_read_label(fd, &config);
+ (void) close(fd);
+ if (error)
+ goto out;
+
+ error = nvlist_lookup_string(config,
+ ZPOOL_CONFIG_POOL_NAME, &name);
+ if (error) {
+ nvlist_free(config);
+ } else {
+ dataset = strdup(name);
+ nvlist_free(config);
+ return (dataset);
+ }
+ }
+out:
+ /*
+ * If a file or directory in your current working directory is
+ * named 'dataset' then mount(8) will prepend your current working
+ * directory to the dataset. There is no way to prevent this
+ * behavior so we simply check for it and strip the prepended
+ * patch when it is added.
+ */
+ if (getcwd(cwd, PATH_MAX) == NULL)
+ return (dataset);
+
len = strlen(cwd);
/* Do not add one when cwd already ends in a trailing '/' */
- if (!strncmp(cwd, dataset, len))
+ if (strncmp(cwd, dataset, len) == 0)
return (dataset + len + (cwd[len-1] != '/'));
return (dataset);
struct stat st;
int error, fd;
- error = stat(MNTTAB, &st);
+ error = lstat(MNTTAB, &st);
if (error || S_ISLNK(st.st_mode))
return (0);
char legacy[ZFS_MAXPROPLEN];
char mntopts[MNT_LINE_MAX] = { '\0' };
char badopt[MNT_LINE_MAX] = { '\0' };
- char *dataset, *mntpoint;
- unsigned long mntflags = 0, zfsflags = 0;
+ char mtabopt[MNT_LINE_MAX] = { '\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;
}
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 at '%s' due to canonicalization error %d.\n"),
+ dataset, argv[1], errno);
+ return (MOUNT_SYSERR);
+ }
/* validate mount options and set mntflags */
- error = parse_options(mntopts, &mntflags, &zfsflags, sloppy, badopt);
+ error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
+ badopt, mtabopt);
if (error) {
switch (error) {
case ENOMEM:
(void) fprintf(stderr, gettext("filesystem '%s' "
"cannot be mounted due to a memory allocation "
- "failure\n"), dataset);
+ "failure.\n"), dataset);
return (MOUNT_SYSERR);
- case EINVAL:
+ case ENOENT:
(void) fprintf(stderr, gettext("filesystem '%s' "
- "cannot be mounted of due to the invalid option "
- "'%s'\n"), dataset, badopt);
+ "cannot be mounted due to invalid option "
+ "'%s'.\n"), dataset, badopt);
(void) fprintf(stderr, gettext("Use the '-s' option "
"to ignore the bad mount option.\n"));
return (MOUNT_USAGE);
default:
(void) fprintf(stderr, gettext("filesystem '%s' "
- "cannot be mounted due to internal error %d\n"),
+ "cannot be mounted due to internal error %d.\n"),
dataset, error);
return (MOUNT_SOFTWARE);
}
* done until zfs is added to the default selinux policy configuration
* as a known filesystem type which supports xattrs.
*/
- if (is_selinux_enabled() && !(zfsflags & ZS_NOCONTEXT))
- (void) strlcat(mntopts, ",context=\"system_u:"
- "object_r:file_t:s0\"", sizeof (mntopts));
+ if (is_selinux_enabled() && !(zfsflags & ZS_NOCONTEXT)) {
+ (void) strlcat(mntopts, ",context=\"system_u:"
+ "object_r:file_t:s0\"", sizeof (mntopts));
+ (void) strlcat(mtabopt, ",context=\"system_u:"
+ "object_r:file_t:s0\"", sizeof (mtabopt));
+ }
#endif /* HAVE_LIBSELINUX */
(void) fprintf(stdout, gettext("mount.zfs:\n"
" dataset: \"%s\"\n mountpoint: \"%s\"\n"
" mountflags: 0x%lx\n zfsflags: 0x%lx\n"
- " mountopts: \"%s\"\n\n"),
- dataset, mntpoint, mntflags, zfsflags, mntopts);
+ " mountopts: \"%s\"\n mtabopts: \"%s\"\n"),
+ dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
- if (mntflags & MS_REMOUNT)
+ if (mntflags & MS_REMOUNT) {
nomtab = 1;
+ remount = 1;
+ }
- if (zfsflags * ZS_ZFSUTIL)
+ if (zfsflags & ZS_ZFSUTIL)
zfsutil = 1;
if ((g_zfs = libzfs_init()) == NULL)
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);
* 'zfs mount'. However, since 'zfs mount' actually invokes 'mount'
* 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' 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)) {
+ if (zfsutil && (strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) == 0)) {
(void) fprintf(stderr, gettext(
"filesystem '%s' cannot be mounted using 'zfs mount'.\n"
"Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n"
"See zfs(8) for more information.\n"),
- dataset, mntpoint, dataset, mntpoint);
+ dataset, mntpoint, dataset, mntpoint);
return (MOUNT_USAGE);
}
- if (!zfsutil && strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
+ 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"
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);
- return (MOUNT_SYSERR);
+ return (MOUNT_BUSY);
default:
(void) fprintf(stderr, gettext("filesystem "
"'%s' can not be mounted due to error "
}
if (!nomtab && mtab_is_writeable()) {
- error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mntopts);
+ error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt);
if (error)
return (error);
}