X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=cmd%2Fzfs%2Fzfs_main.c;h=8d75d2a457c0fbca334ca8bac973b613ff906ba4;hb=9a616b5d17185c7fa5cd0d39ff8bc101cad8466d;hp=353fd4fa627db988f138357e8418f6db25fa8199;hpb=428870ff734fdaccc342b33fc53cf94724409a46;p=zfs.git diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 353fd4f..8d75d2a 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ static int zfs_do_userspace(int argc, char **argv); static int zfs_do_python(int argc, char **argv); static int zfs_do_hold(int argc, char **argv); static int zfs_do_release(int argc, char **argv); +static int zfs_do_diff(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -128,7 +130,8 @@ typedef enum { HELP_GROUPSPACE, HELP_HOLD, HELP_HOLDS, - HELP_RELEASE + HELP_RELEASE, + HELP_DIFF } zfs_help_t; typedef struct zfs_command { @@ -180,6 +183,7 @@ static zfs_command_t command_table[] = { { "hold", zfs_do_hold, HELP_HOLD }, { "holds", zfs_do_python, HELP_HOLDS }, { "release", zfs_do_release, HELP_RELEASE }, + { "diff", zfs_do_diff, HELP_DIFF }, }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) @@ -283,6 +287,9 @@ get_usage(zfs_help_t idx) return (gettext("\tholds [-r] ...\n")); case HELP_RELEASE: return (gettext("\trelease [-r] ...\n")); + case HELP_DIFF: + return (gettext("\tdiff [-FHt] " + "[snapshot|filesystem]\n")); } abort(); @@ -311,6 +318,7 @@ safe_malloc(size_t size) return (data); } +#ifdef HAVE_ZPL static char * safe_strdup(char *str) { @@ -321,6 +329,7 @@ safe_strdup(char *str) return (dupstr); } +#endif /* HAVE_ZPL */ /* * Callback routine that will print out information for each of @@ -488,6 +497,7 @@ parse_depth(char *opt, int *flags) #define PROGRESS_DELAY 2 /* seconds */ +#ifdef HAVE_ZPL static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; static time_t pt_begin; static char *pt_header = NULL; @@ -539,6 +549,8 @@ finish_progress(char *done) free(pt_header); pt_header = NULL; } +#endif /* HAVE_ZPL */ + /* * zfs clone [-p] [-o prop=value] ... * @@ -619,16 +631,19 @@ zfs_do_clone(int argc, char **argv) ret = zfs_clone(zhp, argv[1], props); /* create the mountpoint if necessary */ +#ifdef HAVE_ZPL if (ret == 0) { zfs_handle_t *clone; clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); if (clone != NULL) { - if ((ret = zfs_mount(clone, NULL, 0)) == 0) - ret = zfs_share(clone); + if (zfs_get_type(clone) != ZFS_TYPE_VOLUME) + if ((ret = zfs_mount(clone, NULL, 0)) == 0) + ret = zfs_share(clone); zfs_close(clone); } } +#endif /* HAVE_ZPL */ zfs_close(zhp); nvlist_free(props); @@ -663,7 +678,7 @@ zfs_do_create(int argc, char **argv) { 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; @@ -671,7 +686,7 @@ zfs_do_create(int argc, char **argv) int ret = 1; nvlist_t *props; uint64_t intval; - int canmount; + int canmount = ZFS_CANMOUNT_OFF; if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) nomem(); @@ -756,7 +771,7 @@ zfs_do_create(int argc, char **argv) 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) @@ -802,19 +817,21 @@ zfs_do_create(int argc, char **argv) if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) goto error; + + ret = 0; /* * if the user doesn't want the dataset automatically mounted, * then skip the mount/share step */ - - canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); + if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type)) + canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); /* * Mount and/or share the new filesystem as appropriate. We provide a * verbose error message to let the user know that their filesystem was * in fact created, even if we failed to mount or share it. */ - ret = 0; +#ifdef HAVE_ZPL if (canmount == ZFS_CANMOUNT_ON) { if (zfs_mount(zhp, NULL, 0) != 0) { (void) fprintf(stderr, gettext("filesystem " @@ -826,6 +843,7 @@ zfs_do_create(int argc, char **argv) ret = 1; } } +#endif /* HAVE_ZPL */ error: if (zhp) @@ -1673,7 +1691,7 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data) 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 @@ -1711,7 +1729,7 @@ zfs_do_upgrade(int argc, char **argv) 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 */ @@ -1780,11 +1798,11 @@ zfs_do_upgrade(int argc, char **argv) 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; @@ -1948,9 +1966,9 @@ print_header(zprop_list_t *pl) 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"); @@ -2322,6 +2340,7 @@ typedef struct rollback_cbdata { * 'cb_dependent' is set, then this is a dependent and we should report it * without checking the transaction group. */ +#ifdef HAVE_ZPL static int rollback_check(zfs_handle_t *zhp, void *data) { @@ -2381,10 +2400,12 @@ rollback_check(zfs_handle_t *zhp, void *data) zfs_close(zhp); return (0); } +#endif /* HAVE_ZPL */ static int zfs_do_rollback(int argc, char **argv) { +#ifdef HAVE_ZPL int ret; int c; boolean_t force = B_FALSE; @@ -2466,6 +2487,9 @@ out: return (0); else return (1); +#else + return ENOSYS; +#endif /*HAVE_ZPL*/ } /* @@ -2541,7 +2565,7 @@ zfs_do_set(int argc, char **argv) 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); @@ -2558,7 +2582,7 @@ zfs_do_snapshot(int argc, char **argv) { boolean_t recursive = B_FALSE; int ret; - char c; + signed char c; nvlist_t *props; if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) @@ -2888,7 +2912,7 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) } if (holding) { if (zfs_hold(zhp, delim+1, tag, recursive, - temphold, B_FALSE) != 0) + temphold, B_FALSE, -1, 0, 0) != 0) ++errors; } else { if (zfs_release(zhp, delim+1, tag, recursive) != 0) @@ -2927,18 +2951,11 @@ zfs_do_release(int argc, char **argv) return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); } -typedef struct get_all_cbdata { - zfs_handle_t **cb_handles; - size_t cb_alloc; - size_t cb_used; - uint_t cb_types; - boolean_t cb_verbose; -} get_all_cbdata_t; - #define CHECK_SPINNER 30 #define SPINNER_TIME 3 /* seconds */ #define MOUNT_TIME 5 /* seconds */ +#ifdef HAVE_ZPL static int get_one_dataset(zfs_handle_t *zhp, void *data) { @@ -2946,7 +2963,7 @@ get_one_dataset(zfs_handle_t *zhp, void *data) static int spinval = 0; static int spincheck = 0; static time_t last_spin_time = (time_t)0; - get_all_cbdata_t *cbp = data; + get_all_cb_t *cbp = data; zfs_type_t type = zfs_get_type(zhp); if (cbp->cb_verbose) { @@ -2963,8 +2980,7 @@ get_one_dataset(zfs_handle_t *zhp, void *data) /* * Interate over any nested datasets. */ - if (type == ZFS_TYPE_FILESYSTEM && - zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { + if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { zfs_close(zhp); return (1); } @@ -2972,46 +2988,25 @@ get_one_dataset(zfs_handle_t *zhp, void *data) /* * Skip any datasets whose type does not match. */ - if ((type & cbp->cb_types) == 0) { + if ((type & ZFS_TYPE_FILESYSTEM) == 0) { zfs_close(zhp); return (0); } - - if (cbp->cb_alloc == cbp->cb_used) { - zfs_handle_t **handles; - - if (cbp->cb_alloc == 0) - cbp->cb_alloc = 64; - else - cbp->cb_alloc *= 2; - - handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); - - if (cbp->cb_handles) { - bcopy(cbp->cb_handles, handles, - cbp->cb_used * sizeof (void *)); - free(cbp->cb_handles); - } - - cbp->cb_handles = handles; - } - - cbp->cb_handles[cbp->cb_used++] = zhp; + libzfs_add_handle(cbp, zhp); + assert(cbp->cb_used <= cbp->cb_alloc); return (0); } static void -get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count, - boolean_t verbose) +get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose) { - get_all_cbdata_t cb = { 0 }; - cb.cb_types = types; + get_all_cb_t cb = { 0 }; cb.cb_verbose = verbose; + cb.cb_getone = get_one_dataset; if (verbose) set_progress_header(gettext("Reading ZFS config")); - (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); *dslist = cb.cb_handles; @@ -3021,33 +3016,6 @@ get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count, finish_progress(gettext("done.")); } -static int -dataset_cmp(const void *a, const void *b) -{ - zfs_handle_t **za = (zfs_handle_t **)a; - zfs_handle_t **zb = (zfs_handle_t **)b; - char mounta[MAXPATHLEN]; - char mountb[MAXPATHLEN]; - boolean_t gota, gotb; - - if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) - verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, - sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); - if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) - verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, - sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); - - if (gota && gotb) - return (strcmp(mounta, mountb)); - - if (gota) - return (-1); - if (gotb) - return (1); - - return (strcmp(zfs_get_name(a), zfs_get_name(b))); -} - /* * Generic callback for sharing or mounting filesystems. Because the code is so * similar, we have a common function with an extra parameter to determine which @@ -3069,184 +3037,180 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, const char *cmdname = op == OP_SHARE ? "share" : "mount"; struct mnttab mnt; uint64_t zoned, canmount; - zfs_type_t type = zfs_get_type(zhp); boolean_t shared_nfs, shared_smb; - assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); + assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM); - if (type == ZFS_TYPE_FILESYSTEM) { - /* - * Check to make sure we can mount/share this dataset. If we - * are in the global zone and the filesystem is exported to a - * local zone, or if we are in a local zone and the - * filesystem is not exported, then it is an error. - */ - zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); + /* + * Check to make sure we can mount/share this dataset. If we + * are in the global zone and the filesystem is exported to a + * local zone, or if we are in a local zone and the + * filesystem is not exported, then it is an error. + */ + zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); - if (zoned && getzoneid() == GLOBAL_ZONEID) { - if (!explicit) - return (0); + if (zoned && getzoneid() == GLOBAL_ZONEID) { + if (!explicit) + return (0); - (void) fprintf(stderr, gettext("cannot %s '%s': " - "dataset is exported to a local zone\n"), cmdname, - zfs_get_name(zhp)); - return (1); + (void) fprintf(stderr, gettext("cannot %s '%s': " + "dataset is exported to a local zone\n"), cmdname, + zfs_get_name(zhp)); + return (1); - } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { - if (!explicit) - return (0); + } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { + if (!explicit) + return (0); - (void) fprintf(stderr, gettext("cannot %s '%s': " - "permission denied\n"), cmdname, - zfs_get_name(zhp)); - return (1); - } + (void) fprintf(stderr, gettext("cannot %s '%s': " + "permission denied\n"), cmdname, + zfs_get_name(zhp)); + return (1); + } - /* - * Ignore any filesystems which don't apply to us. This - * includes those with a legacy mountpoint, or those with - * legacy share options. - */ - verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, - sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); - verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, - sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); - verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, - sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); - - if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && - strcmp(smbshareopts, "off") == 0) { - if (!explicit) - return (0); + /* + * Ignore any filesystems which don't apply to us. This + * includes those with a legacy mountpoint, or those with + * legacy share options. + */ + verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, + sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, + sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, + sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); + + if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && + strcmp(smbshareopts, "off") == 0) { + if (!explicit) + return (0); - (void) fprintf(stderr, gettext("cannot share '%s': " - "legacy share\n"), zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use share(1M) to " - "share this filesystem, or set " - "sharenfs property on\n")); - return (1); - } + (void) fprintf(stderr, gettext("cannot share '%s': " + "legacy share\n"), zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use share(1M) to " + "share this filesystem, or set " + "sharenfs property on\n")); + return (1); + } - /* - * We cannot share or mount legacy filesystems. If the - * shareopts is non-legacy but the mountpoint is legacy, we - * treat it as a legacy share. - */ - if (strcmp(mountpoint, "legacy") == 0) { - if (!explicit) - return (0); + /* + * We cannot share or mount legacy filesystems. If the + * shareopts is non-legacy but the mountpoint is legacy, we + * treat it as a legacy share. + */ + if (strcmp(mountpoint, "legacy") == 0) { + if (!explicit) + return (0); - (void) fprintf(stderr, gettext("cannot %s '%s': " - "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use %s(1M) to " - "%s this filesystem\n"), cmdname, cmdname); - return (1); - } + (void) fprintf(stderr, gettext("cannot %s '%s': " + "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use %s(1M) to " + "%s this filesystem\n"), cmdname, cmdname); + return (1); + } - if (strcmp(mountpoint, "none") == 0) { - if (!explicit) - return (0); + if (strcmp(mountpoint, "none") == 0) { + if (!explicit) + return (0); - (void) fprintf(stderr, gettext("cannot %s '%s': no " - "mountpoint set\n"), cmdname, zfs_get_name(zhp)); - return (1); - } + (void) fprintf(stderr, gettext("cannot %s '%s': no " + "mountpoint set\n"), cmdname, zfs_get_name(zhp)); + return (1); + } - /* - * canmount explicit outcome - * on no pass through - * on yes pass through - * off no return 0 - * off yes display error, return 1 - * noauto no return 0 - * noauto yes pass through - */ - canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); - if (canmount == ZFS_CANMOUNT_OFF) { + /* + * canmount explicit outcome + * on no pass through + * on yes pass through + * off no return 0 + * off yes display error, return 1 + * noauto no return 0 + * noauto yes pass through + */ + canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); + if (canmount == ZFS_CANMOUNT_OFF) { + if (!explicit) + return (0); + + (void) fprintf(stderr, gettext("cannot %s '%s': " + "'canmount' property is set to 'off'\n"), cmdname, + zfs_get_name(zhp)); + return (1); + } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { + return (0); + } + + /* + * At this point, we have verified that the mountpoint and/or + * shareopts are appropriate for auto management. If the + * filesystem is already mounted or shared, return (failing + * for explicit requests); otherwise mount or share the + * filesystem. + */ + switch (op) { + case OP_SHARE: + + 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 (!explicit) return (0); - (void) fprintf(stderr, gettext("cannot %s '%s': " - "'canmount' property is set to 'off'\n"), cmdname, + (void) fprintf(stderr, gettext("cannot share " + "'%s': filesystem already shared\n"), zfs_get_name(zhp)); return (1); - } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { - return (0); } - /* - * At this point, we have verified that the mountpoint and/or - * shareopts are appropriate for auto management. If the - * filesystem is already mounted or shared, return (failing - * for explicit requests); otherwise mount or share the - * filesystem. - */ - switch (op) { - case OP_SHARE: - - 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 (!explicit) - return (0); + if (!zfs_is_mounted(zhp, NULL) && + zfs_mount(zhp, NULL, 0) != 0) + return (1); - (void) fprintf(stderr, gettext("cannot share " - "'%s': filesystem already shared\n"), - zfs_get_name(zhp)); + if (protocol == NULL) { + if (zfs_shareall(zhp) != 0) return (1); - } - - if (!zfs_is_mounted(zhp, NULL) && - zfs_mount(zhp, NULL, 0) != 0) + } else if (strcmp(protocol, "nfs") == 0) { + if (zfs_share_nfs(zhp)) return (1); - - if (protocol == NULL) { - if (zfs_shareall(zhp) != 0) - return (1); - } else if (strcmp(protocol, "nfs") == 0) { - if (zfs_share_nfs(zhp)) - return (1); - } else if (strcmp(protocol, "smb") == 0) { - if (zfs_share_smb(zhp)) - return (1); - } else { - (void) fprintf(stderr, gettext("cannot share " - "'%s': invalid share type '%s' " - "specified\n"), - zfs_get_name(zhp), protocol); + } else if (strcmp(protocol, "smb") == 0) { + if (zfs_share_smb(zhp)) return (1); - } - - break; + } else { + (void) fprintf(stderr, gettext("cannot share " + "'%s': invalid share type '%s' " + "specified\n"), + zfs_get_name(zhp), protocol); + return (1); + } - case OP_MOUNT: - if (options == NULL) - mnt.mnt_mntopts = ""; - else - mnt.mnt_mntopts = (char *)options; + break; - if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && - zfs_is_mounted(zhp, NULL)) { - if (!explicit) - return (0); + case OP_MOUNT: + if (options == NULL) + mnt.mnt_mntopts = ""; + else + mnt.mnt_mntopts = (char *)options; - (void) fprintf(stderr, gettext("cannot mount " - "'%s': filesystem already mounted\n"), - zfs_get_name(zhp)); - return (1); - } + if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && + zfs_is_mounted(zhp, NULL)) { + if (!explicit) + return (0); - if (zfs_mount(zhp, options, flags) != 0) - return (1); - break; + (void) fprintf(stderr, gettext("cannot mount " + "'%s': filesystem already mounted\n"), + zfs_get_name(zhp)); + return (1); } - } else - assert(op == OP_SHARE); + + if (zfs_mount(zhp, options, flags) != 0) + return (1); + break; + } return (0); } @@ -3290,7 +3254,7 @@ append_options(char *mntopts, char *newopts) /* 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); } @@ -3308,10 +3272,10 @@ share_mount(int op, int argc, char **argv) boolean_t verbose = B_FALSE; int c, ret = 0; char *options = NULL; - int types, flags = 0; + 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': @@ -3334,9 +3298,6 @@ share_mount(int op, int argc, char **argv) append_options(options, optarg); break; - case 'O': - flags |= MS_OVERLAY; - break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3358,13 +3319,9 @@ share_mount(int op, int argc, char **argv) size_t i, count = 0; char *protocol = NULL; - if (op == OP_MOUNT) { - types = ZFS_TYPE_FILESYSTEM; - } else if (argc > 0) { - if (strcmp(argv[0], "nfs") == 0 || - strcmp(argv[0], "smb") == 0) { - types = ZFS_TYPE_FILESYSTEM; - } else { + if (op == OP_SHARE && argc > 0) { + if (strcmp(argv[0], "nfs") != 0 && + strcmp(argv[0], "smb") != 0) { (void) fprintf(stderr, gettext("share type " "must be 'nfs' or 'smb'\n")); usage(B_FALSE); @@ -3372,8 +3329,6 @@ share_mount(int op, int argc, char **argv) protocol = argv[0]; argc--; argv++; - } else { - types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; } if (argc != 0) { @@ -3382,12 +3337,12 @@ share_mount(int op, int argc, char **argv) } start_progress_timer(); - get_all_datasets(types, &dslist, &count, verbose); + get_all_datasets(&dslist, &count, verbose); if (count == 0) return (0); - qsort(dslist, count, sizeof (void *), dataset_cmp); + qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp); for (i = 0; i < count; i++) { if (verbose) @@ -3410,7 +3365,7 @@ share_mount(int op, int argc, char **argv) } /* - * 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. */ @@ -3427,17 +3382,14 @@ share_mount(int op, int argc, char **argv) } else { zfs_handle_t *zhp; - types = ZFS_TYPE_FILESYSTEM; - if (op == OP_SHARE) - types |= ZFS_TYPE_VOLUME; - if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } - if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { + if ((zhp = zfs_open(g_zfs, argv[0], + ZFS_TYPE_FILESYSTEM)) == NULL) { ret = 1; } else { ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, @@ -3448,6 +3400,7 @@ share_mount(int op, int argc, char **argv) return (ret); } +#endif /* HAVE_ZPL */ /* * zfs mount -a [nfs] @@ -3458,7 +3411,11 @@ share_mount(int op, int argc, char **argv) static int zfs_do_mount(int argc, char **argv) { +#ifdef HAVE_ZPL return (share_mount(OP_MOUNT, argc, argv)); +#else + return ENOSYS; +#endif /* HAVE_ZPL */ } /* @@ -3470,9 +3427,14 @@ zfs_do_mount(int argc, char **argv) static int zfs_do_share(int argc, char **argv) { +#ifdef HAVE_ZPL return (share_mount(OP_SHARE, argc, argv)); +#else + return ENOSYS; +#endif /* HAVE_ZPL */ } +#ifdef HAVE_ZPL typedef struct unshare_unmount_node { zfs_handle_t *un_zhp; char *un_mountp; @@ -3491,7 +3453,7 @@ unshare_unmount_compare(const void *larg, const void *rarg, void *unused) /* * 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 @@ -3505,7 +3467,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 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. @@ -3532,7 +3494,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) "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, @@ -3574,8 +3536,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 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); @@ -3594,7 +3556,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) (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); @@ -3616,7 +3578,7 @@ unshare_unmount(int op, int argc, char **argv) int do_all = 0; int flags = 0; int ret = 0; - int types, c; + int c; zfs_handle_t *zhp; char nfs_mnt_prop[ZFS_MAXPROPLEN]; char sharesmb[ZFS_MAXPROPLEN]; @@ -3644,8 +3606,8 @@ unshare_unmount(int op, int argc, char **argv) /* * 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 @@ -3657,7 +3619,7 @@ unshare_unmount(int op, int argc, char **argv) */ 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; @@ -3792,68 +3754,63 @@ unshare_unmount(int op, int argc, char **argv) return (unshare_unmount_path(op, argv[0], flags, B_FALSE)); - types = ZFS_TYPE_FILESYSTEM; - if (op == OP_SHARE) - types |= ZFS_TYPE_VOLUME; - - if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) + if ((zhp = zfs_open(g_zfs, argv[0], + ZFS_TYPE_FILESYSTEM)) == NULL) return (1); - if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - verify(zfs_prop_get(zhp, op == OP_SHARE ? - ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, - nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, - NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, op == OP_SHARE ? + ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, + nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, + NULL, 0, B_FALSE) == 0); - switch (op) { - case OP_SHARE: - verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, - nfs_mnt_prop, - sizeof (nfs_mnt_prop), - NULL, NULL, 0, B_FALSE) == 0); - verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, - sharesmb, sizeof (sharesmb), NULL, NULL, - 0, B_FALSE) == 0); - - if (strcmp(nfs_mnt_prop, "off") == 0 && - strcmp(sharesmb, "off") == 0) { - (void) fprintf(stderr, gettext("cannot " - "unshare '%s': legacy share\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use " - "unshare(1M) to unshare this " - "filesystem\n")); - ret = 1; - } else if (!zfs_is_shared(zhp)) { - (void) fprintf(stderr, gettext("cannot " - "unshare '%s': not currently " - "shared\n"), zfs_get_name(zhp)); - ret = 1; - } else if (zfs_unshareall(zhp) != 0) { - ret = 1; - } - break; + switch (op) { + case OP_SHARE: + verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, + nfs_mnt_prop, + sizeof (nfs_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, + sharesmb, sizeof (sharesmb), NULL, NULL, + 0, B_FALSE) == 0); + + if (strcmp(nfs_mnt_prop, "off") == 0 && + strcmp(sharesmb, "off") == 0) { + (void) fprintf(stderr, gettext("cannot " + "unshare '%s': legacy share\n"), + zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use " + "unshare(1M) to unshare this " + "filesystem\n")); + ret = 1; + } else if (!zfs_is_shared(zhp)) { + (void) fprintf(stderr, gettext("cannot " + "unshare '%s': not currently " + "shared\n"), zfs_get_name(zhp)); + ret = 1; + } else if (zfs_unshareall(zhp) != 0) { + ret = 1; + } + break; - case OP_MOUNT: - if (strcmp(nfs_mnt_prop, "legacy") == 0) { - (void) fprintf(stderr, gettext("cannot " - "unmount '%s': legacy " - "mountpoint\n"), zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use " - "umount(1M) to unmount this " - "filesystem\n")); - ret = 1; - } else if (!zfs_is_mounted(zhp, NULL)) { - (void) fprintf(stderr, gettext("cannot " - "unmount '%s': not currently " - "mounted\n"), - zfs_get_name(zhp)); - ret = 1; - } else if (zfs_unmountall(zhp, flags) != 0) { - ret = 1; - } - break; + case OP_MOUNT: + if (strcmp(nfs_mnt_prop, "legacy") == 0) { + (void) fprintf(stderr, gettext("cannot " + "unmount '%s': legacy " + "mountpoint\n"), zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use " + "umount(1M) to unmount this " + "filesystem\n")); + ret = 1; + } else if (!zfs_is_mounted(zhp, NULL)) { + (void) fprintf(stderr, gettext("cannot " + "unmount '%s': not currently " + "mounted\n"), + zfs_get_name(zhp)); + ret = 1; + } else if (zfs_unmountall(zhp, flags) != 0) { + ret = 1; } + break; } zfs_close(zhp); @@ -3861,6 +3818,7 @@ unshare_unmount(int op, int argc, char **argv) return (ret); } +#endif /* HAVE_ZPL */ /* * zfs unmount -a @@ -3871,7 +3829,11 @@ unshare_unmount(int op, int argc, char **argv) static int zfs_do_unmount(int argc, char **argv) { +#ifdef HAVE_ZPL return (unshare_unmount(OP_MOUNT, argc, argv)); +#else + return ENOSYS; +#endif /* HAVE_ZPL */ } /* @@ -3883,7 +3845,11 @@ zfs_do_unmount(int argc, char **argv) static int zfs_do_unshare(int argc, char **argv) { +#ifdef HAVE_ZPL return (unshare_unmount(OP_SHARE, argc, argv)); +#else + return ENOSYS; +#endif /* HAVE_ZPL */ } /* ARGSUSED */ @@ -3895,44 +3861,208 @@ zfs_do_python(int argc, char **argv) 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 }, + { MNTOPT_RBIND, MS_BIND|MS_REC }, + { 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); +} + +/* + * 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 /etc/fs/zfs/mount. Do the mount if the mountpoint is - * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. + * 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] " - "\n")); - return (2); + (void) fprintf(stderr, gettext("Usage: mount.zfs " + "[-sfnv] [-o options] \n")); + return (MOUNT_USAGE); } } @@ -3950,85 +4080,149 @@ manual_mount(int argc, char **argv) else (void) fprintf(stderr, gettext("too many arguments\n")); (void) fprintf(stderr, "usage: mount \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); } - return (ret); + /* 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); + } + } + + 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] " - "\n")); - return (2); + (void) fprintf(stderr, gettext("Usage: umount.zfs " + "[-nlfvr] \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] \n")); - return (2); + + (void) fprintf(stderr, gettext("Usage: umount.zfs [-nlfvr] " + "\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) @@ -4047,11 +4241,86 @@ find_command_idx(char *command, int *idx) return (1); } +static int +zfs_do_diff(int argc, char **argv) +{ + zfs_handle_t *zhp; + int flags = 0; + char *tosnap = NULL; + char *fromsnap = NULL; + char *atp, *copy; + int err; + int c; + + while ((c = getopt(argc, argv, "FHt")) != -1) { + switch (c) { + case 'F': + flags |= ZFS_DIFF_CLASSIFY; + break; + case 'H': + flags |= ZFS_DIFF_PARSEABLE; + break; + case 't': + flags |= ZFS_DIFF_TIMESTAMP; + break; + default: + (void) fprintf(stderr, + gettext("invalid option '%c'\n"), optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, + gettext("must provide at least one snapshot name\n")); + usage(B_FALSE); + } + + if (argc > 2) { + (void) fprintf(stderr, gettext("too many arguments\n")); + usage(B_FALSE); + } + + fromsnap = argv[0]; + tosnap = (argc == 2) ? argv[1] : NULL; + + copy = NULL; + if (*fromsnap != '@') + copy = strdup(fromsnap); + else if (tosnap) + copy = strdup(tosnap); + if (copy == NULL) + usage(B_FALSE); + + if ((atp = strchr(copy, '@'))) + *atp = '\0'; + + if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) + return (1); + + free(copy); + + /* + * Ignore SIGPIPE so that the library can give us + * information on any failure + */ + (void) sigignore(SIGPIPE); + + err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags); + + zfs_close(zhp); + + return (err != 0); +} + int main(int argc, char **argv) { int ret; - int i; + int i = 0; char *progname; char *cmdname; @@ -4060,17 +4329,6 @@ main(int argc, char **argv) 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); @@ -4082,10 +4340,12 @@ main(int argc, char **argv) * 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. @@ -4112,9 +4372,18 @@ main(int argc, char **argv) /* * 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. */ @@ -4130,6 +4399,7 @@ main(int argc, char **argv) (void) fprintf(stderr, gettext("unrecognized " "command '%s'\n"), cmdname); usage(B_FALSE); + ret = 1; } libzfs_mnttab_cache(g_zfs, B_FALSE); }