free(pt_header);
pt_header = NULL;
}
+
/*
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
*
{
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
zfs_handle_t *zhp = NULL;
- uint64_t volsize;
+ uint64_t volsize = 0;
int c;
boolean_t noreserve = B_FALSE;
boolean_t bflag = B_FALSE;
zfs_prop_t resv_prop;
char *strval;
- if (p = strchr(argv[0], '/'))
+ if ((p = strchr(argv[0], '/')))
*p = '\0';
zpool_handle = zpool_open(g_zfs, argv[0]);
if (p != NULL)
if (version < cb->cb_version) {
char verstr[16];
(void) snprintf(verstr, sizeof (verstr),
- "%llu", cb->cb_version);
+ "%llu", (u_longlong_t)cb->cb_version);
if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
/*
* If they did "zfs upgrade -a", then we could
boolean_t showversions = B_FALSE;
int ret;
upgrade_cbdata_t cb = { 0 };
- char c;
+ signed char c;
int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
/* check options */
ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
NULL, NULL, 0, upgrade_set_callback, &cb);
(void) printf(gettext("%llu filesystems upgraded\n"),
- cb.cb_numupgraded);
+ (u_longlong_t)cb.cb_numupgraded);
if (cb.cb_numsamegraded) {
(void) printf(gettext("%llu filesystems already at "
"this version\n"),
- cb.cb_numsamegraded);
+ (u_longlong_t)cb.cb_numsamegraded);
}
if (cb.cb_numfailed != 0)
ret = 1;
if (pl->pl_next == NULL && !right_justify)
(void) printf("%s", header);
else if (right_justify)
- (void) printf("%*s", pl->pl_width, header);
+ (void) printf("%*s", (int)pl->pl_width, header);
else
- (void) printf("%-*s", pl->pl_width, header);
+ (void) printf("%-*s", (int)pl->pl_width, header);
}
(void) printf("\n");
usage(B_FALSE);
}
- ret = zfs_for_each(argc - 2, argv + 2, NULL,
+ ret = zfs_for_each(argc - 2, argv + 2, 0,
ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
return (ret);
{
boolean_t recursive = B_FALSE;
int ret;
- char c;
+ signed char c;
nvlist_t *props;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
shared_nfs = zfs_is_shared_nfs(zhp, NULL);
shared_smb = zfs_is_shared_smb(zhp, NULL);
- if (shared_nfs && shared_smb ||
- (shared_nfs && strcmp(shareopts, "on") == 0 &&
- strcmp(smbshareopts, "off") == 0) ||
- (shared_smb && strcmp(smbshareopts, "on") == 0 &&
- strcmp(shareopts, "off") == 0)) {
+ if ((shared_nfs && shared_smb) ||
+ ((shared_nfs && strcmp(shareopts, "on") == 0) &&
+ (strcmp(smbshareopts, "off") == 0)) ||
+ ((shared_smb && strcmp(smbshareopts, "on") == 0) &&
+ (strcmp(shareopts, "off") == 0))) {
if (!explicit)
return (0);
/* original length plus new string to append plus 1 for the comma */
if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
(void) fprintf(stderr, gettext("the opts argument for "
- "'%c' option is too long (more than %d chars)\n"),
+ "'%s' option is too long (more than %d chars)\n"),
"-o", MNT_LINE_MAX);
usage(B_FALSE);
}
int flags = 0;
/* check options */
- while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
+ while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:" : "a"))
!= -1) {
switch (c) {
case 'a':
append_options(options, optarg);
break;
- case 'O':
- flags |= MS_OVERLAY;
- break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
}
/*
- * When mount is given no arguments, go through /etc/mnttab and
+ * When mount is given no arguments, go through /etc/mtab and
* display any active ZFS mounts. We hide any snapshots, since
* they are controlled automatically.
*/
/*
* Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
- * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
+ * absolute path, find the entry /etc/mtab, verify that its a ZFS filesystem,
* and unmount it appropriately.
*/
static int
ino_t path_inode;
/*
- * Search for the path in /etc/mnttab. Rather than looking for the
+ * Search for the path in /etc/mtab. Rather than looking for the
* specific path, which can be fooled by non-standard paths (i.e. ".."
* or "//"), we stat() the path and search for the corresponding
* (major,minor) device pair.
"currently mounted\n"), cmdname, path);
return (1);
}
- (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
+ (void) fprintf(stderr, gettext("warning: %s not in mtab\n"),
path);
if ((ret = umount2(path, flags)) != 0)
(void) fprintf(stderr, gettext("%s: %s\n"), path,
strcmp(smbshare_prop, "off") == 0) {
(void) fprintf(stderr, gettext("cannot unshare "
"'%s': legacy share\n"), path);
- (void) fprintf(stderr, gettext("use "
- "unshare(1M) to unshare this filesystem\n"));
+ (void) fprintf(stderr, gettext("use exportfs(8) "
+ "or smbcontrol(1) to unshare this filesystem\n"));
} else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot unshare '%s': "
"not currently shared\n"), path);
(void) fprintf(stderr, gettext("cannot unmount "
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use umount(1M) "
+ (void) fprintf(stderr, gettext("use umount(8) "
"to unmount this filesystem\n"));
} else {
ret = zfs_unmountall(zhp, flags);
/*
* We could make use of zfs_for_each() to walk all datasets in
* the system, but this would be very inefficient, especially
- * since we would have to linearly search /etc/mnttab for each
- * one. Instead, do one pass through /etc/mnttab looking for
+ * since we would have to linearly search /etc/mtab for each
+ * one. Instead, do one pass through /etc/mtab looking for
* zfs entries and call zfs_unmount() for each one.
*
* Things get a little tricky if the administrator has created
*/
struct mnttab entry;
uu_avl_pool_t *pool;
- uu_avl_t *tree;
+ uu_avl_t *tree = NULL;
unshare_unmount_node_t *node;
uu_avl_index_t idx;
uu_avl_walk_t *walk;
return (-1);
}
+typedef struct option_map {
+ const char *name;
+ int mask;
+} option_map_t;
+
+static const option_map_t option_map[] = {
+ /* Canonicalized filesystem independent options from mount(8) */
+ { MNTOPT_NOAUTO, MS_COMMENT },
+ { MNTOPT_DEFAULTS, MS_COMMENT },
+ { MNTOPT_NODEVICES, MS_NODEV },
+ { MNTOPT_DIRSYNC, MS_DIRSYNC },
+ { MNTOPT_NOEXEC, MS_NOEXEC },
+ { MNTOPT_GROUP, MS_GROUP },
+ { MNTOPT_NETDEV, MS_COMMENT },
+ { MNTOPT_NOFAIL, MS_COMMENT },
+ { MNTOPT_NOSUID, MS_NOSUID },
+ { MNTOPT_OWNER, MS_OWNER },
+ { MNTOPT_REMOUNT, MS_REMOUNT },
+ { MNTOPT_RO, MS_RDONLY },
+ { MNTOPT_SYNC, MS_SYNCHRONOUS },
+ { MNTOPT_USER, MS_USERS },
+ { MNTOPT_USERS, MS_USERS },
+#ifdef MS_NOATIME
+ { MNTOPT_NOATIME, MS_NOATIME },
+#endif
+#ifdef MS_NODIRATIME
+ { MNTOPT_NODIRATIME, MS_NODIRATIME },
+#endif
+#ifdef MS_RELATIME
+ { MNTOPT_RELATIME, MS_RELATIME },
+#endif
+#ifdef MS_STRICTATIME
+ { MNTOPT_DFRATIME, MS_STRICTATIME },
+#endif
+#ifdef HAVE_SELINUX
+ { MNTOPT_CONTEXT, MS_COMMENT },
+ { MNTOPT_FSCONTEXT, MS_COMMENT },
+ { MNTOPT_DEFCONTEXT, MS_COMMENT },
+ { MNTOPT_ROOTCONTEXT, MS_COMMENT },
+#endif
+#ifdef MS_I_VERSION
+ { MNTOPT_IVERSION, MS_I_VERSION },
+#endif
+#ifdef MS_MANDLOCK
+ { MNTOPT_NBMAND, MS_MANDLOCK },
+#endif
+ /* Valid options not found in mount(8) */
+ { MNTOPT_BIND, MS_BIND },
+#ifdef MS_REC
+ { MNTOPT_RBIND, MS_BIND|MS_REC },
+#endif
+ { MNTOPT_COMMENT, MS_COMMENT },
+ { MNTOPT_BOOTWAIT, MS_COMMENT },
+ { MNTOPT_NOBOOTWAIT, MS_COMMENT },
+ { MNTOPT_OPTIONAL, MS_COMMENT },
+ { MNTOPT_SHOWTHROUGH, MS_COMMENT },
+#ifdef MS_NOSUB
+ { MNTOPT_NOSUB, MS_NOSUB },
+#endif
+#ifdef MS_SILENT
+ { MNTOPT_QUIET, MS_SILENT },
+#endif
+ /* Custom zfs options */
+ { MNTOPT_NOXATTR, MS_COMMENT },
+ { NULL, 0 } };
+
+/*
+ * Break the mount option in to a name/value pair. The name is
+ * validated against the option map and mount flags set accordingly.
+ */
+static int
+parse_option(char *mntopt, unsigned long *mntflags, int sloppy)
+{
+ const option_map_t *opt;
+ char *ptr, *name, *value = NULL;
+ int rc;
+
+ name = strdup(mntopt);
+ if (name == NULL)
+ return (ENOMEM);
+
+ for (ptr = name; ptr && *ptr; ptr++) {
+ if (*ptr == '=') {
+ *ptr = '\0';
+ value = ptr+1;
+ break;
+ }
+ }
+
+ for (opt = option_map; opt->name != NULL; opt++) {
+ if (strncmp(name, opt->name, strlen(name)) == 0) {
+ *mntflags |= opt->mask;
+
+ /* MS_USERS implies default user options */
+ if (opt->mask & (MS_USERS))
+ *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV);
+
+ /* MS_OWNER|MS_GROUP imply default owner options */
+ if (opt->mask & (MS_OWNER | MS_GROUP))
+ *mntflags |= (MS_NOSUID|MS_NODEV);
+
+ rc = 0;
+ goto out;
+ }
+ }
+
+ if (!sloppy)
+ rc = ENOENT;
+out:
+ /* If required further process on the value may be done here */
+ free(name);
+ return (rc);
+}
+
/*
- * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
- * 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
+ * Translate the mount option string in to MS_* mount flags for the
+ * kernel vfs. When sloppy is non-zero unknown options will be ignored
+ * otherwise they are considered fatal are copied in to badopt.
+ */
+static int
+parse_options(char *mntopts, unsigned long *mntflags, int sloppy, char *badopt)
+{
+ int rc = 0, quote = 0;
+ char *ptr, *opt, *opts;
+
+ opts = strdup(mntopts);
+ if (opts == NULL)
+ return (ENOMEM);
+
+ *mntflags = 0;
+ opt = NULL;
+
+ /*
+ * Scan through all mount options which must be comma delimited.
+ * We must be careful to notice regions which are double quoted
+ * and skip commas in these regions. Each option is then checked
+ * to determine if it is a known option.
+ */
+ for (ptr = opts; ptr && *ptr; ptr++) {
+ if (opt == NULL)
+ opt = ptr;
+
+ if (*ptr == '"')
+ quote = !quote;
+
+ if (quote)
+ continue;
+
+ if ((*ptr == ',') || (*ptr == '\0')) {
+ *ptr = '\0';
+ rc = parse_option(opt, mntflags, sloppy);
+ if (rc) {
+ strcpy(badopt, opt);
+ goto out;
+ }
+
+ opt = NULL;
+ }
+ }
+out:
+ free(opts);
+ return (rc);
+}
+
+/*
+ * Called when invoked as /sbin/mount.zfs, mount helper for mount(8).
*/
static int
manual_mount(int argc, char **argv)
{
zfs_handle_t *zhp;
- char mountpoint[ZFS_MAXPROPLEN];
+ char legacy[ZFS_MAXPROPLEN];
char mntopts[MNT_LINE_MAX] = { '\0' };
- int ret;
- int c;
- int flags = 0;
- char *dataset, *path;
+ char badopt[MNT_LINE_MAX] = { '\0' };
+ char *dataset, *mntpoint;
+ unsigned long mntflags;
+ int sloppy = 0, fake = 0, verbose = 0;
+ int rc, c;
/* check options */
- while ((c = getopt(argc, argv, ":mo:O")) != -1) {
+ while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) {
switch (c) {
- case 'o':
- (void) strlcpy(mntopts, optarg, sizeof (mntopts));
+ case 's':
+ sloppy = 1;
break;
- case 'O':
- flags |= MS_OVERLAY;
+ case 'f':
+ fake = 1;
break;
- case 'm':
- flags |= MS_NOMNTTAB;
+ case 'n':
+ /* Ignored, handled by mount(8) */
break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
+ case 'v':
+ verbose++;
+ break;
+ case 'o':
+ (void) strlcpy(mntopts, optarg, sizeof (mntopts));
break;
+ case 'h':
case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
optopt);
- (void) fprintf(stderr, gettext("usage: mount [-o opts] "
- "<path>\n"));
- return (2);
+ (void) fprintf(stderr, gettext("Usage: mount.zfs "
+ "[-sfnv] [-o options] <dataset> <mountpoint>\n"));
+ return (MOUNT_USAGE);
}
}
else
(void) fprintf(stderr, gettext("too many arguments\n"));
(void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
- return (2);
+ return (MOUNT_USAGE);
}
dataset = argv[0];
- path = argv[1];
+ mntpoint = argv[1];
- /* try to open the dataset */
- if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
- return (1);
+ /* try to open the dataset to access the mount point */
+ if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) {
+ (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
+ "mounted, unable to open the dataset\n"), dataset);
+ return (MOUNT_USAGE);
+ }
- (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
+ (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
+ sizeof (legacy), NULL, NULL, 0, B_FALSE);
- /* check for legacy mountpoint and complain appropriately */
- ret = 0;
- if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
- if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
- NULL, 0, mntopts, sizeof (mntopts)) != 0) {
- (void) fprintf(stderr, gettext("mount failed: %s\n"),
- strerror(errno));
- ret = 1;
- }
- } else {
+ zfs_close(zhp);
+
+ /* check for legacy mountpoint or util mount option */
+ if ((!strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) == 0) &&
+ (strstr(mntopts, MNTOPT_ZFSUTIL) == NULL)) {
(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted using 'mount -F zfs'\n"), dataset);
+ "mounted using 'mount -a -t zfs'\n"), dataset);
(void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
- "instead.\n"), path);
- (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
- "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
- (void) fprintf(stderr, gettext("See zfs(1M) for more "
+ "instead.\n"), mntpoint);
+ (void) fprintf(stderr, gettext("If you must use 'mount -a -t "
+ "zfs' or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
+ (void) fprintf(stderr, gettext("See zfs(8) for more "
"information.\n"));
- ret = 1;
+ return (MOUNT_USAGE);
+ }
+
+ /* validate mount options and set mntflags */
+ rc = parse_options(mntopts, &mntflags, sloppy, badopt);
+ if (rc) {
+ switch (rc) {
+ case ENOMEM:
+ (void) fprintf(stderr, gettext("filesystem '%s' "
+ "cannot be mounted due to a memory allocation "
+ "failure\n"), dataset);
+ return (MOUNT_SYSERR);
+ case EINVAL:
+ (void) fprintf(stderr, gettext("filesystem '%s' "
+ "cannot be mounted of due to the 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"),
+ dataset, rc);
+ return (MOUNT_SOFTWARE);
+ }
}
- return (ret);
+ if (verbose > 2)
+ printf("mount.zfs: dataset: \"%s\", mountpoint: \"%s\" "
+ "mountflags: 0x%lx, mountopts: \"%s\"\n", dataset,
+ mntpoint, mntflags, mntopts);
+
+ /* load the zfs posix layer module (zpl) */
+ if (libzfs_load_module("zpl")) {
+ (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
+ "mounted without the zpl kernel module\n"), dataset);
+ (void) fprintf(stderr, gettext("Use 'dmesg' to determine why "
+ "the module could not be loaded.\n"));
+ return (MOUNT_SYSERR);
+ }
+
+ if (!fake) {
+ rc = mount(dataset, mntpoint, MNTTYPE_ZFS, mntflags, mntopts);
+ if (rc) {
+ (void) fprintf(stderr, gettext("filesystem '%s' can"
+ "not be mounted due to error %d\n"), dataset, rc);
+ return (MOUNT_USAGE);
+ }
+ }
+
+ return (MOUNT_SUCCESS);
}
+#ifdef HAVE_UNMOUNT_HELPER
/*
- * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow
- * unmounts of non-legacy filesystems, as this is the dominant administrative
- * interface.
+ * Called when invoked as /sbin/umount.zfs, mount helper for mount(8).
+ * Unlike a manual mount, we allow unmounts of non-legacy filesystems,
+ * as this is the dominant administrative interface.
*/
static int
manual_unmount(int argc, char **argv)
{
- int flags = 0;
+ int verbose = 0, flags = 0;
int c;
/* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
+ while ((c = getopt(argc, argv, "nlfvrh?")) != -1) {
switch (c) {
+ case 'n':
+ /* Ignored, handled by mount(8) */
+ break;
+ case 'l':
+ flags = MS_DETACH;
+ break;
case 'f':
flags = MS_FORCE;
break;
+ case 'v':
+ verbose++;
+ break;
+ case 'r':
+ /* Remount read-only on umount failure, unsupported */
+ (void) fprintf(stderr, gettext("Unsupported option "
+ "'%c'\n"), optopt);
+ return (MOUNT_USAGE);
+ case 'h':
case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
optopt);
- (void) fprintf(stderr, gettext("usage: unmount [-f] "
- "<path>\n"));
- return (2);
+ (void) fprintf(stderr, gettext("Usage: umount.zfs "
+ "[-nlfvr] <mountpoint>\n"));
+ return (MOUNT_USAGE);
}
}
argc -= optind;
argv += optind;
- /* check arguments */
+ /* check that we only have one argument */
if (argc != 1) {
if (argc == 0)
- (void) fprintf(stderr, gettext("missing path "
+ (void) fprintf(stderr, gettext("missing mountpoint "
"argument\n"));
else
(void) fprintf(stderr, gettext("too many arguments\n"));
- (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
- return (2);
+
+ (void) fprintf(stderr, gettext("Usage: umount.zfs [-nlfvr] "
+ "<mountpoint>\n"));
+ return (MOUNT_USAGE);
}
return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
}
+#endif /* HAVE_UNMOUNT_HELPER */
static int
find_command_idx(char *command, int *idx)
if (copy == NULL)
usage(B_FALSE);
- if (atp = strchr(copy, '@'))
+ if ((atp = strchr(copy, '@')))
*atp = '\0';
if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
main(int argc, char **argv)
{
int ret;
- int i;
+ int i = 0;
char *progname;
char *cmdname;
opterr = 0;
- if ((g_zfs = libzfs_init()) == NULL) {
- (void) fprintf(stderr, gettext("internal error: failed to "
- "initialize ZFS library\n"));
- return (1);
- }
-
- zpool_set_history_str("zfs", argc, argv, history_str);
- verify(zpool_stage_history(g_zfs, history_str) == 0);
-
- libzfs_print_on_error(g_zfs, B_TRUE);
-
if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, gettext("internal error: unable to "
"open %s\n"), MNTTAB);
* Determine if we should take this behavior based on argv[0].
*/
progname = basename(argv[0]);
- if (strcmp(progname, "mount") == 0) {
+ if (strcmp(progname, "mount.zfs") == 0) {
ret = manual_mount(argc, argv);
- } else if (strcmp(progname, "umount") == 0) {
+#ifdef HAVE_UNMOUNT_HELPER
+ } else if (strcmp(progname, "umount.zfs") == 0) {
ret = manual_unmount(argc, argv);
+#endif /* HAVE_UNMOUNT_HELPER */
} else {
/*
* Make sure the user has specified some command.
/*
* Special case '-?'
*/
- if (strcmp(cmdname, "-?") == 0)
+ if ((strcmp(cmdname, "-?") == 0) ||
+ (strcmp(cmdname, "--help") == 0))
usage(B_TRUE);
+ if ((g_zfs = libzfs_init()) == NULL)
+ return (1);
+
+ zpool_set_history_str("zfs", argc, argv, history_str);
+ verify(zpool_stage_history(g_zfs, history_str) == 0);
+
+ libzfs_print_on_error(g_zfs, B_TRUE);
+
/*
* Run the appropriate command.
*/
(void) fprintf(stderr, gettext("unrecognized "
"command '%s'\n"), cmdname);
usage(B_FALSE);
+ ret = 1;
}
libzfs_mnttab_cache(g_zfs, B_FALSE);
}