1fa9e4066Sahrens /*
2fa9e4066Sahrens * CDDL HEADER START
3fa9e4066Sahrens *
4fa9e4066Sahrens * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock * You may not use this file except in compliance with the License.
7fa9e4066Sahrens *
8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens * See the License for the specific language governing permissions
11fa9e4066Sahrens * and limitations under the License.
12fa9e4066Sahrens *
13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens *
19fa9e4066Sahrens * CDDL HEADER END
20fa9e4066Sahrens */
21f3861e1aSahl
22fa9e4066Sahrens /*
233f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24dfc11533SChris Williamson * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
2533f5ff17SMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved.
26b73ccab0SMike Gerdts * Copyright 2019 Joyent, Inc.
270d8fa8f8SMartin Matuska * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
28a7a845e4SSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved.
29c3d26abcSMatthew Ahrens * Copyright (c) 2014 Integros [integros.com]
30c16bcc45SIgor Kozhukhov * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
318808ac5dSYuri Pankov * Copyright 2016 Nexenta Systems, Inc.
3252675910SAlek Pinchuk * Copyright (c) 2018 Datto Inc.
331e9e241fSAndrew Stormont * Copyright 2021 RackTop Systems, Inc.
34fa9e4066Sahrens */
35fa9e4066Sahrens
36fa9e4066Sahrens #include <assert.h>
37e9dbad6fSeschrock #include <ctype.h>
38fa9e4066Sahrens #include <errno.h>
395602294fSDan Kimmel #include <getopt.h>
40fa9e4066Sahrens #include <libgen.h>
41fa9e4066Sahrens #include <libintl.h>
42fa9e4066Sahrens #include <libuutil.h>
43ecd6cf80Smarks #include <libnvpair.h>
44fa9e4066Sahrens #include <locale.h>
45fa9e4066Sahrens #include <stddef.h>
46fa9e4066Sahrens #include <stdio.h>
47fa9e4066Sahrens #include <stdlib.h>
48fa9e4066Sahrens #include <strings.h>
49fa9e4066Sahrens #include <unistd.h>
50fa9e4066Sahrens #include <fcntl.h>
51fa9e4066Sahrens #include <zone.h>
5214843421SMatthew Ahrens #include <grp.h>
5314843421SMatthew Ahrens #include <pwd.h>
5499d5e173STim Haley #include <signal.h>
55dfc11533SChris Williamson #include <sys/debug.h>
561af68beaSAlexander Stetsenko #include <sys/list.h>
573cf6ec9cSalex #include <sys/sysmacros.h>
58fa9e4066Sahrens #include <sys/mkdev.h>
59fa9e4066Sahrens #include <sys/mntent.h>
60fa9e4066Sahrens #include <sys/mnttab.h>
61fa9e4066Sahrens #include <sys/mount.h>
62fa9e4066Sahrens #include <sys/stat.h>
6314843421SMatthew Ahrens #include <sys/fs/zfs.h>
64a8b6ddafSMark J Musante #include <sys/types.h>
65a8b6ddafSMark J Musante #include <time.h>
66f67950b2SNasf-Fan #include <sys/zfs_project.h>
67591e0e13SSebastien Roy #include <synch.h>
68fa9e4066Sahrens
69fa9e4066Sahrens #include <libzfs.h>
704445fffbSMatthew Ahrens #include <libzfs_core.h>
711af68beaSAlexander Stetsenko #include <zfs_prop.h>
721af68beaSAlexander Stetsenko #include <zfs_deleg.h>
73d8ab6e12SDon Brady #include <libzutil.h>
74ecd6cf80Smarks #include <libuutil.h>
751af68beaSAlexander Stetsenko #include <aclutils.h>
761af68beaSAlexander Stetsenko #include <directory.h>
771ed6b69aSGordon Ross #include <idmap.h>
788a981c33SDaniel Hoffman #include <libshare.h>
79fa9e4066Sahrens
80fa9e4066Sahrens #include "zfs_iter.h"
8199653d4eSeschrock #include "zfs_util.h"
820a586ceaSMark Shellenbaum #include "zfs_comutil.h"
83f67950b2SNasf-Fan #include "zfs_projectutil.h"
8499653d4eSeschrock
8599653d4eSeschrock libzfs_handle_t *g_zfs;
86fa9e4066Sahrens
87fa9e4066Sahrens static FILE *mnttab_file;
882a6b87f0Sek110237 static char history_str[HIS_MAX_RECORD_LEN];
894445fffbSMatthew Ahrens static boolean_t log_history = B_TRUE;
90fa9e4066Sahrens
91fa9e4066Sahrens static int zfs_do_clone(int argc, char **argv);
92fa9e4066Sahrens static int zfs_do_create(int argc, char **argv);
93fa9e4066Sahrens static int zfs_do_destroy(int argc, char **argv);
94fa9e4066Sahrens static int zfs_do_get(int argc, char **argv);
95fa9e4066Sahrens static int zfs_do_inherit(int argc, char **argv);
96fa9e4066Sahrens static int zfs_do_list(int argc, char **argv);
97fa9e4066Sahrens static int zfs_do_mount(int argc, char **argv);
98fa9e4066Sahrens static int zfs_do_rename(int argc, char **argv);
99fa9e4066Sahrens static int zfs_do_rollback(int argc, char **argv);
100fa9e4066Sahrens static int zfs_do_set(int argc, char **argv);
101e7437265Sahrens static int zfs_do_upgrade(int argc, char **argv);
102fa9e4066Sahrens static int zfs_do_snapshot(int argc, char **argv);
103fa9e4066Sahrens static int zfs_do_unmount(int argc, char **argv);
104fa9e4066Sahrens static int zfs_do_share(int argc, char **argv);
105fa9e4066Sahrens static int zfs_do_unshare(int argc, char **argv);
106f2a3c691Sahrens static int zfs_do_send(int argc, char **argv);
107f2a3c691Sahrens static int zfs_do_receive(int argc, char **argv);
10899653d4eSeschrock static int zfs_do_promote(int argc, char **argv);
10914843421SMatthew Ahrens static int zfs_do_userspace(int argc, char **argv);
1101af68beaSAlexander Stetsenko static int zfs_do_allow(int argc, char **argv);
1111af68beaSAlexander Stetsenko static int zfs_do_unallow(int argc, char **argv);
112842727c2SChris Kirby static int zfs_do_hold(int argc, char **argv);
1131af68beaSAlexander Stetsenko static int zfs_do_holds(int argc, char **argv);
114842727c2SChris Kirby static int zfs_do_release(int argc, char **argv);
11599d5e173STim Haley static int zfs_do_diff(int argc, char **argv);
11678f17100SMatthew Ahrens static int zfs_do_bookmark(int argc, char **argv);
1175cabbc6bSPrashanth Sreenivasa static int zfs_do_remap(int argc, char **argv);
118dfc11533SChris Williamson static int zfs_do_channel_program(int argc, char **argv);
119eb633035STom Caputi static int zfs_do_load_key(int argc, char **argv);
120eb633035STom Caputi static int zfs_do_unload_key(int argc, char **argv);
121eb633035STom Caputi static int zfs_do_change_key(int argc, char **argv);
122f67950b2SNasf-Fan static int zfs_do_project(int argc, char **argv);
123fa9e4066Sahrens
124fa9e4066Sahrens /*
12529ab75c9Srm160521 * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
126fa9e4066Sahrens */
12729ab75c9Srm160521
12829ab75c9Srm160521 #ifdef DEBUG
129fa9e4066Sahrens const char *
_umem_debug_init(void)130f3861e1aSahl _umem_debug_init(void)
131fa9e4066Sahrens {
132fa9e4066Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */
133fa9e4066Sahrens }
134fa9e4066Sahrens
135fa9e4066Sahrens const char *
_umem_logging_init(void)136fa9e4066Sahrens _umem_logging_init(void)
137fa9e4066Sahrens {
138fa9e4066Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */
139fa9e4066Sahrens }
14029ab75c9Srm160521 #endif
141fa9e4066Sahrens
14265cd9f28Seschrock typedef enum {
14365cd9f28Seschrock HELP_CLONE,
14465cd9f28Seschrock HELP_CREATE,
14565cd9f28Seschrock HELP_DESTROY,
14665cd9f28Seschrock HELP_GET,
14765cd9f28Seschrock HELP_INHERIT,
148e7437265Sahrens HELP_UPGRADE,
14965cd9f28Seschrock HELP_LIST,
15065cd9f28Seschrock HELP_MOUNT,
15199653d4eSeschrock HELP_PROMOTE,
152f2a3c691Sahrens HELP_RECEIVE,
15365cd9f28Seschrock HELP_RENAME,
15465cd9f28Seschrock HELP_ROLLBACK,
155f2a3c691Sahrens HELP_SEND,
15665cd9f28Seschrock HELP_SET,
15765cd9f28Seschrock HELP_SHARE,
15865cd9f28Seschrock HELP_SNAPSHOT,
15965cd9f28Seschrock HELP_UNMOUNT,
160ecd6cf80Smarks HELP_UNSHARE,
161ecd6cf80Smarks HELP_ALLOW,
16214843421SMatthew Ahrens HELP_UNALLOW,
16314843421SMatthew Ahrens HELP_USERSPACE,
164842727c2SChris Kirby HELP_GROUPSPACE,
165f67950b2SNasf-Fan HELP_PROJECTSPACE,
166f67950b2SNasf-Fan HELP_PROJECT,
167842727c2SChris Kirby HELP_HOLD,
168842727c2SChris Kirby HELP_HOLDS,
16999d5e173STim Haley HELP_RELEASE,
17019b94df9SMatthew Ahrens HELP_DIFF,
1715cabbc6bSPrashanth Sreenivasa HELP_REMAP,
17278f17100SMatthew Ahrens HELP_BOOKMARK,
173dfc11533SChris Williamson HELP_CHANNEL_PROGRAM,
174eb633035STom Caputi HELP_LOAD_KEY,
175eb633035STom Caputi HELP_UNLOAD_KEY,
176eb633035STom Caputi HELP_CHANGE_KEY,
17765cd9f28Seschrock } zfs_help_t;
17865cd9f28Seschrock
179fa9e4066Sahrens typedef struct zfs_command {
180fa9e4066Sahrens const char *name;
181fa9e4066Sahrens int (*func)(int argc, char **argv);
18265cd9f28Seschrock zfs_help_t usage;
183fa9e4066Sahrens } zfs_command_t;
184fa9e4066Sahrens
185fa9e4066Sahrens /*
186fa9e4066Sahrens * Master command table. Each ZFS command has a name, associated function, and
187ea8dc4b6Seschrock * usage message. The usage messages need to be internationalized, so we have
188ea8dc4b6Seschrock * to have a function to return the usage message based on a command index.
18965cd9f28Seschrock *
19065cd9f28Seschrock * These commands are organized according to how they are displayed in the usage
19165cd9f28Seschrock * message. An empty command (one with a NULL name) indicates an empty line in
19265cd9f28Seschrock * the generic usage message.
193fa9e4066Sahrens */
194fa9e4066Sahrens static zfs_command_t command_table[] = {
19565cd9f28Seschrock { "create", zfs_do_create, HELP_CREATE },
19665cd9f28Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY },
197fa9e4066Sahrens { NULL },
19865cd9f28Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },
19965cd9f28Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK },
20065cd9f28Seschrock { "clone", zfs_do_clone, HELP_CLONE },
20199653d4eSeschrock { "promote", zfs_do_promote, HELP_PROMOTE },
20265cd9f28Seschrock { "rename", zfs_do_rename, HELP_RENAME },
20378f17100SMatthew Ahrens { "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
204dfc11533SChris Williamson { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },
205fa9e4066Sahrens { NULL },
20665cd9f28Seschrock { "list", zfs_do_list, HELP_LIST },
207fa9e4066Sahrens { NULL },
20865cd9f28Seschrock { "set", zfs_do_set, HELP_SET },
20965cd9f28Seschrock { "get", zfs_do_get, HELP_GET },
21065cd9f28Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT },
211e7437265Sahrens { "upgrade", zfs_do_upgrade, HELP_UPGRADE },
212f67950b2SNasf-Fan { NULL },
21314843421SMatthew Ahrens { "userspace", zfs_do_userspace, HELP_USERSPACE },
21414843421SMatthew Ahrens { "groupspace", zfs_do_userspace, HELP_GROUPSPACE },
215f67950b2SNasf-Fan { "projectspace", zfs_do_userspace, HELP_PROJECTSPACE },
216f67950b2SNasf-Fan { NULL },
217f67950b2SNasf-Fan { "project", zfs_do_project, HELP_PROJECT },
218fa9e4066Sahrens { NULL },
21965cd9f28Seschrock { "mount", zfs_do_mount, HELP_MOUNT },
22065cd9f28Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT },
22165cd9f28Seschrock { "share", zfs_do_share, HELP_SHARE },
22265cd9f28Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE },
223fa9e4066Sahrens { NULL },
224f2a3c691Sahrens { "send", zfs_do_send, HELP_SEND },
225f2a3c691Sahrens { "receive", zfs_do_receive, HELP_RECEIVE },
226ecd6cf80Smarks { NULL },
2271af68beaSAlexander Stetsenko { "allow", zfs_do_allow, HELP_ALLOW },
228ecd6cf80Smarks { NULL },
2291af68beaSAlexander Stetsenko { "unallow", zfs_do_unallow, HELP_UNALLOW },
230842727c2SChris Kirby { NULL },
231842727c2SChris Kirby { "hold", zfs_do_hold, HELP_HOLD },
2321af68beaSAlexander Stetsenko { "holds", zfs_do_holds, HELP_HOLDS },
233842727c2SChris Kirby { "release", zfs_do_release, HELP_RELEASE },
23499d5e173STim Haley { "diff", zfs_do_diff, HELP_DIFF },
2355cabbc6bSPrashanth Sreenivasa { "remap", zfs_do_remap, HELP_REMAP },
236eb633035STom Caputi { "load-key", zfs_do_load_key, HELP_LOAD_KEY },
237eb633035STom Caputi { "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY },
238eb633035STom Caputi { "change-key", zfs_do_change_key, HELP_CHANGE_KEY },
239fa9e4066Sahrens };
240fa9e4066Sahrens
241fa9e4066Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
242fa9e4066Sahrens
243fa9e4066Sahrens zfs_command_t *current_command;
244fa9e4066Sahrens
24565cd9f28Seschrock static const char *
get_usage(zfs_help_t idx)24665cd9f28Seschrock get_usage(zfs_help_t idx)
24765cd9f28Seschrock {
24865cd9f28Seschrock switch (idx) {
24965cd9f28Seschrock case HELP_CLONE:
250bb0ade09Sahrens return (gettext("\tclone [-p] [-o property=value] ... "
251bb0ade09Sahrens "<snapshot> <filesystem|volume>\n"));
25265cd9f28Seschrock case HELP_CREATE:
2531320ddf5SMike Gerdts return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
254e9dbad6fSeschrock "<filesystem>\n"
2551320ddf5SMike Gerdts "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
256e45ce728Sahrens "-V <size> <volume>\n"));
25765cd9f28Seschrock case HELP_DESTROY:
25819b94df9SMatthew Ahrens return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
25919b94df9SMatthew Ahrens "\tdestroy [-dnpRrv] "
26078f17100SMatthew Ahrens "<filesystem|volume>@<snap>[%<snap>][,...]\n"
26178f17100SMatthew Ahrens "\tdestroy <filesystem|volume>#<bookmark>\n"));
26265cd9f28Seschrock case HELP_GET:
263ae1726b6SChris Gerhard return (gettext("\tget [-rHp] [-d max] "
26478f17100SMatthew Ahrens "[-o \"all\" | field[,...]]\n"
26578f17100SMatthew Ahrens "\t [-t type[,...]] [-s source[,...]]\n"
266e45ce728Sahrens "\t <\"all\" | property[,...]> "
267edb901aaSMarcel Telka "[filesystem|volume|snapshot|bookmark] ...\n"));
26865cd9f28Seschrock case HELP_INHERIT:
26992241e0bSTom Erickson return (gettext("\tinherit [-rS] <property> "
270bb0ade09Sahrens "<filesystem|volume|snapshot> ...\n"));
271e7437265Sahrens case HELP_UPGRADE:
272e7437265Sahrens return (gettext("\tupgrade [-v]\n"
273e7437265Sahrens "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
27465cd9f28Seschrock case HELP_LIST:
27543d68d68SYuri Pankov return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
27643d68d68SYuri Pankov "[-s property]...\n\t [-S property]... [-t type[,...]] "
277e45ce728Sahrens "[filesystem|volume|snapshot] ...\n"));
27865cd9f28Seschrock case HELP_MOUNT:
27965cd9f28Seschrock return (gettext("\tmount\n"
280eb633035STom Caputi "\tmount [-lvO] [-o opts] <-a | filesystem>\n"));
28199653d4eSeschrock case HELP_PROMOTE:
282e45ce728Sahrens return (gettext("\tpromote <clone-filesystem>\n"));
283f2a3c691Sahrens case HELP_RECEIVE:
2846ccda740Sloli10K return (gettext("\treceive [-vnsFhu] "
2856ccda740Sloli10K "[-o <property>=<value>] ... [-x <property>] ...\n"
2866ccda740Sloli10K "\t <filesystem|volume|snapshot>\n"
2876ccda740Sloli10K "\treceive [-vnsFhu] [-o <property>=<value>] ... "
2886ccda740Sloli10K "[-x <property>] ... \n"
2896ccda740Sloli10K "\t [-d | -e] <filesystem>\n"
2909c3fd121SMatthew Ahrens "\treceive -A <filesystem|volume>\n"));
29165cd9f28Seschrock case HELP_RENAME:
2926a9cb0eaSEric Schrock return (gettext("\trename [-f] <filesystem|volume|snapshot> "
293cdf5b4caSmmusante "<filesystem|volume|snapshot>\n"
2946a9cb0eaSEric Schrock "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
29578f17100SMatthew Ahrens "\trename -r <snapshot> <snapshot>\n"));
29665cd9f28Seschrock case HELP_ROLLBACK:
297f17c7ff8Sahrens return (gettext("\trollback [-rRf] <snapshot>\n"));
298f2a3c691Sahrens case HELP_SEND:
2996ccda740Sloli10K return (gettext("\tsend [-DnPpRvLecwhb] [-[iI] snapshot] "
30078f17100SMatthew Ahrens "<snapshot>\n"
3016ccda740Sloli10K "\tsend [-nvPLecw] [-i snapshot|bookmark] "
3029c3fd121SMatthew Ahrens "<filesystem|volume|snapshot>\n"
3039c3fd121SMatthew Ahrens "\tsend [-nvPe] -t <receive_resume_token>\n"));
30465cd9f28Seschrock case HELP_SET:
30530925561SChris Williamson return (gettext("\tset <property=value> ... "
306bb0ade09Sahrens "<filesystem|volume|snapshot> ...\n"));
30765cd9f28Seschrock case HELP_SHARE:
308eb633035STom Caputi return (gettext("\tshare [-l] <-a | filesystem>\n"));
30965cd9f28Seschrock case HELP_SNAPSHOT:
310bb0ade09Sahrens return (gettext("\tsnapshot [-r] [-o property=value] ... "
31178f17100SMatthew Ahrens "<filesystem|volume>@<snap> ...\n"));
31265cd9f28Seschrock case HELP_UNMOUNT:
313e45ce728Sahrens return (gettext("\tunmount [-f] "
314e45ce728Sahrens "<-a | filesystem|mountpoint>\n"));
31565cd9f28Seschrock case HELP_UNSHARE:
3162269545aSstephanie scheffler return (gettext("\tunshare "
317e45ce728Sahrens "<-a | filesystem|mountpoint>\n"));
318ecd6cf80Smarks case HELP_ALLOW:
31960b94cc3Sstephanie scheffler return (gettext("\tallow <filesystem|volume>\n"
32060b94cc3Sstephanie scheffler "\tallow [-ldug] "
321e45ce728Sahrens "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
322e45ce728Sahrens "\t <filesystem|volume>\n"
323e45ce728Sahrens "\tallow [-ld] -e <perm|@setname>[,...] "
324ecd6cf80Smarks "<filesystem|volume>\n"
325e45ce728Sahrens "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
326e45ce728Sahrens "\tallow -s @setname <perm|@setname>[,...] "
327ecd6cf80Smarks "<filesystem|volume>\n"));
328ecd6cf80Smarks case HELP_UNALLOW:
329e45ce728Sahrens return (gettext("\tunallow [-rldug] "
330e45ce728Sahrens "<\"everyone\"|user|group>[,...]\n"
331e45ce728Sahrens "\t [<perm|@setname>[,...]] <filesystem|volume>\n"
332e45ce728Sahrens "\tunallow [-rld] -e [<perm|@setname>[,...]] "
333ecd6cf80Smarks "<filesystem|volume>\n"
334e45ce728Sahrens "\tunallow [-r] -c [<perm|@setname>[,...]] "
335ecd6cf80Smarks "<filesystem|volume>\n"
336e45ce728Sahrens "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
337e45ce728Sahrens "<filesystem|volume>\n"));
33814843421SMatthew Ahrens case HELP_USERSPACE:
33989f5d17bSYuri Pankov return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
34078f17100SMatthew Ahrens "[-s field] ...\n"
34178f17100SMatthew Ahrens "\t [-S field] ... [-t type[,...]] "
34243d68d68SYuri Pankov "<filesystem|snapshot>\n"));
34314843421SMatthew Ahrens case HELP_GROUPSPACE:
34489f5d17bSYuri Pankov return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
34578f17100SMatthew Ahrens "[-s field] ...\n"
34678f17100SMatthew Ahrens "\t [-S field] ... [-t type[,...]] "
34743d68d68SYuri Pankov "<filesystem|snapshot>\n"));
348f67950b2SNasf-Fan case HELP_PROJECTSPACE:
349f67950b2SNasf-Fan return (gettext("\tprojectspace [-Hp] [-o field[,...]] "
350f67950b2SNasf-Fan "[-s field] ... \n"
351f67950b2SNasf-Fan "\t [-S field] ... <filesystem|snapshot>\n"));
352f67950b2SNasf-Fan case HELP_PROJECT:
353f67950b2SNasf-Fan return (gettext("\tproject [-d|-r] <directory|file ...>\n"
354f67950b2SNasf-Fan "\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"
355f67950b2SNasf-Fan "\tproject -C [-k] [-r] <directory ...>\n"
356f67950b2SNasf-Fan "\tproject [-p id] [-r] [-s] <directory ...>\n"));
357842727c2SChris Kirby case HELP_HOLD:
358842727c2SChris Kirby return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
359842727c2SChris Kirby case HELP_HOLDS:
360842727c2SChris Kirby return (gettext("\tholds [-r] <snapshot> ...\n"));
361842727c2SChris Kirby case HELP_RELEASE:
362842727c2SChris Kirby return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
36399d5e173STim Haley case HELP_DIFF:
36499d5e173STim Haley return (gettext("\tdiff [-FHt] <snapshot> "
36599d5e173STim Haley "[snapshot|filesystem]\n"));
3665cabbc6bSPrashanth Sreenivasa case HELP_REMAP:
3675cabbc6bSPrashanth Sreenivasa return (gettext("\tremap <filesystem | volume>\n"));
36878f17100SMatthew Ahrens case HELP_BOOKMARK:
36978f17100SMatthew Ahrens return (gettext("\tbookmark <snapshot> <bookmark>\n"));
370dfc11533SChris Williamson case HELP_CHANNEL_PROGRAM:
37152675910SAlek Pinchuk return (gettext("\tprogram [-jn] [-t <instruction limit>] "
372dfc11533SChris Williamson "[-m <memory limit (b)>] <pool> <program file> "
373dfc11533SChris Williamson "[lua args...]\n"));
374eb633035STom Caputi case HELP_LOAD_KEY:
375eb633035STom Caputi return (gettext("\tload-key [-rn] [-L <keylocation>] "
376eb633035STom Caputi "<-a | filesystem|volume>\n"));
377eb633035STom Caputi case HELP_UNLOAD_KEY:
378eb633035STom Caputi return (gettext("\tunload-key [-r] "
379eb633035STom Caputi "<-a | filesystem|volume>\n"));
380eb633035STom Caputi case HELP_CHANGE_KEY:
381eb633035STom Caputi return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
382eb633035STom Caputi "\t [-o keylocation=<value>] [-o pbkfd2iters=<value>]\n"
383eb633035STom Caputi "\t <filesystem|volume>\n"
384eb633035STom Caputi "\tchange-key -i [-l] <filesystem|volume>\n"));
38565cd9f28Seschrock }
38665cd9f28Seschrock
38765cd9f28Seschrock abort();
38865cd9f28Seschrock /* NOTREACHED */
38965cd9f28Seschrock }
39065cd9f28Seschrock
391a8b6ddafSMark J Musante void
nomem(void)392a8b6ddafSMark J Musante nomem(void)
393a8b6ddafSMark J Musante {
394a8b6ddafSMark J Musante (void) fprintf(stderr, gettext("internal error: out of memory\n"));
395a8b6ddafSMark J Musante exit(1);
396a8b6ddafSMark J Musante }
397a8b6ddafSMark J Musante
398fa9e4066Sahrens /*
399fa9e4066Sahrens * Utility function to guarantee malloc() success.
400fa9e4066Sahrens */
401a8b6ddafSMark J Musante
402fa9e4066Sahrens void *
safe_malloc(size_t size)403fa9e4066Sahrens safe_malloc(size_t size)
404fa9e4066Sahrens {
405fa9e4066Sahrens void *data;
406fa9e4066Sahrens
407a8b6ddafSMark J Musante if ((data = calloc(1, size)) == NULL)
408a8b6ddafSMark J Musante nomem();
409fa9e4066Sahrens
410fa9e4066Sahrens return (data);
411fa9e4066Sahrens }
412fa9e4066Sahrens
413dfc11533SChris Williamson void *
safe_realloc(void * data,size_t size)414dfc11533SChris Williamson safe_realloc(void *data, size_t size)
415dfc11533SChris Williamson {
416dfc11533SChris Williamson void *newp;
417dfc11533SChris Williamson if ((newp = realloc(data, size)) == NULL) {
418dfc11533SChris Williamson free(data);
419dfc11533SChris Williamson nomem();
420dfc11533SChris Williamson }
421dfc11533SChris Williamson
422dfc11533SChris Williamson return (newp);
423dfc11533SChris Williamson }
424dfc11533SChris Williamson
425a8b6ddafSMark J Musante static char *
safe_strdup(char * str)426a8b6ddafSMark J Musante safe_strdup(char *str)
427a8b6ddafSMark J Musante {
428a8b6ddafSMark J Musante char *dupstr = strdup(str);
429a8b6ddafSMark J Musante
430a8b6ddafSMark J Musante if (dupstr == NULL)
431a8b6ddafSMark J Musante nomem();
432a8b6ddafSMark J Musante
433a8b6ddafSMark J Musante return (dupstr);
434a8b6ddafSMark J Musante }
435a8b6ddafSMark J Musante
436fa9e4066Sahrens /*
437990b4856Slling * Callback routine that will print out information for each of
43866e2aaccSgw25295 * the properties.
43966e2aaccSgw25295 */
440990b4856Slling static int
usage_prop_cb(int prop,void * cb)441990b4856Slling usage_prop_cb(int prop, void *cb)
44266e2aaccSgw25295 {
44366e2aaccSgw25295 FILE *fp = cb;
44466e2aaccSgw25295
445c5904d13Seschrock (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
44666e2aaccSgw25295
447c5904d13Seschrock if (zfs_prop_readonly(prop))
44866e2aaccSgw25295 (void) fprintf(fp, " NO ");
44966e2aaccSgw25295 else
45066e2aaccSgw25295 (void) fprintf(fp, "YES ");
45166e2aaccSgw25295
45266e2aaccSgw25295 if (zfs_prop_inheritable(prop))
45366e2aaccSgw25295 (void) fprintf(fp, " YES ");
45466e2aaccSgw25295 else
45566e2aaccSgw25295 (void) fprintf(fp, " NO ");
45666e2aaccSgw25295
45766e2aaccSgw25295 if (zfs_prop_values(prop) == NULL)
45866e2aaccSgw25295 (void) fprintf(fp, "-\n");
45966e2aaccSgw25295 else
46066e2aaccSgw25295 (void) fprintf(fp, "%s\n", zfs_prop_values(prop));
46166e2aaccSgw25295
462990b4856Slling return (ZPROP_CONT);
46366e2aaccSgw25295 }
46466e2aaccSgw25295
46566e2aaccSgw25295 /*
466fa9e4066Sahrens * Display usage message. If we're inside a command, display only the usage for
467fa9e4066Sahrens * that command. Otherwise, iterate over the entire command table and display
468fa9e4066Sahrens * a complete usage message.
469fa9e4066Sahrens */
470fa9e4066Sahrens static void
usage(boolean_t requested)47199653d4eSeschrock usage(boolean_t requested)
472fa9e4066Sahrens {
473fa9e4066Sahrens int i;
47499653d4eSeschrock boolean_t show_properties = B_FALSE;
475fa9e4066Sahrens FILE *fp = requested ? stdout : stderr;
476fa9e4066Sahrens
477fa9e4066Sahrens if (current_command == NULL) {
478fa9e4066Sahrens
479fa9e4066Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
480fa9e4066Sahrens (void) fprintf(fp,
481fa9e4066Sahrens gettext("where 'command' is one of the following:\n\n"));
482fa9e4066Sahrens
483fa9e4066Sahrens for (i = 0; i < NCOMMAND; i++) {
484fa9e4066Sahrens if (command_table[i].name == NULL)
485fa9e4066Sahrens (void) fprintf(fp, "\n");
486fa9e4066Sahrens else
487fa9e4066Sahrens (void) fprintf(fp, "%s",
48865cd9f28Seschrock get_usage(command_table[i].usage));
489fa9e4066Sahrens }
490fa9e4066Sahrens
491fa9e4066Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: "
492fa9e4066Sahrens "pool/[dataset/]*dataset[@name]\n"));
493fa9e4066Sahrens } else {
494fa9e4066Sahrens (void) fprintf(fp, gettext("usage:\n"));
49565cd9f28Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage));
496fa9e4066Sahrens }
497fa9e4066Sahrens
4985c21526aSdarrenm if (current_command != NULL &&
4995c21526aSdarrenm (strcmp(current_command->name, "set") == 0 ||
500fa9e4066Sahrens strcmp(current_command->name, "get") == 0 ||
501fa9e4066Sahrens strcmp(current_command->name, "inherit") == 0 ||
5025c21526aSdarrenm strcmp(current_command->name, "list") == 0))
50399653d4eSeschrock show_properties = B_TRUE;
504fa9e4066Sahrens
505fa9e4066Sahrens if (show_properties) {
506fa9e4066Sahrens (void) fprintf(fp,
507fa9e4066Sahrens gettext("\nThe following properties are supported:\n"));
508fa9e4066Sahrens
509a9799022Sck153898 (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n",
510fa9e4066Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES");
511fa9e4066Sahrens
51266e2aaccSgw25295 /* Iterate over all properties */
513990b4856Slling (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
514990b4856Slling ZFS_TYPE_DATASET);
515fa9e4066Sahrens
51614843421SMatthew Ahrens (void) fprintf(fp, "\t%-15s ", "userused@...");
51714843421SMatthew Ahrens (void) fprintf(fp, " NO NO <size>\n");
51814843421SMatthew Ahrens (void) fprintf(fp, "\t%-15s ", "groupused@...");
51914843421SMatthew Ahrens (void) fprintf(fp, " NO NO <size>\n");
520f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "projectused@...");
521f67950b2SNasf-Fan (void) fprintf(fp, " NO NO <size>\n");
522f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "userobjused@...");
523f67950b2SNasf-Fan (void) fprintf(fp, " NO NO <size>\n");
524f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "groupobjused@...");
525f67950b2SNasf-Fan (void) fprintf(fp, " NO NO <size>\n");
526f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "projectobjused@...");
527f67950b2SNasf-Fan (void) fprintf(fp, " NO NO <size>\n");
52814843421SMatthew Ahrens (void) fprintf(fp, "\t%-15s ", "userquota@...");
52914843421SMatthew Ahrens (void) fprintf(fp, "YES NO <size> | none\n");
53014843421SMatthew Ahrens (void) fprintf(fp, "\t%-15s ", "groupquota@...");
53114843421SMatthew Ahrens (void) fprintf(fp, "YES NO <size> | none\n");
532f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "projectquota@...");
533f67950b2SNasf-Fan (void) fprintf(fp, "YES NO <size> | none\n");
534f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "userobjquota@...");
535f67950b2SNasf-Fan (void) fprintf(fp, "YES NO <size> | none\n");
536f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "groupobjquota@...");
537f67950b2SNasf-Fan (void) fprintf(fp, "YES NO <size> | none\n");
538f67950b2SNasf-Fan (void) fprintf(fp, "\t%-15s ", "projectobjquota@...");
539f67950b2SNasf-Fan (void) fprintf(fp, "YES NO <size> | none\n");
54019b94df9SMatthew Ahrens (void) fprintf(fp, "\t%-15s ", "written@<snap>");
54119b94df9SMatthew Ahrens (void) fprintf(fp, " NO NO <size>\n");
54214843421SMatthew Ahrens
543fa9e4066Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes "
544fa9e4066Sahrens "with standard units such as K, M, G, etc.\n"));
54574e7dc98SMatthew Ahrens (void) fprintf(fp, gettext("\nUser-defined properties can "
546e9dbad6fSeschrock "be specified by using a name containing a colon (:).\n"));
547f67950b2SNasf-Fan (void) fprintf(fp, gettext("\nThe {user|group|project}"
548f67950b2SNasf-Fan "[obj]{used|quota}@ properties must be appended with\n"
549f67950b2SNasf-Fan "a user|group|project specifier of one of these forms:\n"
55014843421SMatthew Ahrens " POSIX name (eg: \"matt\")\n"
55114843421SMatthew Ahrens " POSIX id (eg: \"126829\")\n"
55214843421SMatthew Ahrens " SMB name@domain (eg: \"matt@sun\")\n"
55314843421SMatthew Ahrens " SMB SID (eg: \"S-1-234-567-89\")\n"));
5545c21526aSdarrenm } else {
5555c21526aSdarrenm (void) fprintf(fp,
556deb8317bSMark J Musante gettext("\nFor the property list, run: %s\n"),
557deb8317bSMark J Musante "zfs set|get");
5586949a980Smarks (void) fprintf(fp,
559deb8317bSMark J Musante gettext("\nFor the delegated permission list, run: %s\n"),
560deb8317bSMark J Musante "zfs allow|unallow");
561fa9e4066Sahrens }
562fa9e4066Sahrens
563e9dbad6fSeschrock /*
564e9dbad6fSeschrock * See comments at end of main().
565e9dbad6fSeschrock */
566e9dbad6fSeschrock if (getenv("ZFS_ABORT") != NULL) {
567e9dbad6fSeschrock (void) printf("dumping core by request\n");
568e9dbad6fSeschrock abort();
569e9dbad6fSeschrock }
570e9dbad6fSeschrock
571fa9e4066Sahrens exit(requested ? 0 : 2);
572fa9e4066Sahrens }
573fa9e4066Sahrens
57430925561SChris Williamson /*
57530925561SChris Williamson * Take a property=value argument string and add it to the given nvlist.
57630925561SChris Williamson * Modifies the argument inplace.
57730925561SChris Williamson */
5786ccda740Sloli10K static boolean_t
parseprop(nvlist_t * props,char * propname)57930925561SChris Williamson parseprop(nvlist_t *props, char *propname)
580bb0ade09Sahrens {
5816ccda740Sloli10K char *propval;
582bb0ade09Sahrens
583bb0ade09Sahrens if ((propval = strchr(propname, '=')) == NULL) {
584bb0ade09Sahrens (void) fprintf(stderr, gettext("missing "
58530925561SChris Williamson "'=' for property=value argument\n"));
5866ccda740Sloli10K return (B_FALSE);
587bb0ade09Sahrens }
588bb0ade09Sahrens *propval = '\0';
589bb0ade09Sahrens propval++;
5906ccda740Sloli10K if (nvlist_exists(props, propname)) {
591bb0ade09Sahrens (void) fprintf(stderr, gettext("property '%s' "
592bb0ade09Sahrens "specified multiple times\n"), propname);
5936ccda740Sloli10K return (B_FALSE);
594bb0ade09Sahrens }
595a8b6ddafSMark J Musante if (nvlist_add_string(props, propname, propval) != 0)
596a8b6ddafSMark J Musante nomem();
5976ccda740Sloli10K return (B_TRUE);
5986ccda740Sloli10K }
5996ccda740Sloli10K
6006ccda740Sloli10K /*
6016ccda740Sloli10K * Take a property name argument and add it to the given nvlist.
6026ccda740Sloli10K * Modifies the argument inplace.
6036ccda740Sloli10K */
6046ccda740Sloli10K static boolean_t
parsepropname(nvlist_t * props,char * propname)6056ccda740Sloli10K parsepropname(nvlist_t *props, char *propname)
6066ccda740Sloli10K {
6076ccda740Sloli10K if (strchr(propname, '=') != NULL) {
6086ccda740Sloli10K (void) fprintf(stderr, gettext("invalid character "
6096ccda740Sloli10K "'=' in property argument\n"));
6106ccda740Sloli10K return (B_FALSE);
6116ccda740Sloli10K }
6126ccda740Sloli10K if (nvlist_exists(props, propname)) {
6136ccda740Sloli10K (void) fprintf(stderr, gettext("property '%s' "
6146ccda740Sloli10K "specified multiple times\n"), propname);
6156ccda740Sloli10K return (B_FALSE);
6166ccda740Sloli10K }
6176ccda740Sloli10K if (nvlist_add_boolean(props, propname) != 0)
6186ccda740Sloli10K nomem();
6196ccda740Sloli10K return (B_TRUE);
620bb0ade09Sahrens }
621bb0ade09Sahrens
622ae1726b6SChris Gerhard static int
parse_depth(char * opt,int * flags)623ae1726b6SChris Gerhard parse_depth(char *opt, int *flags)
624ae1726b6SChris Gerhard {
625ae1726b6SChris Gerhard char *tmp;
626ae1726b6SChris Gerhard int depth;
627ae1726b6SChris Gerhard
628ae1726b6SChris Gerhard depth = (int)strtol(opt, &tmp, 0);
629ae1726b6SChris Gerhard if (*tmp) {
630ae1726b6SChris Gerhard (void) fprintf(stderr,
631ae1726b6SChris Gerhard gettext("%s is not an integer\n"), optarg);
632ae1726b6SChris Gerhard usage(B_FALSE);
633ae1726b6SChris Gerhard }
634ae1726b6SChris Gerhard if (depth < 0) {
635ae1726b6SChris Gerhard (void) fprintf(stderr,
636ae1726b6SChris Gerhard gettext("Depth can not be negative.\n"));
637ae1726b6SChris Gerhard usage(B_FALSE);
638ae1726b6SChris Gerhard }
639ae1726b6SChris Gerhard *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
640ae1726b6SChris Gerhard return (depth);
641ae1726b6SChris Gerhard }
642ae1726b6SChris Gerhard
643a8b6ddafSMark J Musante #define PROGRESS_DELAY 2 /* seconds */
644a8b6ddafSMark J Musante
645a8b6ddafSMark J Musante 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";
646a8b6ddafSMark J Musante static time_t pt_begin;
647a8b6ddafSMark J Musante static char *pt_header = NULL;
648a8b6ddafSMark J Musante static boolean_t pt_shown;
649a8b6ddafSMark J Musante
650a8b6ddafSMark J Musante static void
start_progress_timer(void)651a8b6ddafSMark J Musante start_progress_timer(void)
652a8b6ddafSMark J Musante {
653a8b6ddafSMark J Musante pt_begin = time(NULL) + PROGRESS_DELAY;
654a8b6ddafSMark J Musante pt_shown = B_FALSE;
655a8b6ddafSMark J Musante }
656a8b6ddafSMark J Musante
657a8b6ddafSMark J Musante static void
set_progress_header(char * header)658a8b6ddafSMark J Musante set_progress_header(char *header)
659a8b6ddafSMark J Musante {
660a8b6ddafSMark J Musante assert(pt_header == NULL);
661a8b6ddafSMark J Musante pt_header = safe_strdup(header);
662a8b6ddafSMark J Musante if (pt_shown) {
663a8b6ddafSMark J Musante (void) printf("%s: ", header);
664a8b6ddafSMark J Musante (void) fflush(stdout);
665a8b6ddafSMark J Musante }
666a8b6ddafSMark J Musante }
667a8b6ddafSMark J Musante
668a8b6ddafSMark J Musante static void
update_progress(char * update)669a8b6ddafSMark J Musante update_progress(char *update)
670a8b6ddafSMark J Musante {
671a8b6ddafSMark J Musante if (!pt_shown && time(NULL) > pt_begin) {
672a8b6ddafSMark J Musante int len = strlen(update);
673a8b6ddafSMark J Musante
674a8b6ddafSMark J Musante (void) printf("%s: %s%*.*s", pt_header, update, len, len,
675a8b6ddafSMark J Musante pt_reverse);
676a8b6ddafSMark J Musante (void) fflush(stdout);
677a8b6ddafSMark J Musante pt_shown = B_TRUE;
678a8b6ddafSMark J Musante } else if (pt_shown) {
679a8b6ddafSMark J Musante int len = strlen(update);
680a8b6ddafSMark J Musante
681a8b6ddafSMark J Musante (void) printf("%s%*.*s", update, len, len, pt_reverse);
682a8b6ddafSMark J Musante (void) fflush(stdout);
683a8b6ddafSMark J Musante }
684a8b6ddafSMark J Musante }
685a8b6ddafSMark J Musante
686a8b6ddafSMark J Musante static void
finish_progress(char * done)687a8b6ddafSMark J Musante finish_progress(char *done)
688a8b6ddafSMark J Musante {
689a8b6ddafSMark J Musante if (pt_shown) {
690a8b6ddafSMark J Musante (void) printf("%s\n", done);
691a8b6ddafSMark J Musante (void) fflush(stdout);
692a8b6ddafSMark J Musante }
693a8b6ddafSMark J Musante free(pt_header);
694a8b6ddafSMark J Musante pt_header = NULL;
695a8b6ddafSMark J Musante }
6965d7b4d43SMatthew Ahrens
697f5a3d331SBrian Behlendorf static int
zfs_mount_and_share(libzfs_handle_t * hdl,const char * dataset,zfs_type_t type)698f5a3d331SBrian Behlendorf zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
699780828c8SAndriy Gapon {
700f5a3d331SBrian Behlendorf zfs_handle_t *zhp = NULL;
701f5a3d331SBrian Behlendorf int ret = 0;
702f5a3d331SBrian Behlendorf
703f5a3d331SBrian Behlendorf zhp = zfs_open(hdl, dataset, type);
704f5a3d331SBrian Behlendorf if (zhp == NULL)
705f5a3d331SBrian Behlendorf return (1);
706f5a3d331SBrian Behlendorf
707f5a3d331SBrian Behlendorf /*
708f5a3d331SBrian Behlendorf * Volumes may neither be mounted or shared. Potentially in the
709f5a3d331SBrian Behlendorf * future filesystems detected on these volumes could be mounted.
710f5a3d331SBrian Behlendorf */
711f5a3d331SBrian Behlendorf if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
712f5a3d331SBrian Behlendorf zfs_close(zhp);
713f5a3d331SBrian Behlendorf return (0);
714f5a3d331SBrian Behlendorf }
715f5a3d331SBrian Behlendorf
716f5a3d331SBrian Behlendorf /*
717f5a3d331SBrian Behlendorf * Mount and/or share the new filesystem as appropriate. We provide a
718f5a3d331SBrian Behlendorf * verbose error message to let the user know that their filesystem was
719f5a3d331SBrian Behlendorf * in fact created, even if we failed to mount or share it.
720f5a3d331SBrian Behlendorf *
721f5a3d331SBrian Behlendorf * If the user doesn't want the dataset automatically mounted, then
722f5a3d331SBrian Behlendorf * skip the mount/share step
723f5a3d331SBrian Behlendorf */
724f5a3d331SBrian Behlendorf if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&
725f5a3d331SBrian Behlendorf zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {
726f5a3d331SBrian Behlendorf if (zfs_mount(zhp, NULL, 0) != 0) {
727f5a3d331SBrian Behlendorf (void) fprintf(stderr, gettext("filesystem "
728f5a3d331SBrian Behlendorf "successfully created, but not mounted\n"));
729f5a3d331SBrian Behlendorf ret = 1;
730f5a3d331SBrian Behlendorf } else if (zfs_share(zhp) != 0) {
731f5a3d331SBrian Behlendorf (void) fprintf(stderr, gettext("filesystem "
732f5a3d331SBrian Behlendorf "successfully created, but not shared\n"));
733f5a3d331SBrian Behlendorf ret = 1;
734f5a3d331SBrian Behlendorf }
735f5a3d331SBrian Behlendorf }
736f5a3d331SBrian Behlendorf
737f5a3d331SBrian Behlendorf zfs_close(zhp);
738f5a3d331SBrian Behlendorf
739f5a3d331SBrian Behlendorf return (ret);
740780828c8SAndriy Gapon }
741780828c8SAndriy Gapon
742780828c8SAndriy Gapon /*
743bb0ade09Sahrens * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
744fa9e4066Sahrens *
745fa9e4066Sahrens * Given an existing dataset, create a writable copy whose initial contents
746fa9e4066Sahrens * are the same as the source. The newly created dataset maintains a
747fa9e4066Sahrens * dependency on the original; the original cannot be destroyed so long as
748fa9e4066Sahrens * the clone exists.
7497f1f55eaSvb160487 *
7507f1f55eaSvb160487 * The '-p' flag creates all the non-existing ancestors of the target first.
751fa9e4066Sahrens */
752fa9e4066Sahrens static int
zfs_do_clone(int argc,char ** argv)753fa9e4066Sahrens zfs_do_clone(int argc, char **argv)
754fa9e4066Sahrens {
755bb0ade09Sahrens zfs_handle_t *zhp = NULL;
7567f1f55eaSvb160487 boolean_t parents = B_FALSE;
757bb0ade09Sahrens nvlist_t *props;
75805c998d6SRichard Lowe int ret = 0;
7597f1f55eaSvb160487 int c;
760fa9e4066Sahrens
761a8b6ddafSMark J Musante if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
762a8b6ddafSMark J Musante nomem();
763bb0ade09Sahrens
764fa9e4066Sahrens /* check options */
765bb0ade09Sahrens while ((c = getopt(argc, argv, "o:p")) != -1) {
7667f1f55eaSvb160487 switch (c) {
767bb0ade09Sahrens case 'o':
7686ccda740Sloli10K if (!parseprop(props, optarg)) {
7696ccda740Sloli10K nvlist_free(props);
770bb0ade09Sahrens return (1);
7716ccda740Sloli10K }
772bb0ade09Sahrens break;
7737f1f55eaSvb160487 case 'p':
7747f1f55eaSvb160487 parents = B_TRUE;
7757f1f55eaSvb160487 break;
7767f1f55eaSvb160487 case '?':
777fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
7787f1f55eaSvb160487 optopt);
779bb0ade09Sahrens goto usage;
780fa9e4066Sahrens }
7817f1f55eaSvb160487 }
7827f1f55eaSvb160487
7837f1f55eaSvb160487 argc -= optind;
7847f1f55eaSvb160487 argv += optind;
785fa9e4066Sahrens
786fa9e4066Sahrens /* check number of arguments */
7877f1f55eaSvb160487 if (argc < 1) {
788fa9e4066Sahrens (void) fprintf(stderr, gettext("missing source dataset "
789fa9e4066Sahrens "argument\n"));
790bb0ade09Sahrens goto usage;
791fa9e4066Sahrens }
7927f1f55eaSvb160487 if (argc < 2) {
793fa9e4066Sahrens (void) fprintf(stderr, gettext("missing target dataset "
794fa9e4066Sahrens "argument\n"));
795bb0ade09Sahrens goto usage;
796fa9e4066Sahrens }
7977f1f55eaSvb160487 if (argc > 2) {
798fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
799bb0ade09Sahrens goto usage;
800fa9e4066Sahrens }
801fa9e4066Sahrens
802fa9e4066Sahrens /* open the source dataset */
8037f1f55eaSvb160487 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
804fa9e4066Sahrens return (1);
805fa9e4066Sahrens
8067f1f55eaSvb160487 if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
8077f1f55eaSvb160487 ZFS_TYPE_VOLUME)) {
8087f1f55eaSvb160487 /*
8097f1f55eaSvb160487 * Now create the ancestors of the target dataset. If the
8107f1f55eaSvb160487 * target already exists and '-p' option was used we should not
8117f1f55eaSvb160487 * complain.
8127f1f55eaSvb160487 */
8137f1f55eaSvb160487 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
8147f1f55eaSvb160487 ZFS_TYPE_VOLUME))
8157f1f55eaSvb160487 return (0);
8167f1f55eaSvb160487 if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
8177f1f55eaSvb160487 return (1);
8187f1f55eaSvb160487 }
8197f1f55eaSvb160487
820fa9e4066Sahrens /* pass to libzfs */
821bb0ade09Sahrens ret = zfs_clone(zhp, argv[1], props);
822fa9e4066Sahrens
823fa9e4066Sahrens /* create the mountpoint if necessary */
824fa9e4066Sahrens if (ret == 0) {
825f5a3d331SBrian Behlendorf if (log_history) {
826f5a3d331SBrian Behlendorf (void) zpool_log_history(g_zfs, history_str);
827f5a3d331SBrian Behlendorf log_history = B_FALSE;
828f5a3d331SBrian Behlendorf }
829990b4856Slling
830f5a3d331SBrian Behlendorf ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
831fa9e4066Sahrens }
832fa9e4066Sahrens
833fa9e4066Sahrens zfs_close(zhp);
834bb0ade09Sahrens nvlist_free(props);
835fa9e4066Sahrens
836bb0ade09Sahrens return (!!ret);
837bb0ade09Sahrens
838bb0ade09Sahrens usage:
839bb0ade09Sahrens if (zhp)
840bb0ade09Sahrens zfs_close(zhp);
841bb0ade09Sahrens nvlist_free(props);
842bb0ade09Sahrens usage(B_FALSE);
843bb0ade09Sahrens return (-1);
844fa9e4066Sahrens }
845fa9e4066Sahrens
846fa9e4066Sahrens /*
8471320ddf5SMike Gerdts * zfs create [-Pnpv] [-o prop=value] ... fs
8481320ddf5SMike Gerdts * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
849fa9e4066Sahrens *
850fa9e4066Sahrens * Create a new dataset. This command can be used to create filesystems
851fa9e4066Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'.
852fa9e4066Sahrens * For volumes, the user must specify a size to be used.
853fa9e4066Sahrens *
854fa9e4066Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try
855fa9e4066Sahrens * to set the reservation for this volume. By default we set a reservation
856a9b821a0Sck153898 * equal to the size for any volume. For pools with SPA_VERSION >=
857a9b821a0Sck153898 * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
8587f1f55eaSvb160487 *
8597f1f55eaSvb160487 * The '-p' flag creates all the non-existing ancestors of the target first.
8601320ddf5SMike Gerdts *
8611320ddf5SMike Gerdts * The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity
8621320ddf5SMike Gerdts * check of arguments and properties, but does not check for permissions,
8631320ddf5SMike Gerdts * available space, etc.
8641320ddf5SMike Gerdts *
8651320ddf5SMike Gerdts * The '-v' flag is for verbose output.
8661320ddf5SMike Gerdts *
8671320ddf5SMike Gerdts * The '-P' flag is used for parseable output. It implies '-v'.
868fa9e4066Sahrens */
869fa9e4066Sahrens static int
zfs_do_create(int argc,char ** argv)870fa9e4066Sahrens zfs_do_create(int argc, char **argv)
871fa9e4066Sahrens {
872fa9e4066Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM;
8731320ddf5SMike Gerdts zpool_handle_t *zpool_handle = NULL;
8741320ddf5SMike Gerdts nvlist_t *real_props = NULL;
875c16bcc45SIgor Kozhukhov uint64_t volsize = 0;
876fa9e4066Sahrens int c;
87799653d4eSeschrock boolean_t noreserve = B_FALSE;
8783cb34c60Sahrens boolean_t bflag = B_FALSE;
8797f1f55eaSvb160487 boolean_t parents = B_FALSE;
8801320ddf5SMike Gerdts boolean_t dryrun = B_FALSE;
8811320ddf5SMike Gerdts boolean_t verbose = B_FALSE;
8821320ddf5SMike Gerdts boolean_t parseable = B_FALSE;
883e9dbad6fSeschrock int ret = 1;
884bb0ade09Sahrens nvlist_t *props;
885e9dbad6fSeschrock uint64_t intval;
886e9dbad6fSeschrock
887a8b6ddafSMark J Musante if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
888a8b6ddafSMark J Musante nomem();
889fa9e4066Sahrens
890fa9e4066Sahrens /* check options */
8911320ddf5SMike Gerdts while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
892fa9e4066Sahrens switch (c) {
893fa9e4066Sahrens case 'V':
894fa9e4066Sahrens type = ZFS_TYPE_VOLUME;
895e9dbad6fSeschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
896e9dbad6fSeschrock (void) fprintf(stderr, gettext("bad volume "
897e9dbad6fSeschrock "size '%s': %s\n"), optarg,
898e9dbad6fSeschrock libzfs_error_description(g_zfs));
899e9dbad6fSeschrock goto error;
900e9dbad6fSeschrock }
901e9dbad6fSeschrock
902e9dbad6fSeschrock if (nvlist_add_uint64(props,
903a8b6ddafSMark J Musante zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
904a8b6ddafSMark J Musante nomem();
905e9dbad6fSeschrock volsize = intval;
906fa9e4066Sahrens break;
9071320ddf5SMike Gerdts case 'P':
9081320ddf5SMike Gerdts verbose = B_TRUE;
9091320ddf5SMike Gerdts parseable = B_TRUE;
9101320ddf5SMike Gerdts break;
9117f1f55eaSvb160487 case 'p':
9127f1f55eaSvb160487 parents = B_TRUE;
9137f1f55eaSvb160487 break;
914fa9e4066Sahrens case 'b':
9153cb34c60Sahrens bflag = B_TRUE;
916e9dbad6fSeschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
917e9dbad6fSeschrock (void) fprintf(stderr, gettext("bad volume "
918e9dbad6fSeschrock "block size '%s': %s\n"), optarg,
919e9dbad6fSeschrock libzfs_error_description(g_zfs));
920e9dbad6fSeschrock goto error;
921e9dbad6fSeschrock }
922e9dbad6fSeschrock
923e9dbad6fSeschrock if (nvlist_add_uint64(props,
924e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
925a8b6ddafSMark J Musante intval) != 0)
926a8b6ddafSMark J Musante nomem();
927e9dbad6fSeschrock break;
9281320ddf5SMike Gerdts case 'n':
9291320ddf5SMike Gerdts dryrun = B_TRUE;
9301320ddf5SMike Gerdts break;
931e9dbad6fSeschrock case 'o':
9326ccda740Sloli10K if (!parseprop(props, optarg))
933e9dbad6fSeschrock goto error;
934fa9e4066Sahrens break;
935fa9e4066Sahrens case 's':
93699653d4eSeschrock noreserve = B_TRUE;
937fa9e4066Sahrens break;
9381320ddf5SMike Gerdts case 'v':
9391320ddf5SMike Gerdts verbose = B_TRUE;
9401320ddf5SMike Gerdts break;
941fa9e4066Sahrens case ':':
942fa9e4066Sahrens (void) fprintf(stderr, gettext("missing size "
943fa9e4066Sahrens "argument\n"));
944e9dbad6fSeschrock goto badusage;
945fa9e4066Sahrens case '?':
946fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
947fa9e4066Sahrens optopt);
948e9dbad6fSeschrock goto badusage;
949fa9e4066Sahrens }
950fa9e4066Sahrens }
951fa9e4066Sahrens
9523cb34c60Sahrens if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
9533cb34c60Sahrens (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
9543cb34c60Sahrens "used when creating a volume\n"));
955e9dbad6fSeschrock goto badusage;
956fa9e4066Sahrens }
957fa9e4066Sahrens
958fa9e4066Sahrens argc -= optind;
959fa9e4066Sahrens argv += optind;
960fa9e4066Sahrens
961fa9e4066Sahrens /* check number of arguments */
962fa9e4066Sahrens if (argc == 0) {
963fa9e4066Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"),
964fa9e4066Sahrens zfs_type_to_name(type));
965e9dbad6fSeschrock goto badusage;
966fa9e4066Sahrens }
967fa9e4066Sahrens if (argc > 1) {
968fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
969e9dbad6fSeschrock goto badusage;
970e9dbad6fSeschrock }
971e9dbad6fSeschrock
9721320ddf5SMike Gerdts if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
9731320ddf5SMike Gerdts char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
974a9b821a0Sck153898 char *p;
975a9b821a0Sck153898
976c16bcc45SIgor Kozhukhov if ((p = strchr(argv[0], '/')) != NULL)
977a9b821a0Sck153898 *p = '\0';
978a9b821a0Sck153898 zpool_handle = zpool_open(g_zfs, argv[0]);
979a9b821a0Sck153898 if (p != NULL)
980a9b821a0Sck153898 *p = '/';
981a9b821a0Sck153898 if (zpool_handle == NULL)
982a9b821a0Sck153898 goto error;
9831320ddf5SMike Gerdts
9841320ddf5SMike Gerdts (void) snprintf(msg, sizeof (msg),
9851320ddf5SMike Gerdts dryrun ? gettext("cannot verify '%s'") :
9861320ddf5SMike Gerdts gettext("cannot create '%s'"), argv[0]);
9871320ddf5SMike Gerdts if (props && (real_props = zfs_valid_proplist(g_zfs, type,
9881320ddf5SMike Gerdts props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
9891320ddf5SMike Gerdts zpool_close(zpool_handle);
9901320ddf5SMike Gerdts goto error;
9911320ddf5SMike Gerdts }
9921320ddf5SMike Gerdts }
9931320ddf5SMike Gerdts
9943cf6ec9cSalex /*
9953cf6ec9cSalex * if volsize is not a multiple of volblocksize, round it up to the
9963cf6ec9cSalex * nearest multiple of the volblocksize
9973cf6ec9cSalex */
9983cf6ec9cSalex if (type == ZFS_TYPE_VOLUME) {
9993cf6ec9cSalex uint64_t volblocksize;
10003cf6ec9cSalex
10013cf6ec9cSalex if (nvlist_lookup_uint64(props,
10023cf6ec9cSalex zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
10033cf6ec9cSalex &volblocksize) != 0)
10043cf6ec9cSalex volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
10053cf6ec9cSalex
10063cf6ec9cSalex if (volsize % volblocksize) {
10073cf6ec9cSalex volsize = P2ROUNDUP_TYPED(volsize, volblocksize,
10083cf6ec9cSalex uint64_t);
10093cf6ec9cSalex
10103cf6ec9cSalex if (nvlist_add_uint64(props,
10113cf6ec9cSalex zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {
10123cf6ec9cSalex nvlist_free(props);
10133cf6ec9cSalex nomem();
10143cf6ec9cSalex }
10153cf6ec9cSalex }
10163cf6ec9cSalex }
10173cf6ec9cSalex
10183cf6ec9cSalex
10191320ddf5SMike Gerdts if (type == ZFS_TYPE_VOLUME && !noreserve) {
10201320ddf5SMike Gerdts uint64_t spa_version;
10211320ddf5SMike Gerdts zfs_prop_t resv_prop;
10221320ddf5SMike Gerdts char *strval;
10231320ddf5SMike Gerdts
1024a9b821a0Sck153898 spa_version = zpool_get_prop_int(zpool_handle,
1025a9b821a0Sck153898 ZPOOL_PROP_VERSION, NULL);
1026a9b821a0Sck153898 if (spa_version >= SPA_VERSION_REFRESERVATION)
1027a9b821a0Sck153898 resv_prop = ZFS_PROP_REFRESERVATION;
1028a9b821a0Sck153898 else
1029a9b821a0Sck153898 resv_prop = ZFS_PROP_RESERVATION;
1030c61ea566SGeorge Wilson
1031b73ccab0SMike Gerdts volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
1032b73ccab0SMike Gerdts real_props);
1033a9b821a0Sck153898
1034a9b821a0Sck153898 if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
1035e9dbad6fSeschrock &strval) != 0) {
1036e9dbad6fSeschrock if (nvlist_add_uint64(props,
1037a9b821a0Sck153898 zfs_prop_to_name(resv_prop), volsize) != 0) {
1038e9dbad6fSeschrock nvlist_free(props);
1039a8b6ddafSMark J Musante nomem();
1040e9dbad6fSeschrock }
1041fa9e4066Sahrens }
1042a9b821a0Sck153898 }
10431320ddf5SMike Gerdts if (zpool_handle != NULL) {
10441320ddf5SMike Gerdts zpool_close(zpool_handle);
10451320ddf5SMike Gerdts nvlist_free(real_props);
10461320ddf5SMike Gerdts }
1047fa9e4066Sahrens
10487f1f55eaSvb160487 if (parents && zfs_name_valid(argv[0], type)) {
10497f1f55eaSvb160487 /*
10507f1f55eaSvb160487 * Now create the ancestors of target dataset. If the target
10517f1f55eaSvb160487 * already exists and '-p' option was used we should not
10527f1f55eaSvb160487 * complain.
10537f1f55eaSvb160487 */
10547f1f55eaSvb160487 if (zfs_dataset_exists(g_zfs, argv[0], type)) {
10557f1f55eaSvb160487 ret = 0;
10567f1f55eaSvb160487 goto error;
10577f1f55eaSvb160487 }
10581320ddf5SMike Gerdts if (verbose) {
10591320ddf5SMike Gerdts (void) printf(parseable ? "create_ancestors\t%s\n" :
10601320ddf5SMike Gerdts dryrun ? "would create ancestors of %s\n" :
10611320ddf5SMike Gerdts "create ancestors of %s\n", argv[0]);
10621320ddf5SMike Gerdts }
10631320ddf5SMike Gerdts if (!dryrun) {
10641320ddf5SMike Gerdts if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
10651320ddf5SMike Gerdts goto error;
10661320ddf5SMike Gerdts }
10671320ddf5SMike Gerdts }
10681320ddf5SMike Gerdts }
10691320ddf5SMike Gerdts
10701320ddf5SMike Gerdts if (verbose) {
10711320ddf5SMike Gerdts nvpair_t *nvp = NULL;
10721320ddf5SMike Gerdts (void) printf(parseable ? "create\t%s\n" :
10731320ddf5SMike Gerdts dryrun ? "would create %s\n" : "create %s\n", argv[0]);
10741320ddf5SMike Gerdts while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
10751320ddf5SMike Gerdts uint64_t uval;
10761320ddf5SMike Gerdts char *sval;
10771320ddf5SMike Gerdts
10781320ddf5SMike Gerdts switch (nvpair_type(nvp)) {
10791320ddf5SMike Gerdts case DATA_TYPE_UINT64:
10801320ddf5SMike Gerdts VERIFY0(nvpair_value_uint64(nvp, &uval));
10811320ddf5SMike Gerdts (void) printf(parseable ?
10821320ddf5SMike Gerdts "property\t%s\t%llu\n" : "\t%s=%llu\n",
10831320ddf5SMike Gerdts nvpair_name(nvp), (u_longlong_t)uval);
10841320ddf5SMike Gerdts break;
10851320ddf5SMike Gerdts case DATA_TYPE_STRING:
10861320ddf5SMike Gerdts VERIFY0(nvpair_value_string(nvp, &sval));
10871320ddf5SMike Gerdts (void) printf(parseable ?
10881320ddf5SMike Gerdts "property\t%s\t%s\n" : "\t%s=%s\n",
10891320ddf5SMike Gerdts nvpair_name(nvp), sval);
10901320ddf5SMike Gerdts break;
10911320ddf5SMike Gerdts default:
10921320ddf5SMike Gerdts (void) fprintf(stderr, "property '%s' "
10931320ddf5SMike Gerdts "has illegal type %d\n",
10941320ddf5SMike Gerdts nvpair_name(nvp), nvpair_type(nvp));
10951320ddf5SMike Gerdts abort();
10961320ddf5SMike Gerdts }
10971320ddf5SMike Gerdts }
10981320ddf5SMike Gerdts }
10991320ddf5SMike Gerdts if (dryrun) {
11001320ddf5SMike Gerdts ret = 0;
11017f1f55eaSvb160487 goto error;
11027f1f55eaSvb160487 }
11037f1f55eaSvb160487
1104fa9e4066Sahrens /* pass to libzfs */
1105e9dbad6fSeschrock if (zfs_create(g_zfs, argv[0], type, props) != 0)
1106e9dbad6fSeschrock goto error;
1107fa9e4066Sahrens
1108f5a3d331SBrian Behlendorf if (log_history) {
1109f5a3d331SBrian Behlendorf (void) zpool_log_history(g_zfs, history_str);
1110f5a3d331SBrian Behlendorf log_history = B_FALSE;
1111fa9e4066Sahrens }
1112fa9e4066Sahrens
1113f5a3d331SBrian Behlendorf ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
1114e9dbad6fSeschrock error:
1115e9dbad6fSeschrock nvlist_free(props);
1116fa9e4066Sahrens return (ret);
1117e9dbad6fSeschrock badusage:
1118e9dbad6fSeschrock nvlist_free(props);
1119e9dbad6fSeschrock usage(B_FALSE);
1120e9dbad6fSeschrock return (2);
1121fa9e4066Sahrens }
1122fa9e4066Sahrens
1123fa9e4066Sahrens /*
1124922d9a97SChris Kirby * zfs destroy [-rRf] <fs, vol>
1125922d9a97SChris Kirby * zfs destroy [-rRd] <snap>
1126fa9e4066Sahrens *
1127fa9e4066Sahrens * -r Recursively destroy all children
1128fa9e4066Sahrens * -R Recursively destroy all dependents, including clones
1129fa9e4066Sahrens * -f Force unmounting of any dependents
1130842727c2SChris Kirby * -d If we can't destroy now, mark for deferred destruction
1131fa9e4066Sahrens *
1132fa9e4066Sahrens * Destroys the given dataset. By default, it will unmount any filesystems,
1133fa9e4066Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can
1134fa9e4066Sahrens * either be a child, or a clone of a child.
1135fa9e4066Sahrens */
1136fa9e4066Sahrens typedef struct destroy_cbdata {
113799653d4eSeschrock boolean_t cb_first;
113819b94df9SMatthew Ahrens boolean_t cb_force;
113919b94df9SMatthew Ahrens boolean_t cb_recurse;
114019b94df9SMatthew Ahrens boolean_t cb_error;
114119b94df9SMatthew Ahrens boolean_t cb_doclones;
1142fa9e4066Sahrens zfs_handle_t *cb_target;
1143842727c2SChris Kirby boolean_t cb_defer_destroy;
114419b94df9SMatthew Ahrens boolean_t cb_verbose;
114519b94df9SMatthew Ahrens boolean_t cb_parsable;
114619b94df9SMatthew Ahrens boolean_t cb_dryrun;
114719b94df9SMatthew Ahrens nvlist_t *cb_nvl;
11483b2aab18SMatthew Ahrens nvlist_t *cb_batchedsnaps;
114919b94df9SMatthew Ahrens
115019b94df9SMatthew Ahrens /* first snap in contiguous run */
11514445fffbSMatthew Ahrens char *cb_firstsnap;
115219b94df9SMatthew Ahrens /* previous snap in contiguous run */
11534445fffbSMatthew Ahrens char *cb_prevsnap;
115419b94df9SMatthew Ahrens int64_t cb_snapused;
115519b94df9SMatthew Ahrens char *cb_snapspec;
115678f17100SMatthew Ahrens char *cb_bookmark;
1157fa9e4066Sahrens } destroy_cbdata_t;
1158fa9e4066Sahrens
1159fa9e4066Sahrens /*
1160fa9e4066Sahrens * Check for any dependents based on the '-r' or '-R' flags.
1161fa9e4066Sahrens */
1162fa9e4066Sahrens static int
destroy_check_dependent(zfs_handle_t * zhp,void * data)1163fa9e4066Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data)
1164fa9e4066Sahrens {
1165fa9e4066Sahrens destroy_cbdata_t *cbp = data;
1166fa9e4066Sahrens const char *tname = zfs_get_name(cbp->cb_target);
1167fa9e4066Sahrens const char *name = zfs_get_name(zhp);
1168fa9e4066Sahrens
1169fa9e4066Sahrens if (strncmp(tname, name, strlen(tname)) == 0 &&
1170fa9e4066Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
1171fa9e4066Sahrens /*
1172fa9e4066Sahrens * This is a direct descendant, not a clone somewhere else in
1173fa9e4066Sahrens * the hierarchy.
1174fa9e4066Sahrens */
1175fa9e4066Sahrens if (cbp->cb_recurse)
1176fa9e4066Sahrens goto out;
1177fa9e4066Sahrens
1178fa9e4066Sahrens if (cbp->cb_first) {
1179fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': "
1180fa9e4066Sahrens "%s has children\n"),
1181fa9e4066Sahrens zfs_get_name(cbp->cb_target),
1182fa9e4066Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target)));
1183fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy "
1184fa9e4066Sahrens "the following datasets:\n"));
118599653d4eSeschrock cbp->cb_first = B_FALSE;
118619b94df9SMatthew Ahrens cbp->cb_error = B_TRUE;
1187fa9e4066Sahrens }
1188fa9e4066Sahrens
1189fa9e4066Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
1190fa9e4066Sahrens } else {
1191fa9e4066Sahrens /*
1192fa9e4066Sahrens * This is a clone. We only want to report this if the '-r'
1193fa9e4066Sahrens * wasn't specified, or the target is a snapshot.
1194fa9e4066Sahrens */
1195fa9e4066Sahrens if (!cbp->cb_recurse &&
1196fa9e4066Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
1197fa9e4066Sahrens goto out;
1198fa9e4066Sahrens
1199fa9e4066Sahrens if (cbp->cb_first) {
1200fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': "
1201fa9e4066Sahrens "%s has dependent clones\n"),
1202fa9e4066Sahrens zfs_get_name(cbp->cb_target),
1203fa9e4066Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target)));
1204fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy "
1205fa9e4066Sahrens "the following datasets:\n"));
120699653d4eSeschrock cbp->cb_first = B_FALSE;
120719b94df9SMatthew Ahrens cbp->cb_error = B_TRUE;
120819b94df9SMatthew Ahrens cbp->cb_dryrun = B_TRUE;
1209fa9e4066Sahrens }
1210fa9e4066Sahrens
1211fa9e4066Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
1212fa9e4066Sahrens }
1213fa9e4066Sahrens
1214fa9e4066Sahrens out:
1215fa9e4066Sahrens zfs_close(zhp);
1216fa9e4066Sahrens return (0);
1217fa9e4066Sahrens }
1218fa9e4066Sahrens
1219fa9e4066Sahrens static int
destroy_callback(zfs_handle_t * zhp,void * data)1220fa9e4066Sahrens destroy_callback(zfs_handle_t *zhp, void *data)
1221fa9e4066Sahrens {
122219b94df9SMatthew Ahrens destroy_cbdata_t *cb = data;
122319b94df9SMatthew Ahrens const char *name = zfs_get_name(zhp);
122419b94df9SMatthew Ahrens
122519b94df9SMatthew Ahrens if (cb->cb_verbose) {
122619b94df9SMatthew Ahrens if (cb->cb_parsable) {
122719b94df9SMatthew Ahrens (void) printf("destroy\t%s\n", name);
122819b94df9SMatthew Ahrens } else if (cb->cb_dryrun) {
122919b94df9SMatthew Ahrens (void) printf(gettext("would destroy %s\n"),
123019b94df9SMatthew Ahrens name);
123119b94df9SMatthew Ahrens } else {
123219b94df9SMatthew Ahrens (void) printf(gettext("will destroy %s\n"),
123319b94df9SMatthew Ahrens name);
123419b94df9SMatthew Ahrens }
123519b94df9SMatthew Ahrens }
1236fa9e4066Sahrens
1237fa9e4066Sahrens /*
1238fa9e4066Sahrens * Ignore pools (which we've already flagged as an error before getting
1239681d9761SEric Taylor * here).
1240fa9e4066Sahrens */
1241fa9e4066Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL &&
1242fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1243fa9e4066Sahrens zfs_close(zhp);
1244fa9e4066Sahrens return (0);
1245fa9e4066Sahrens }
12463b2aab18SMatthew Ahrens if (cb->cb_dryrun) {
12473b2aab18SMatthew Ahrens zfs_close(zhp);
12483b2aab18SMatthew Ahrens return (0);
12493b2aab18SMatthew Ahrens }
1250fa9e4066Sahrens
12513b2aab18SMatthew Ahrens /*
12523b2aab18SMatthew Ahrens * We batch up all contiguous snapshots (even of different
12533b2aab18SMatthew Ahrens * filesystems) and destroy them with one ioctl. We can't
12543b2aab18SMatthew Ahrens * simply do all snap deletions and then all fs deletions,
12553b2aab18SMatthew Ahrens * because we must delete a clone before its origin.
12563b2aab18SMatthew Ahrens */
12573b2aab18SMatthew Ahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
12583b2aab18SMatthew Ahrens fnvlist_add_boolean(cb->cb_batchedsnaps, name);
12593b2aab18SMatthew Ahrens } else {
12603b2aab18SMatthew Ahrens int error = zfs_destroy_snaps_nvl(g_zfs,
12613b2aab18SMatthew Ahrens cb->cb_batchedsnaps, B_FALSE);
12623b2aab18SMatthew Ahrens fnvlist_free(cb->cb_batchedsnaps);
12633b2aab18SMatthew Ahrens cb->cb_batchedsnaps = fnvlist_alloc();
12643b2aab18SMatthew Ahrens
12653b2aab18SMatthew Ahrens if (error != 0 ||
12663b2aab18SMatthew Ahrens zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
126719b94df9SMatthew Ahrens zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
1268fa9e4066Sahrens zfs_close(zhp);
1269fa9e4066Sahrens return (-1);
1270fa9e4066Sahrens }
127119b94df9SMatthew Ahrens }
1272fa9e4066Sahrens
1273fa9e4066Sahrens zfs_close(zhp);
1274fa9e4066Sahrens return (0);
1275fa9e4066Sahrens }
1276fa9e4066Sahrens
12771d452cf5Sahrens static int
destroy_print_cb(zfs_handle_t * zhp,void * arg)127819b94df9SMatthew Ahrens destroy_print_cb(zfs_handle_t *zhp, void *arg)
12791d452cf5Sahrens {
128019b94df9SMatthew Ahrens destroy_cbdata_t *cb = arg;
128119b94df9SMatthew Ahrens const char *name = zfs_get_name(zhp);
128219b94df9SMatthew Ahrens int err = 0;
12831d452cf5Sahrens
128419b94df9SMatthew Ahrens if (nvlist_exists(cb->cb_nvl, name)) {
128519b94df9SMatthew Ahrens if (cb->cb_firstsnap == NULL)
12864445fffbSMatthew Ahrens cb->cb_firstsnap = strdup(name);
128719b94df9SMatthew Ahrens if (cb->cb_prevsnap != NULL)
12884445fffbSMatthew Ahrens free(cb->cb_prevsnap);
128919b94df9SMatthew Ahrens /* this snap continues the current range */
12904445fffbSMatthew Ahrens cb->cb_prevsnap = strdup(name);
12914445fffbSMatthew Ahrens if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
12924445fffbSMatthew Ahrens nomem();
129319b94df9SMatthew Ahrens if (cb->cb_verbose) {
129419b94df9SMatthew Ahrens if (cb->cb_parsable) {
129519b94df9SMatthew Ahrens (void) printf("destroy\t%s\n", name);
129619b94df9SMatthew Ahrens } else if (cb->cb_dryrun) {
129719b94df9SMatthew Ahrens (void) printf(gettext("would destroy %s\n"),
129819b94df9SMatthew Ahrens name);
129919b94df9SMatthew Ahrens } else {
130019b94df9SMatthew Ahrens (void) printf(gettext("will destroy %s\n"),
130119b94df9SMatthew Ahrens name);
130219b94df9SMatthew Ahrens }
130319b94df9SMatthew Ahrens }
130419b94df9SMatthew Ahrens } else if (cb->cb_firstsnap != NULL) {
130519b94df9SMatthew Ahrens /* end of this range */
130619b94df9SMatthew Ahrens uint64_t used = 0;
13074445fffbSMatthew Ahrens err = lzc_snaprange_space(cb->cb_firstsnap,
130819b94df9SMatthew Ahrens cb->cb_prevsnap, &used);
130919b94df9SMatthew Ahrens cb->cb_snapused += used;
13104445fffbSMatthew Ahrens free(cb->cb_firstsnap);
131119b94df9SMatthew Ahrens cb->cb_firstsnap = NULL;
13124445fffbSMatthew Ahrens free(cb->cb_prevsnap);
131319b94df9SMatthew Ahrens cb->cb_prevsnap = NULL;
131419b94df9SMatthew Ahrens }
131519b94df9SMatthew Ahrens zfs_close(zhp);
131619b94df9SMatthew Ahrens return (err);
131719b94df9SMatthew Ahrens }
13181d452cf5Sahrens
131919b94df9SMatthew Ahrens static int
destroy_print_snapshots(zfs_handle_t * fs_zhp,destroy_cbdata_t * cb)132019b94df9SMatthew Ahrens destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
132119b94df9SMatthew Ahrens {
132205c998d6SRichard Lowe int err = 0;
132319b94df9SMatthew Ahrens assert(cb->cb_firstsnap == NULL);
132419b94df9SMatthew Ahrens assert(cb->cb_prevsnap == NULL);
132519b94df9SMatthew Ahrens err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
132619b94df9SMatthew Ahrens if (cb->cb_firstsnap != NULL) {
132719b94df9SMatthew Ahrens uint64_t used = 0;
132819b94df9SMatthew Ahrens if (err == 0) {
13294445fffbSMatthew Ahrens err = lzc_snaprange_space(cb->cb_firstsnap,
133019b94df9SMatthew Ahrens cb->cb_prevsnap, &used);
133119b94df9SMatthew Ahrens }
133219b94df9SMatthew Ahrens cb->cb_snapused += used;
13334445fffbSMatthew Ahrens free(cb->cb_firstsnap);
133419b94df9SMatthew Ahrens cb->cb_firstsnap = NULL;
13354445fffbSMatthew Ahrens free(cb->cb_prevsnap);
133619b94df9SMatthew Ahrens cb->cb_prevsnap = NULL;
133719b94df9SMatthew Ahrens }
133819b94df9SMatthew Ahrens return (err);
133919b94df9SMatthew Ahrens }
134019b94df9SMatthew Ahrens
134119b94df9SMatthew Ahrens static int
snapshot_to_nvl_cb(zfs_handle_t * zhp,void * arg)134219b94df9SMatthew Ahrens snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
134319b94df9SMatthew Ahrens {
134419b94df9SMatthew Ahrens destroy_cbdata_t *cb = arg;
134519b94df9SMatthew Ahrens int err = 0;
134619b94df9SMatthew Ahrens
134719b94df9SMatthew Ahrens /* Check for clones. */
134865fec9f6SChristopher Siden if (!cb->cb_doclones && !cb->cb_defer_destroy) {
134919b94df9SMatthew Ahrens cb->cb_target = zhp;
135019b94df9SMatthew Ahrens cb->cb_first = B_TRUE;
135119b94df9SMatthew Ahrens err = zfs_iter_dependents(zhp, B_TRUE,
135219b94df9SMatthew Ahrens destroy_check_dependent, cb);
135319b94df9SMatthew Ahrens }
135419b94df9SMatthew Ahrens
135519b94df9SMatthew Ahrens if (err == 0) {
135619b94df9SMatthew Ahrens if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
135719b94df9SMatthew Ahrens nomem();
135819b94df9SMatthew Ahrens }
135919b94df9SMatthew Ahrens zfs_close(zhp);
136019b94df9SMatthew Ahrens return (err);
136119b94df9SMatthew Ahrens }
136219b94df9SMatthew Ahrens
136319b94df9SMatthew Ahrens static int
gather_snapshots(zfs_handle_t * zhp,void * arg)136419b94df9SMatthew Ahrens gather_snapshots(zfs_handle_t *zhp, void *arg)
136519b94df9SMatthew Ahrens {
136619b94df9SMatthew Ahrens destroy_cbdata_t *cb = arg;
136719b94df9SMatthew Ahrens int err = 0;
136819b94df9SMatthew Ahrens
136919b94df9SMatthew Ahrens err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
137019b94df9SMatthew Ahrens if (err == ENOENT)
137119b94df9SMatthew Ahrens err = 0;
137219b94df9SMatthew Ahrens if (err != 0)
137319b94df9SMatthew Ahrens goto out;
137419b94df9SMatthew Ahrens
137519b94df9SMatthew Ahrens if (cb->cb_verbose) {
137619b94df9SMatthew Ahrens err = destroy_print_snapshots(zhp, cb);
137719b94df9SMatthew Ahrens if (err != 0)
137819b94df9SMatthew Ahrens goto out;
137919b94df9SMatthew Ahrens }
138019b94df9SMatthew Ahrens
138119b94df9SMatthew Ahrens if (cb->cb_recurse)
138219b94df9SMatthew Ahrens err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
138319b94df9SMatthew Ahrens
138419b94df9SMatthew Ahrens out:
138519b94df9SMatthew Ahrens zfs_close(zhp);
138619b94df9SMatthew Ahrens return (err);
138719b94df9SMatthew Ahrens }
138819b94df9SMatthew Ahrens
138919b94df9SMatthew Ahrens static int
destroy_clones(destroy_cbdata_t * cb)139019b94df9SMatthew Ahrens destroy_clones(destroy_cbdata_t *cb)
139119b94df9SMatthew Ahrens {
139219b94df9SMatthew Ahrens nvpair_t *pair;
139319b94df9SMatthew Ahrens for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
139419b94df9SMatthew Ahrens pair != NULL;
139519b94df9SMatthew Ahrens pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
139619b94df9SMatthew Ahrens zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
139719b94df9SMatthew Ahrens ZFS_TYPE_SNAPSHOT);
139819b94df9SMatthew Ahrens if (zhp != NULL) {
139919b94df9SMatthew Ahrens boolean_t defer = cb->cb_defer_destroy;
140005c998d6SRichard Lowe int err = 0;
140119b94df9SMatthew Ahrens
14021d452cf5Sahrens /*
140319b94df9SMatthew Ahrens * We can't defer destroy non-snapshots, so set it to
140419b94df9SMatthew Ahrens * false while destroying the clones.
14051d452cf5Sahrens */
140619b94df9SMatthew Ahrens cb->cb_defer_destroy = B_FALSE;
140719b94df9SMatthew Ahrens err = zfs_iter_dependents(zhp, B_FALSE,
140819b94df9SMatthew Ahrens destroy_callback, cb);
140919b94df9SMatthew Ahrens cb->cb_defer_destroy = defer;
14103ccfa83cSahrens zfs_close(zhp);
141119b94df9SMatthew Ahrens if (err != 0)
141219b94df9SMatthew Ahrens return (err);
14133bb79becSeschrock }
14141d452cf5Sahrens }
141519b94df9SMatthew Ahrens return (0);
14161d452cf5Sahrens }
1417fa9e4066Sahrens
1418fa9e4066Sahrens static int
zfs_do_destroy(int argc,char ** argv)1419fa9e4066Sahrens zfs_do_destroy(int argc, char **argv)
1420fa9e4066Sahrens {
1421fa9e4066Sahrens destroy_cbdata_t cb = { 0 };
14223b2aab18SMatthew Ahrens int rv = 0;
14233b2aab18SMatthew Ahrens int err = 0;
1424fa9e4066Sahrens int c;
14253b2aab18SMatthew Ahrens zfs_handle_t *zhp = NULL;
142678f17100SMatthew Ahrens char *at, *pound;
1427922d9a97SChris Kirby zfs_type_t type = ZFS_TYPE_DATASET;
1428fa9e4066Sahrens
1429fa9e4066Sahrens /* check options */
143019b94df9SMatthew Ahrens while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
1431fa9e4066Sahrens switch (c) {
143219b94df9SMatthew Ahrens case 'v':
143319b94df9SMatthew Ahrens cb.cb_verbose = B_TRUE;
143419b94df9SMatthew Ahrens break;
143519b94df9SMatthew Ahrens case 'p':
143619b94df9SMatthew Ahrens cb.cb_verbose = B_TRUE;
143719b94df9SMatthew Ahrens cb.cb_parsable = B_TRUE;
143819b94df9SMatthew Ahrens break;
143919b94df9SMatthew Ahrens case 'n':
144019b94df9SMatthew Ahrens cb.cb_dryrun = B_TRUE;
144119b94df9SMatthew Ahrens break;
1442842727c2SChris Kirby case 'd':
1443842727c2SChris Kirby cb.cb_defer_destroy = B_TRUE;
1444922d9a97SChris Kirby type = ZFS_TYPE_SNAPSHOT;
1445842727c2SChris Kirby break;
1446fa9e4066Sahrens case 'f':
144719b94df9SMatthew Ahrens cb.cb_force = B_TRUE;
1448fa9e4066Sahrens break;
1449fa9e4066Sahrens case 'r':
145019b94df9SMatthew Ahrens cb.cb_recurse = B_TRUE;
1451fa9e4066Sahrens break;
1452fa9e4066Sahrens case 'R':
145319b94df9SMatthew Ahrens cb.cb_recurse = B_TRUE;
145419b94df9SMatthew Ahrens cb.cb_doclones = B_TRUE;
1455fa9e4066Sahrens break;
1456fa9e4066Sahrens case '?':
1457fa9e4066Sahrens default:
1458fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1459fa9e4066Sahrens optopt);
146099653d4eSeschrock usage(B_FALSE);
1461fa9e4066Sahrens }
1462fa9e4066Sahrens }
1463fa9e4066Sahrens
1464fa9e4066Sahrens argc -= optind;
1465fa9e4066Sahrens argv += optind;
1466fa9e4066Sahrens
1467fa9e4066Sahrens /* check number of arguments */
1468fa9e4066Sahrens if (argc == 0) {
146919b94df9SMatthew Ahrens (void) fprintf(stderr, gettext("missing dataset argument\n"));
147099653d4eSeschrock usage(B_FALSE);
1471fa9e4066Sahrens }
1472fa9e4066Sahrens if (argc > 1) {
1473fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
147499653d4eSeschrock usage(B_FALSE);
1475fa9e4066Sahrens }
1476fa9e4066Sahrens
147719b94df9SMatthew Ahrens at = strchr(argv[0], '@');
147878f17100SMatthew Ahrens pound = strchr(argv[0], '#');
147919b94df9SMatthew Ahrens if (at != NULL) {
14801d452cf5Sahrens
148119b94df9SMatthew Ahrens /* Build the list of snaps to destroy in cb_nvl. */
14823b2aab18SMatthew Ahrens cb.cb_nvl = fnvlist_alloc();
148319b94df9SMatthew Ahrens
148419b94df9SMatthew Ahrens *at = '\0';
148519b94df9SMatthew Ahrens zhp = zfs_open(g_zfs, argv[0],
148619b94df9SMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
148719b94df9SMatthew Ahrens if (zhp == NULL)
14881d452cf5Sahrens return (1);
14891d452cf5Sahrens
149019b94df9SMatthew Ahrens cb.cb_snapspec = at + 1;
149119b94df9SMatthew Ahrens if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
149219b94df9SMatthew Ahrens cb.cb_error) {
14933b2aab18SMatthew Ahrens rv = 1;
14943b2aab18SMatthew Ahrens goto out;
14953bb79becSeschrock }
14961d452cf5Sahrens
149719b94df9SMatthew Ahrens if (nvlist_empty(cb.cb_nvl)) {
149819b94df9SMatthew Ahrens (void) fprintf(stderr, gettext("could not find any "
149919b94df9SMatthew Ahrens "snapshots to destroy; check snapshot names.\n"));
15003b2aab18SMatthew Ahrens rv = 1;
15013b2aab18SMatthew Ahrens goto out;
15021d452cf5Sahrens }
15031d452cf5Sahrens
150419b94df9SMatthew Ahrens if (cb.cb_verbose) {
150519b94df9SMatthew Ahrens char buf[16];
15066520eed5SToomas Soome zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));
150719b94df9SMatthew Ahrens if (cb.cb_parsable) {
150819b94df9SMatthew Ahrens (void) printf("reclaim\t%llu\n",
150919b94df9SMatthew Ahrens cb.cb_snapused);
151019b94df9SMatthew Ahrens } else if (cb.cb_dryrun) {
151119b94df9SMatthew Ahrens (void) printf(gettext("would reclaim %s\n"),
151219b94df9SMatthew Ahrens buf);
151319b94df9SMatthew Ahrens } else {
151419b94df9SMatthew Ahrens (void) printf(gettext("will reclaim %s\n"),
151519b94df9SMatthew Ahrens buf);
151619b94df9SMatthew Ahrens }
151719b94df9SMatthew Ahrens }
151819b94df9SMatthew Ahrens
151919b94df9SMatthew Ahrens if (!cb.cb_dryrun) {
15203b2aab18SMatthew Ahrens if (cb.cb_doclones) {
15213b2aab18SMatthew Ahrens cb.cb_batchedsnaps = fnvlist_alloc();
152219b94df9SMatthew Ahrens err = destroy_clones(&cb);
152319b94df9SMatthew Ahrens if (err == 0) {
15243b2aab18SMatthew Ahrens err = zfs_destroy_snaps_nvl(g_zfs,
15253b2aab18SMatthew Ahrens cb.cb_batchedsnaps, B_FALSE);
15263b2aab18SMatthew Ahrens }
15273b2aab18SMatthew Ahrens if (err != 0) {
15283b2aab18SMatthew Ahrens rv = 1;
15293b2aab18SMatthew Ahrens goto out;
15303b2aab18SMatthew Ahrens }
15313b2aab18SMatthew Ahrens }
15323b2aab18SMatthew Ahrens if (err == 0) {
15333b2aab18SMatthew Ahrens err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
153419b94df9SMatthew Ahrens cb.cb_defer_destroy);
153519b94df9SMatthew Ahrens }
153619b94df9SMatthew Ahrens }
153719b94df9SMatthew Ahrens
153819b94df9SMatthew Ahrens if (err != 0)
15393b2aab18SMatthew Ahrens rv = 1;
154078f17100SMatthew Ahrens } else if (pound != NULL) {
154178f17100SMatthew Ahrens int err;
154278f17100SMatthew Ahrens nvlist_t *nvl;
154378f17100SMatthew Ahrens
154478f17100SMatthew Ahrens if (cb.cb_dryrun) {
154578f17100SMatthew Ahrens (void) fprintf(stderr,
154678f17100SMatthew Ahrens "dryrun is not supported with bookmark\n");
154778f17100SMatthew Ahrens return (-1);
154878f17100SMatthew Ahrens }
154978f17100SMatthew Ahrens
155078f17100SMatthew Ahrens if (cb.cb_defer_destroy) {
155178f17100SMatthew Ahrens (void) fprintf(stderr,
155278f17100SMatthew Ahrens "defer destroy is not supported with bookmark\n");
155378f17100SMatthew Ahrens return (-1);
155478f17100SMatthew Ahrens }
155578f17100SMatthew Ahrens
155678f17100SMatthew Ahrens if (cb.cb_recurse) {
155778f17100SMatthew Ahrens (void) fprintf(stderr,
155878f17100SMatthew Ahrens "recursive is not supported with bookmark\n");
155978f17100SMatthew Ahrens return (-1);
156078f17100SMatthew Ahrens }
156178f17100SMatthew Ahrens
156278f17100SMatthew Ahrens if (!zfs_bookmark_exists(argv[0])) {
156378f17100SMatthew Ahrens (void) fprintf(stderr, gettext("bookmark '%s' "
156478f17100SMatthew Ahrens "does not exist.\n"), argv[0]);
156578f17100SMatthew Ahrens return (1);
156678f17100SMatthew Ahrens }
156778f17100SMatthew Ahrens
156878f17100SMatthew Ahrens nvl = fnvlist_alloc();
156978f17100SMatthew Ahrens fnvlist_add_boolean(nvl, argv[0]);
157078f17100SMatthew Ahrens
157178f17100SMatthew Ahrens err = lzc_destroy_bookmarks(nvl, NULL);
157278f17100SMatthew Ahrens if (err != 0) {
157378f17100SMatthew Ahrens (void) zfs_standard_error(g_zfs, err,
157478f17100SMatthew Ahrens "cannot destroy bookmark");
157578f17100SMatthew Ahrens }
157678f17100SMatthew Ahrens
157778f17100SMatthew Ahrens nvlist_free(cb.cb_nvl);
157878f17100SMatthew Ahrens
157978f17100SMatthew Ahrens return (err);
158019b94df9SMatthew Ahrens } else {
1581fa9e4066Sahrens /* Open the given dataset */
1582922d9a97SChris Kirby if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
1583fa9e4066Sahrens return (1);
1584fa9e4066Sahrens
1585fa9e4066Sahrens cb.cb_target = zhp;
1586fa9e4066Sahrens
1587fa9e4066Sahrens /*
1588fa9e4066Sahrens * Perform an explicit check for pools before going any further.
1589fa9e4066Sahrens */
1590fa9e4066Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
1591fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1592fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': "
1593fa9e4066Sahrens "operation does not apply to pools\n"),
1594fa9e4066Sahrens zfs_get_name(zhp));
1595fa9e4066Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r "
1596fa9e4066Sahrens "%s' to destroy all datasets in the pool\n"),
1597fa9e4066Sahrens zfs_get_name(zhp));
1598fa9e4066Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
1599fa9e4066Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp));
16003b2aab18SMatthew Ahrens rv = 1;
16013b2aab18SMatthew Ahrens goto out;
1602fa9e4066Sahrens }
1603fa9e4066Sahrens
1604fa9e4066Sahrens /*
1605fa9e4066Sahrens * Check for any dependents and/or clones.
1606fa9e4066Sahrens */
160799653d4eSeschrock cb.cb_first = B_TRUE;
160819b94df9SMatthew Ahrens if (!cb.cb_doclones &&
16093bb79becSeschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
16103bb79becSeschrock &cb) != 0) {
16113b2aab18SMatthew Ahrens rv = 1;
16123b2aab18SMatthew Ahrens goto out;
16133bb79becSeschrock }
1614fa9e4066Sahrens
161519b94df9SMatthew Ahrens if (cb.cb_error) {
16163b2aab18SMatthew Ahrens rv = 1;
16173b2aab18SMatthew Ahrens goto out;
161819b94df9SMatthew Ahrens }
161919b94df9SMatthew Ahrens
16203b2aab18SMatthew Ahrens cb.cb_batchedsnaps = fnvlist_alloc();
162119b94df9SMatthew Ahrens if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
162219b94df9SMatthew Ahrens &cb) != 0) {
16233b2aab18SMatthew Ahrens rv = 1;
16243b2aab18SMatthew Ahrens goto out;
1625fa9e4066Sahrens }
1626fa9e4066Sahrens
1627fa9e4066Sahrens /*
162819b94df9SMatthew Ahrens * Do the real thing. The callback will close the
162919b94df9SMatthew Ahrens * handle regardless of whether it succeeds or not.
1630fa9e4066Sahrens */
16313b2aab18SMatthew Ahrens err = destroy_callback(zhp, &cb);
16323b2aab18SMatthew Ahrens zhp = NULL;
16333b2aab18SMatthew Ahrens if (err == 0) {
16343b2aab18SMatthew Ahrens err = zfs_destroy_snaps_nvl(g_zfs,
16353b2aab18SMatthew Ahrens cb.cb_batchedsnaps, cb.cb_defer_destroy);
16363b2aab18SMatthew Ahrens }
16373b2aab18SMatthew Ahrens if (err != 0)
16383b2aab18SMatthew Ahrens rv = 1;
163919b94df9SMatthew Ahrens }
16403bb79becSeschrock
16413b2aab18SMatthew Ahrens out:
16423b2aab18SMatthew Ahrens fnvlist_free(cb.cb_batchedsnaps);
16433b2aab18SMatthew Ahrens fnvlist_free(cb.cb_nvl);
16443b2aab18SMatthew Ahrens if (zhp != NULL)
16453b2aab18SMatthew Ahrens zfs_close(zhp);
16463b2aab18SMatthew Ahrens return (rv);
1647fa9e4066Sahrens }
1648fa9e4066Sahrens
164992241e0bSTom Erickson static boolean_t
is_recvd_column(zprop_get_cbdata_t * cbp)165092241e0bSTom Erickson is_recvd_column(zprop_get_cbdata_t *cbp)
165192241e0bSTom Erickson {
165292241e0bSTom Erickson int i;
165392241e0bSTom Erickson zfs_get_column_t col;
165492241e0bSTom Erickson
165592241e0bSTom Erickson for (i = 0; i < ZFS_GET_NCOLS &&
165692241e0bSTom Erickson (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
165792241e0bSTom Erickson if (col == GET_COL_RECVD)
165892241e0bSTom Erickson return (B_TRUE);
165992241e0bSTom Erickson return (B_FALSE);
166092241e0bSTom Erickson }
166192241e0bSTom Erickson
1662fa9e4066Sahrens /*
166392241e0bSTom Erickson * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
166407ba0419Seschrock * < all | property[,property]... > < fs | snap | vol > ...
1665fa9e4066Sahrens *
1666fa9e4066Sahrens * -r recurse over any child datasets
1667fa9e4066Sahrens * -H scripted mode. Headers are stripped, and fields are separated
1668fa9e4066Sahrens * by tabs instead of spaces.
166992241e0bSTom Erickson * -o Set of fields to display. One of "name,property,value,
167092241e0bSTom Erickson * received,source". Default is "name,property,value,source".
167192241e0bSTom Erickson * "all" is an alias for all five.
1672fa9e4066Sahrens * -s Set of sources to allow. One of
167392241e0bSTom Erickson * "local,default,inherited,received,temporary,none". Default is
167492241e0bSTom Erickson * all six.
1675fa9e4066Sahrens * -p Display values in parsable (literal) format.
1676fa9e4066Sahrens *
1677fa9e4066Sahrens * Prints properties for the given datasets. The user can control which
1678fa9e4066Sahrens * columns to display as well as which property types to allow.
1679fa9e4066Sahrens */
1680fa9e4066Sahrens
1681fa9e4066Sahrens /*
1682fa9e4066Sahrens * Invoked to display the properties for a single dataset.
1683fa9e4066Sahrens */
1684fa9e4066Sahrens static int
get_callback(zfs_handle_t * zhp,void * data)1685fa9e4066Sahrens get_callback(zfs_handle_t *zhp, void *data)
1686fa9e4066Sahrens {
1687fa9e4066Sahrens char buf[ZFS_MAXPROPLEN];
168892241e0bSTom Erickson char rbuf[ZFS_MAXPROPLEN];
1689990b4856Slling zprop_source_t sourcetype;
16909adfa60dSMatthew Ahrens char source[ZFS_MAX_DATASET_NAME_LEN];
1691990b4856Slling zprop_get_cbdata_t *cbp = data;
169292241e0bSTom Erickson nvlist_t *user_props = zfs_get_user_props(zhp);
1693990b4856Slling zprop_list_t *pl = cbp->cb_proplist;
1694e9dbad6fSeschrock nvlist_t *propval;
1695e9dbad6fSeschrock char *strval;
1696e9dbad6fSeschrock char *sourceval;
169792241e0bSTom Erickson boolean_t received = is_recvd_column(cbp);
1698fa9e4066Sahrens
1699e9dbad6fSeschrock for (; pl != NULL; pl = pl->pl_next) {
170092241e0bSTom Erickson char *recvdval = NULL;
1701e9dbad6fSeschrock /*
1702e9dbad6fSeschrock * Skip the special fake placeholder. This will also skip over
1703e9dbad6fSeschrock * the name property when 'all' is specified.
1704e9dbad6fSeschrock */
1705e9dbad6fSeschrock if (pl->pl_prop == ZFS_PROP_NAME &&
1706e9dbad6fSeschrock pl == cbp->cb_proplist)
170707ba0419Seschrock continue;
1708e9dbad6fSeschrock
1709990b4856Slling if (pl->pl_prop != ZPROP_INVAL) {
1710e9dbad6fSeschrock if (zfs_prop_get(zhp, pl->pl_prop, buf,
1711e9dbad6fSeschrock sizeof (buf), &sourcetype, source,
1712e9dbad6fSeschrock sizeof (source),
1713e9dbad6fSeschrock cbp->cb_literal) != 0) {
1714e9dbad6fSeschrock if (pl->pl_all)
1715e9dbad6fSeschrock continue;
1716b1b8ab34Slling if (!zfs_prop_valid_for_type(pl->pl_prop,
17174c2bdae2STim Chase ZFS_TYPE_DATASET, B_FALSE)) {
1718b1b8ab34Slling (void) fprintf(stderr,
1719b1b8ab34Slling gettext("No such property '%s'\n"),
1720b1b8ab34Slling zfs_prop_to_name(pl->pl_prop));
1721b1b8ab34Slling continue;
1722b1b8ab34Slling }
1723990b4856Slling sourcetype = ZPROP_SRC_NONE;
1724e9dbad6fSeschrock (void) strlcpy(buf, "-", sizeof (buf));
1725fa9e4066Sahrens }
1726fa9e4066Sahrens
172792241e0bSTom Erickson if (received && (zfs_prop_get_recvd(zhp,
172892241e0bSTom Erickson zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
172992241e0bSTom Erickson cbp->cb_literal) == 0))
173092241e0bSTom Erickson recvdval = rbuf;
173192241e0bSTom Erickson
1732990b4856Slling zprop_print_one_property(zfs_get_name(zhp), cbp,
1733e9dbad6fSeschrock zfs_prop_to_name(pl->pl_prop),
173492241e0bSTom Erickson buf, sourcetype, source, recvdval);
173514843421SMatthew Ahrens } else if (zfs_prop_userquota(pl->pl_user_prop)) {
173614843421SMatthew Ahrens sourcetype = ZPROP_SRC_LOCAL;
173714843421SMatthew Ahrens
173814843421SMatthew Ahrens if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
173914843421SMatthew Ahrens buf, sizeof (buf), cbp->cb_literal) != 0) {
174014843421SMatthew Ahrens sourcetype = ZPROP_SRC_NONE;
174114843421SMatthew Ahrens (void) strlcpy(buf, "-", sizeof (buf));
174214843421SMatthew Ahrens }
174314843421SMatthew Ahrens
174414843421SMatthew Ahrens zprop_print_one_property(zfs_get_name(zhp), cbp,
174592241e0bSTom Erickson pl->pl_user_prop, buf, sourcetype, source, NULL);
174619b94df9SMatthew Ahrens } else if (zfs_prop_written(pl->pl_user_prop)) {
174719b94df9SMatthew Ahrens sourcetype = ZPROP_SRC_LOCAL;
174819b94df9SMatthew Ahrens
174919b94df9SMatthew Ahrens if (zfs_prop_get_written(zhp, pl->pl_user_prop,
175019b94df9SMatthew Ahrens buf, sizeof (buf), cbp->cb_literal) != 0) {
175119b94df9SMatthew Ahrens sourcetype = ZPROP_SRC_NONE;
175219b94df9SMatthew Ahrens (void) strlcpy(buf, "-", sizeof (buf));
175319b94df9SMatthew Ahrens }
175419b94df9SMatthew Ahrens
175519b94df9SMatthew Ahrens zprop_print_one_property(zfs_get_name(zhp), cbp,
175619b94df9SMatthew Ahrens pl->pl_user_prop, buf, sourcetype, source, NULL);
1757e9dbad6fSeschrock } else {
175892241e0bSTom Erickson if (nvlist_lookup_nvlist(user_props,
1759e9dbad6fSeschrock pl->pl_user_prop, &propval) != 0) {
1760e9dbad6fSeschrock if (pl->pl_all)
1761e9dbad6fSeschrock continue;
1762990b4856Slling sourcetype = ZPROP_SRC_NONE;
1763e9dbad6fSeschrock strval = "-";
1764e9dbad6fSeschrock } else {
1765e9dbad6fSeschrock verify(nvlist_lookup_string(propval,
1766990b4856Slling ZPROP_VALUE, &strval) == 0);
1767e9dbad6fSeschrock verify(nvlist_lookup_string(propval,
1768990b4856Slling ZPROP_SOURCE, &sourceval) == 0);
1769e9dbad6fSeschrock
1770e9dbad6fSeschrock if (strcmp(sourceval,
1771e9dbad6fSeschrock zfs_get_name(zhp)) == 0) {
1772990b4856Slling sourcetype = ZPROP_SRC_LOCAL;
177392241e0bSTom Erickson } else if (strcmp(sourceval,
177492241e0bSTom Erickson ZPROP_SOURCE_VAL_RECVD) == 0) {
177592241e0bSTom Erickson sourcetype = ZPROP_SRC_RECEIVED;
1776e9dbad6fSeschrock } else {
1777990b4856Slling sourcetype = ZPROP_SRC_INHERITED;
1778e9dbad6fSeschrock (void) strlcpy(source,
1779e9dbad6fSeschrock sourceval, sizeof (source));
1780e9dbad6fSeschrock }
1781e9dbad6fSeschrock }
1782e9dbad6fSeschrock
178392241e0bSTom Erickson if (received && (zfs_prop_get_recvd(zhp,
178492241e0bSTom Erickson pl->pl_user_prop, rbuf, sizeof (rbuf),
178592241e0bSTom Erickson cbp->cb_literal) == 0))
178692241e0bSTom Erickson recvdval = rbuf;
178792241e0bSTom Erickson
1788990b4856Slling zprop_print_one_property(zfs_get_name(zhp), cbp,
1789e9dbad6fSeschrock pl->pl_user_prop, strval, sourcetype,
179092241e0bSTom Erickson source, recvdval);
1791e9dbad6fSeschrock }
1792fa9e4066Sahrens }
1793fa9e4066Sahrens
1794fa9e4066Sahrens return (0);
1795fa9e4066Sahrens }
1796fa9e4066Sahrens
1797fa9e4066Sahrens static int
zfs_do_get(int argc,char ** argv)1798fa9e4066Sahrens zfs_do_get(int argc, char **argv)
1799fa9e4066Sahrens {
1800990b4856Slling zprop_get_cbdata_t cb = { 0 };
18015ead3ed9SShampavman int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1802edb901aaSMarcel Telka int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
1803e9dbad6fSeschrock char *value, *fields;
180405c998d6SRichard Lowe int ret = 0;
1805ae1726b6SChris Gerhard int limit = 0;
1806990b4856Slling zprop_list_t fake_name = { 0 };
1807fa9e4066Sahrens
1808fa9e4066Sahrens /*
1809fa9e4066Sahrens * Set up default columns and sources.
1810fa9e4066Sahrens */
1811990b4856Slling cb.cb_sources = ZPROP_SRC_ALL;
1812fa9e4066Sahrens cb.cb_columns[0] = GET_COL_NAME;
1813fa9e4066Sahrens cb.cb_columns[1] = GET_COL_PROPERTY;
1814fa9e4066Sahrens cb.cb_columns[2] = GET_COL_VALUE;
1815fa9e4066Sahrens cb.cb_columns[3] = GET_COL_SOURCE;
1816990b4856Slling cb.cb_type = ZFS_TYPE_DATASET;
1817fa9e4066Sahrens
1818fa9e4066Sahrens /* check options */
1819441cac7aSAndrew Stormont while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
1820fa9e4066Sahrens switch (c) {
1821fa9e4066Sahrens case 'p':
182299653d4eSeschrock cb.cb_literal = B_TRUE;
1823fa9e4066Sahrens break;
1824ae1726b6SChris Gerhard case 'd':
1825ae1726b6SChris Gerhard limit = parse_depth(optarg, &flags);
1826ae1726b6SChris Gerhard break;
1827fa9e4066Sahrens case 'r':
1828d5b5bb25SRich Morris flags |= ZFS_ITER_RECURSE;
1829fa9e4066Sahrens break;
1830fa9e4066Sahrens case 'H':
183199653d4eSeschrock cb.cb_scripted = B_TRUE;
1832fa9e4066Sahrens break;
1833fa9e4066Sahrens case ':':
1834fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for "
1835fa9e4066Sahrens "'%c' option\n"), optopt);
183699653d4eSeschrock usage(B_FALSE);
1837fa9e4066Sahrens break;
1838fa9e4066Sahrens case 'o':
1839fa9e4066Sahrens /*
1840fa9e4066Sahrens * Process the set of columns to display. We zero out
1841fa9e4066Sahrens * the structure to give us a blank slate.
1842fa9e4066Sahrens */
1843fa9e4066Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1844fa9e4066Sahrens i = 0;
1845fa9e4066Sahrens while (*optarg != '\0') {
1846fa9e4066Sahrens static char *col_subopts[] =
184792241e0bSTom Erickson { "name", "property", "value", "received",
184892241e0bSTom Erickson "source", "all", NULL };
1849fa9e4066Sahrens
185092241e0bSTom Erickson if (i == ZFS_GET_NCOLS) {
1851fa9e4066Sahrens (void) fprintf(stderr, gettext("too "
1852fa9e4066Sahrens "many fields given to -o "
1853fa9e4066Sahrens "option\n"));
185499653d4eSeschrock usage(B_FALSE);
1855fa9e4066Sahrens }
1856fa9e4066Sahrens
1857fa9e4066Sahrens switch (getsubopt(&optarg, col_subopts,
1858fa9e4066Sahrens &value)) {
1859fa9e4066Sahrens case 0:
1860fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_NAME;
1861fa9e4066Sahrens break;
1862fa9e4066Sahrens case 1:
1863fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY;
1864fa9e4066Sahrens break;
1865fa9e4066Sahrens case 2:
1866fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_VALUE;
1867fa9e4066Sahrens break;
1868fa9e4066Sahrens case 3:
186992241e0bSTom Erickson cb.cb_columns[i++] = GET_COL_RECVD;
187092241e0bSTom Erickson flags |= ZFS_ITER_RECVD_PROPS;
187192241e0bSTom Erickson break;
187292241e0bSTom Erickson case 4:
1873fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_SOURCE;
1874fa9e4066Sahrens break;
187592241e0bSTom Erickson case 5:
187692241e0bSTom Erickson if (i > 0) {
187792241e0bSTom Erickson (void) fprintf(stderr,
187892241e0bSTom Erickson gettext("\"all\" conflicts "
187992241e0bSTom Erickson "with specific fields "
188092241e0bSTom Erickson "given to -o option\n"));
188192241e0bSTom Erickson usage(B_FALSE);
188292241e0bSTom Erickson }
188392241e0bSTom Erickson cb.cb_columns[0] = GET_COL_NAME;
188492241e0bSTom Erickson cb.cb_columns[1] = GET_COL_PROPERTY;
188592241e0bSTom Erickson cb.cb_columns[2] = GET_COL_VALUE;
188692241e0bSTom Erickson cb.cb_columns[3] = GET_COL_RECVD;
188792241e0bSTom Erickson cb.cb_columns[4] = GET_COL_SOURCE;
188892241e0bSTom Erickson flags |= ZFS_ITER_RECVD_PROPS;
188992241e0bSTom Erickson i = ZFS_GET_NCOLS;
189092241e0bSTom Erickson break;
1891fa9e4066Sahrens default:
1892fa9e4066Sahrens (void) fprintf(stderr,
1893fa9e4066Sahrens gettext("invalid column name "
1894fa9e4066Sahrens "'%s'\n"), value);
189599653d4eSeschrock usage(B_FALSE);
1896fa9e4066Sahrens }
1897fa9e4066Sahrens }
1898fa9e4066Sahrens break;
1899fa9e4066Sahrens
1900fa9e4066Sahrens case 's':
1901fa9e4066Sahrens cb.cb_sources = 0;
1902fa9e4066Sahrens while (*optarg != '\0') {
1903fa9e4066Sahrens static char *source_subopts[] = {
1904fa9e4066Sahrens "local", "default", "inherited",
190592241e0bSTom Erickson "received", "temporary", "none",
190692241e0bSTom Erickson NULL };
1907fa9e4066Sahrens
1908fa9e4066Sahrens switch (getsubopt(&optarg, source_subopts,
1909fa9e4066Sahrens &value)) {
1910fa9e4066Sahrens case 0:
1911990b4856Slling cb.cb_sources |= ZPROP_SRC_LOCAL;
1912fa9e4066Sahrens break;
1913fa9e4066Sahrens case 1:
1914990b4856Slling cb.cb_sources |= ZPROP_SRC_DEFAULT;
1915fa9e4066Sahrens break;
1916fa9e4066Sahrens case 2:
1917990b4856Slling cb.cb_sources |= ZPROP_SRC_INHERITED;
1918fa9e4066Sahrens break;
1919fa9e4066Sahrens case 3:
192092241e0bSTom Erickson cb.cb_sources |= ZPROP_SRC_RECEIVED;
1921fa9e4066Sahrens break;
1922fa9e4066Sahrens case 4:
192392241e0bSTom Erickson cb.cb_sources |= ZPROP_SRC_TEMPORARY;
192492241e0bSTom Erickson break;
192592241e0bSTom Erickson case 5:
1926990b4856Slling cb.cb_sources |= ZPROP_SRC_NONE;
1927fa9e4066Sahrens break;
1928fa9e4066Sahrens default:
1929fa9e4066Sahrens (void) fprintf(stderr,
1930fa9e4066Sahrens gettext("invalid source "
1931fa9e4066Sahrens "'%s'\n"), value);
193299653d4eSeschrock usage(B_FALSE);
1933fa9e4066Sahrens }
1934fa9e4066Sahrens }
1935fa9e4066Sahrens break;
1936fa9e4066Sahrens
1937441cac7aSAndrew Stormont case 't':
1938441cac7aSAndrew Stormont types = 0;
1939441cac7aSAndrew Stormont flags &= ~ZFS_ITER_PROP_LISTSNAPS;
1940441cac7aSAndrew Stormont while (*optarg != '\0') {
1941441cac7aSAndrew Stormont static char *type_subopts[] = { "filesystem",
194278f17100SMatthew Ahrens "volume", "snapshot", "bookmark",
194378f17100SMatthew Ahrens "all", NULL };
1944441cac7aSAndrew Stormont
1945441cac7aSAndrew Stormont switch (getsubopt(&optarg, type_subopts,
1946441cac7aSAndrew Stormont &value)) {
1947441cac7aSAndrew Stormont case 0:
1948441cac7aSAndrew Stormont types |= ZFS_TYPE_FILESYSTEM;
1949441cac7aSAndrew Stormont break;
1950441cac7aSAndrew Stormont case 1:
1951441cac7aSAndrew Stormont types |= ZFS_TYPE_VOLUME;
1952441cac7aSAndrew Stormont break;
1953441cac7aSAndrew Stormont case 2:
1954441cac7aSAndrew Stormont types |= ZFS_TYPE_SNAPSHOT;
1955441cac7aSAndrew Stormont break;
1956441cac7aSAndrew Stormont case 3:
195778f17100SMatthew Ahrens types |= ZFS_TYPE_BOOKMARK;
195878f17100SMatthew Ahrens break;
195978f17100SMatthew Ahrens case 4:
196078f17100SMatthew Ahrens types = ZFS_TYPE_DATASET |
196178f17100SMatthew Ahrens ZFS_TYPE_BOOKMARK;
1962441cac7aSAndrew Stormont break;
1963441cac7aSAndrew Stormont
1964441cac7aSAndrew Stormont default:
1965441cac7aSAndrew Stormont (void) fprintf(stderr,
1966441cac7aSAndrew Stormont gettext("invalid type '%s'\n"),
1967441cac7aSAndrew Stormont value);
1968441cac7aSAndrew Stormont usage(B_FALSE);
1969441cac7aSAndrew Stormont }
1970441cac7aSAndrew Stormont }
1971441cac7aSAndrew Stormont break;
1972441cac7aSAndrew Stormont
1973fa9e4066Sahrens case '?':
1974fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1975fa9e4066Sahrens optopt);
197699653d4eSeschrock usage(B_FALSE);
1977fa9e4066Sahrens }
1978fa9e4066Sahrens }
1979fa9e4066Sahrens
1980fa9e4066Sahrens argc -= optind;
1981fa9e4066Sahrens argv += optind;
1982fa9e4066Sahrens
1983fa9e4066Sahrens if (argc < 1) {
1984fa9e4066Sahrens (void) fprintf(stderr, gettext("missing property "
1985fa9e4066Sahrens "argument\n"));
198699653d4eSeschrock usage(B_FALSE);
1987fa9e4066Sahrens }
1988fa9e4066Sahrens
1989fa9e4066Sahrens fields = argv[0];
1990fa9e4066Sahrens
1991990b4856Slling if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
1992990b4856Slling != 0)
199399653d4eSeschrock usage(B_FALSE);
1994fa9e4066Sahrens
1995fa9e4066Sahrens argc--;
1996fa9e4066Sahrens argv++;
1997fa9e4066Sahrens
1998e9dbad6fSeschrock /*
1999e9dbad6fSeschrock * As part of zfs_expand_proplist(), we keep track of the maximum column
2000e9dbad6fSeschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we
2001e9dbad6fSeschrock * need to know the maximum name length. However, the user likely did
2002e9dbad6fSeschrock * not specify 'name' as one of the properties to fetch, so we need to
2003e9dbad6fSeschrock * make sure we always include at least this property for
2004e9dbad6fSeschrock * print_get_headers() to work properly.
2005e9dbad6fSeschrock */
2006e9dbad6fSeschrock if (cb.cb_proplist != NULL) {
2007e9dbad6fSeschrock fake_name.pl_prop = ZFS_PROP_NAME;
2008e9dbad6fSeschrock fake_name.pl_width = strlen(gettext("NAME"));
2009e9dbad6fSeschrock fake_name.pl_next = cb.cb_proplist;
2010e9dbad6fSeschrock cb.cb_proplist = &fake_name;
2011fa9e4066Sahrens }
2012fa9e4066Sahrens
2013e9dbad6fSeschrock cb.cb_first = B_TRUE;
2014fa9e4066Sahrens
2015fa9e4066Sahrens /* run for each object */
2016441cac7aSAndrew Stormont ret = zfs_for_each(argc, argv, flags, types, NULL,
2017ae1726b6SChris Gerhard &cb.cb_proplist, limit, get_callback, &cb);
2018b6825278Ssjelinek
2019e9dbad6fSeschrock if (cb.cb_proplist == &fake_name)
2020990b4856Slling zprop_free_list(fake_name.pl_next);
2021e9dbad6fSeschrock else
2022990b4856Slling zprop_free_list(cb.cb_proplist);
2023e9dbad6fSeschrock
2024e9dbad6fSeschrock return (ret);
2025fa9e4066Sahrens }
2026fa9e4066Sahrens
2027fa9e4066Sahrens /*
202892241e0bSTom Erickson * inherit [-rS] <property> <fs|vol> ...
2029fa9e4066Sahrens *
2030fa9e4066Sahrens * -r Recurse over all children
203192241e0bSTom Erickson * -S Revert to received value, if any
2032fa9e4066Sahrens *
2033fa9e4066Sahrens * For each dataset specified on the command line, inherit the given property
2034fa9e4066Sahrens * from its parent. Inheriting a property at the pool level will cause it to
2035fa9e4066Sahrens * use the default value. The '-r' flag will recurse over all children, and is
2036fa9e4066Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any
2037fa9e4066Sahrens * local modifications for each dataset.
2038fa9e4066Sahrens */
203906eeb2adSek110237
204092241e0bSTom Erickson typedef struct inherit_cbdata {
204192241e0bSTom Erickson const char *cb_propname;
204292241e0bSTom Erickson boolean_t cb_received;
204392241e0bSTom Erickson } inherit_cbdata_t;
204492241e0bSTom Erickson
2045fa9e4066Sahrens static int
inherit_recurse_cb(zfs_handle_t * zhp,void * data)2046bb0ade09Sahrens inherit_recurse_cb(zfs_handle_t *zhp, void *data)
2047fa9e4066Sahrens {
204892241e0bSTom Erickson inherit_cbdata_t *cb = data;
204992241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
205006eeb2adSek110237
2051bb0ade09Sahrens /*
2052bb0ade09Sahrens * If we're doing it recursively, then ignore properties that
2053bb0ade09Sahrens * are not valid for this type of dataset.
2054bb0ade09Sahrens */
2055bb0ade09Sahrens if (prop != ZPROP_INVAL &&
20564c2bdae2STim Chase !zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))
2057bb0ade09Sahrens return (0);
2058bb0ade09Sahrens
205992241e0bSTom Erickson return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
2060bb0ade09Sahrens }
2061bb0ade09Sahrens
2062bb0ade09Sahrens static int
inherit_cb(zfs_handle_t * zhp,void * data)2063bb0ade09Sahrens inherit_cb(zfs_handle_t *zhp, void *data)
2064bb0ade09Sahrens {
206592241e0bSTom Erickson inherit_cbdata_t *cb = data;
2066bb0ade09Sahrens
206792241e0bSTom Erickson return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
2068fa9e4066Sahrens }
2069fa9e4066Sahrens
2070fa9e4066Sahrens static int
zfs_do_inherit(int argc,char ** argv)2071fa9e4066Sahrens zfs_do_inherit(int argc, char **argv)
2072fa9e4066Sahrens {
2073fa9e4066Sahrens int c;
2074fa9e4066Sahrens zfs_prop_t prop;
207592241e0bSTom Erickson inherit_cbdata_t cb = { 0 };
2076ecd6cf80Smarks char *propname;
207705c998d6SRichard Lowe int ret = 0;
2078d5b5bb25SRich Morris int flags = 0;
207992241e0bSTom Erickson boolean_t received = B_FALSE;
2080fa9e4066Sahrens
2081fa9e4066Sahrens /* check options */
208292241e0bSTom Erickson while ((c = getopt(argc, argv, "rS")) != -1) {
2083fa9e4066Sahrens switch (c) {
2084fa9e4066Sahrens case 'r':
2085d5b5bb25SRich Morris flags |= ZFS_ITER_RECURSE;
2086fa9e4066Sahrens break;
208792241e0bSTom Erickson case 'S':
208892241e0bSTom Erickson received = B_TRUE;
208992241e0bSTom Erickson break;
2090fa9e4066Sahrens case '?':
2091fa9e4066Sahrens default:
2092fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2093fa9e4066Sahrens optopt);
209499653d4eSeschrock usage(B_FALSE);
2095fa9e4066Sahrens }
2096fa9e4066Sahrens }
2097fa9e4066Sahrens
2098fa9e4066Sahrens argc -= optind;
2099fa9e4066Sahrens argv += optind;
2100fa9e4066Sahrens
2101fa9e4066Sahrens /* check number of arguments */
2102fa9e4066Sahrens if (argc < 1) {
2103fa9e4066Sahrens (void) fprintf(stderr, gettext("missing property argument\n"));
210499653d4eSeschrock usage(B_FALSE);
2105fa9e4066Sahrens }
2106fa9e4066Sahrens if (argc < 2) {
2107fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n"));
210899653d4eSeschrock usage(B_FALSE);
2109fa9e4066Sahrens }
2110fa9e4066Sahrens
2111ecd6cf80Smarks propname = argv[0];
2112e9dbad6fSeschrock argc--;
2113e9dbad6fSeschrock argv++;
2114fa9e4066Sahrens
2115990b4856Slling if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
2116fa9e4066Sahrens if (zfs_prop_readonly(prop)) {
2117e9dbad6fSeschrock (void) fprintf(stderr, gettext(
2118e9dbad6fSeschrock "%s property is read-only\n"),
2119ecd6cf80Smarks propname);
2120fa9e4066Sahrens return (1);
2121fa9e4066Sahrens }
212292241e0bSTom Erickson if (!zfs_prop_inheritable(prop) && !received) {
2123e9dbad6fSeschrock (void) fprintf(stderr, gettext("'%s' property cannot "
2124ecd6cf80Smarks "be inherited\n"), propname);
2125e9dbad6fSeschrock if (prop == ZFS_PROP_QUOTA ||
2126a9799022Sck153898 prop == ZFS_PROP_RESERVATION ||
2127a9799022Sck153898 prop == ZFS_PROP_REFQUOTA ||
21285ff8cfa9SPaul B. Henson prop == ZFS_PROP_REFRESERVATION) {
2129e9dbad6fSeschrock (void) fprintf(stderr, gettext("use 'zfs set "
2130ecd6cf80Smarks "%s=none' to clear\n"), propname);
21315ff8cfa9SPaul B. Henson (void) fprintf(stderr, gettext("use 'zfs "
21325ff8cfa9SPaul B. Henson "inherit -S %s' to revert to received "
21335ff8cfa9SPaul B. Henson "value\n"), propname);
21345ff8cfa9SPaul B. Henson }
2135fa9e4066Sahrens return (1);
2136fa9e4066Sahrens }
2137a79992aaSTom Erickson if (received && (prop == ZFS_PROP_VOLSIZE ||
2138a79992aaSTom Erickson prop == ZFS_PROP_VERSION)) {
2139a79992aaSTom Erickson (void) fprintf(stderr, gettext("'%s' property cannot "
2140a79992aaSTom Erickson "be reverted to a received value\n"), propname);
2141a79992aaSTom Erickson return (1);
2142a79992aaSTom Erickson }
2143ecd6cf80Smarks } else if (!zfs_prop_user(propname)) {
2144ecd6cf80Smarks (void) fprintf(stderr, gettext("invalid property '%s'\n"),
2145ecd6cf80Smarks propname);
2146e9dbad6fSeschrock usage(B_FALSE);
2147e9dbad6fSeschrock }
2148fa9e4066Sahrens
214992241e0bSTom Erickson cb.cb_propname = propname;
215092241e0bSTom Erickson cb.cb_received = received;
215192241e0bSTom Erickson
2152d5b5bb25SRich Morris if (flags & ZFS_ITER_RECURSE) {
2153d5b5bb25SRich Morris ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
215492241e0bSTom Erickson NULL, NULL, 0, inherit_recurse_cb, &cb);
2155bb0ade09Sahrens } else {
2156d5b5bb25SRich Morris ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
215792241e0bSTom Erickson NULL, NULL, 0, inherit_cb, &cb);
2158bb0ade09Sahrens }
215906eeb2adSek110237
216006eeb2adSek110237 return (ret);
2161fa9e4066Sahrens }
2162fa9e4066Sahrens
2163e7437265Sahrens typedef struct upgrade_cbdata {
2164e7437265Sahrens uint64_t cb_numupgraded;
2165e7437265Sahrens uint64_t cb_numsamegraded;
2166e45ce728Sahrens uint64_t cb_numfailed;
2167e7437265Sahrens uint64_t cb_version;
2168e7437265Sahrens boolean_t cb_newer;
2169e7437265Sahrens boolean_t cb_foundone;
21709adfa60dSMatthew Ahrens char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];
2171e7437265Sahrens } upgrade_cbdata_t;
2172e7437265Sahrens
2173e7437265Sahrens static int
same_pool(zfs_handle_t * zhp,const char * name)2174e7437265Sahrens same_pool(zfs_handle_t *zhp, const char *name)
2175e7437265Sahrens {
2176e7437265Sahrens int len1 = strcspn(name, "/@");
2177e7437265Sahrens const char *zhname = zfs_get_name(zhp);
2178e7437265Sahrens int len2 = strcspn(zhname, "/@");
2179e7437265Sahrens
2180e7437265Sahrens if (len1 != len2)
2181e7437265Sahrens return (B_FALSE);
21822a6b87f0Sek110237 return (strncmp(name, zhname, len1) == 0);
2183e7437265Sahrens }
2184e7437265Sahrens
2185e7437265Sahrens static int
upgrade_list_callback(zfs_handle_t * zhp,void * data)2186e7437265Sahrens upgrade_list_callback(zfs_handle_t *zhp, void *data)
2187e7437265Sahrens {
2188e7437265Sahrens upgrade_cbdata_t *cb = data;
2189e7437265Sahrens int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
2190e7437265Sahrens
2191e7437265Sahrens /* list if it's old/new */
2192e7437265Sahrens if ((!cb->cb_newer && version < ZPL_VERSION) ||
21933cb34c60Sahrens (cb->cb_newer && version > ZPL_VERSION)) {
2194e7437265Sahrens char *str;
2195e7437265Sahrens if (cb->cb_newer) {
2196e7437265Sahrens str = gettext("The following filesystems are "
2197e7437265Sahrens "formatted using a newer software version and\n"
2198e7437265Sahrens "cannot be accessed on the current system.\n\n");
2199e7437265Sahrens } else {
2200e7437265Sahrens str = gettext("The following filesystems are "
2201e7437265Sahrens "out of date, and can be upgraded. After being\n"
2202e7437265Sahrens "upgraded, these filesystems (and any 'zfs send' "
2203e7437265Sahrens "streams generated from\n"
2204e7437265Sahrens "subsequent snapshots) will no longer be "
2205e7437265Sahrens "accessible by older software versions.\n\n");
2206e7437265Sahrens }
2207e7437265Sahrens
2208e7437265Sahrens if (!cb->cb_foundone) {
2209e7437265Sahrens (void) puts(str);
2210e7437265Sahrens (void) printf(gettext("VER FILESYSTEM\n"));
2211e7437265Sahrens (void) printf(gettext("--- ------------\n"));
2212e7437265Sahrens cb->cb_foundone = B_TRUE;
2213e7437265Sahrens }
2214e7437265Sahrens
2215e7437265Sahrens (void) printf("%2u %s\n", version, zfs_get_name(zhp));
2216e7437265Sahrens }
2217e7437265Sahrens
2218e7437265Sahrens return (0);
2219e7437265Sahrens }
2220e7437265Sahrens
2221e7437265Sahrens static int
upgrade_set_callback(zfs_handle_t * zhp,void * data)2222e7437265Sahrens upgrade_set_callback(zfs_handle_t *zhp, void *data)
2223e7437265Sahrens {
2224e7437265Sahrens upgrade_cbdata_t *cb = data;
2225e7437265Sahrens int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
22260a586ceaSMark Shellenbaum int needed_spa_version;
2227da6c28aaSamw int spa_version;
2228da6c28aaSamw
22297b97dc1aSrm160521 if (zfs_spa_version(zhp, &spa_version) < 0)
2230da6c28aaSamw return (-1);
2231da6c28aaSamw
22320a586ceaSMark Shellenbaum needed_spa_version = zfs_spa_version_map(cb->cb_version);
22330a586ceaSMark Shellenbaum
22340a586ceaSMark Shellenbaum if (needed_spa_version < 0)
22350a586ceaSMark Shellenbaum return (-1);
22360a586ceaSMark Shellenbaum
22370a586ceaSMark Shellenbaum if (spa_version < needed_spa_version) {
2238da6c28aaSamw /* can't upgrade */
223914843421SMatthew Ahrens (void) printf(gettext("%s: can not be "
224014843421SMatthew Ahrens "upgraded; the pool version needs to first "
224114843421SMatthew Ahrens "be upgraded\nto version %d\n\n"),
22420a586ceaSMark Shellenbaum zfs_get_name(zhp), needed_spa_version);
2243da6c28aaSamw cb->cb_numfailed++;
2244da6c28aaSamw return (0);
2245da6c28aaSamw }
2246da6c28aaSamw
2247e7437265Sahrens /* upgrade */
2248e7437265Sahrens if (version < cb->cb_version) {
2249e7437265Sahrens char verstr[16];
2250e45ce728Sahrens (void) snprintf(verstr, sizeof (verstr),
2251e45ce728Sahrens "%llu", cb->cb_version);
2252e7437265Sahrens if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
2253e7437265Sahrens /*
2254e7437265Sahrens * If they did "zfs upgrade -a", then we could
2255e7437265Sahrens * be doing ioctls to different pools. We need
22564445fffbSMatthew Ahrens * to log this history once to each pool, and bypass
22574445fffbSMatthew Ahrens * the normal history logging that happens in main().
2258e7437265Sahrens */
22594445fffbSMatthew Ahrens (void) zpool_log_history(g_zfs, history_str);
22604445fffbSMatthew Ahrens log_history = B_FALSE;
2261e7437265Sahrens }
2262e7437265Sahrens if (zfs_prop_set(zhp, "version", verstr) == 0)
2263e7437265Sahrens cb->cb_numupgraded++;
2264e45ce728Sahrens else
2265e45ce728Sahrens cb->cb_numfailed++;
2266e7437265Sahrens (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
2267e7437265Sahrens } else if (version > cb->cb_version) {
2268e7437265Sahrens /* can't downgrade */
2269e7437265Sahrens (void) printf(gettext("%s: can not be downgraded; "
2270e7437265Sahrens "it is already at version %u\n"),
2271e7437265Sahrens zfs_get_name(zhp), version);
2272e45ce728Sahrens cb->cb_numfailed++;
2273e7437265Sahrens } else {
2274e7437265Sahrens cb->cb_numsamegraded++;
2275e7437265Sahrens }
2276e7437265Sahrens return (0);
2277e7437265Sahrens }
2278e7437265Sahrens
2279e7437265Sahrens /*
2280e7437265Sahrens * zfs upgrade
2281e7437265Sahrens * zfs upgrade -v
2282e7437265Sahrens * zfs upgrade [-r] [-V <version>] <-a | filesystem>
2283e7437265Sahrens */
2284e7437265Sahrens static int
zfs_do_upgrade(int argc,char ** argv)2285e7437265Sahrens zfs_do_upgrade(int argc, char **argv)
2286e7437265Sahrens {
2287e7437265Sahrens boolean_t all = B_FALSE;
2288e7437265Sahrens boolean_t showversions = B_FALSE;
228905c998d6SRichard Lowe int ret = 0;
2290e7437265Sahrens upgrade_cbdata_t cb = { 0 };
2291ef150c2bSRichard Lowe int c;
2292d5b5bb25SRich Morris int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
2293e7437265Sahrens
2294e7437265Sahrens /* check options */
2295e7437265Sahrens while ((c = getopt(argc, argv, "rvV:a")) != -1) {
2296e7437265Sahrens switch (c) {
2297e7437265Sahrens case 'r':
2298d5b5bb25SRich Morris flags |= ZFS_ITER_RECURSE;
2299e7437265Sahrens break;
2300e7437265Sahrens case 'v':
2301e7437265Sahrens showversions = B_TRUE;
2302e7437265Sahrens break;
2303e7437265Sahrens case 'V':
2304e7437265Sahrens if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
2305e7437265Sahrens optarg, &cb.cb_version) != 0) {
2306e7437265Sahrens (void) fprintf(stderr,
2307e7437265Sahrens gettext("invalid version %s\n"), optarg);
2308e7437265Sahrens usage(B_FALSE);
2309e7437265Sahrens }
2310e7437265Sahrens break;
2311e7437265Sahrens case 'a':
2312e7437265Sahrens all = B_TRUE;
2313e7437265Sahrens break;
2314e7437265Sahrens case '?':
2315e7437265Sahrens default:
2316e7437265Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2317e7437265Sahrens optopt);
2318e7437265Sahrens usage(B_FALSE);
2319e7437265Sahrens }
2320e7437265Sahrens }
2321e7437265Sahrens
2322e7437265Sahrens argc -= optind;
2323e7437265Sahrens argv += optind;
2324e7437265Sahrens
2325d5b5bb25SRich Morris if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
2326e7437265Sahrens usage(B_FALSE);
2327d5b5bb25SRich Morris if (showversions && (flags & ZFS_ITER_RECURSE || all ||
2328d5b5bb25SRich Morris cb.cb_version || argc))
2329e7437265Sahrens usage(B_FALSE);
2330e7437265Sahrens if ((all || argc) && (showversions))
2331e7437265Sahrens usage(B_FALSE);
2332e7437265Sahrens if (all && argc)
2333e7437265Sahrens usage(B_FALSE);
2334e7437265Sahrens
2335e7437265Sahrens if (showversions) {
2336e7437265Sahrens /* Show info on available versions. */
2337e7437265Sahrens (void) printf(gettext("The following filesystem versions are "
2338e7437265Sahrens "supported:\n\n"));
2339e7437265Sahrens (void) printf(gettext("VER DESCRIPTION\n"));
2340e7437265Sahrens (void) printf("--- -----------------------------------------"
2341e7437265Sahrens "---------------\n");
2342e7437265Sahrens (void) printf(gettext(" 1 Initial ZFS filesystem version\n"));
2343e7437265Sahrens (void) printf(gettext(" 2 Enhanced directory entries\n"));
234419b94df9SMatthew Ahrens (void) printf(gettext(" 3 Case insensitive and filesystem "
234519b94df9SMatthew Ahrens "user identifier (FUID)\n"));
234614843421SMatthew Ahrens (void) printf(gettext(" 4 userquota, groupquota "
234714843421SMatthew Ahrens "properties\n"));
23480a586ceaSMark Shellenbaum (void) printf(gettext(" 5 System attributes\n"));
2349e7437265Sahrens (void) printf(gettext("\nFor more information on a particular "
23509a8685acSstephanie scheffler "version, including supported releases,\n"));
23519a8685acSstephanie scheffler (void) printf("see the ZFS Administration Guide.\n\n");
2352e7437265Sahrens ret = 0;
2353e7437265Sahrens } else if (argc || all) {
2354e7437265Sahrens /* Upgrade filesystems */
2355e7437265Sahrens if (cb.cb_version == 0)
2356e7437265Sahrens cb.cb_version = ZPL_VERSION;
2357d5b5bb25SRich Morris ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
2358ae1726b6SChris Gerhard NULL, NULL, 0, upgrade_set_callback, &cb);
2359e7437265Sahrens (void) printf(gettext("%llu filesystems upgraded\n"),
2360e7437265Sahrens cb.cb_numupgraded);
2361e7437265Sahrens if (cb.cb_numsamegraded) {
2362e7437265Sahrens (void) printf(gettext("%llu filesystems already at "
2363e7437265Sahrens "this version\n"),
2364e7437265Sahrens cb.cb_numsamegraded);
2365e7437265Sahrens }
2366e45ce728Sahrens if (cb.cb_numfailed != 0)
2367e7437265Sahrens ret = 1;
2368e7437265Sahrens } else {
2369edc8ef7dSToomas Soome /* List old-version filesystems */
2370e7437265Sahrens boolean_t found;
2371e7437265Sahrens (void) printf(gettext("This system is currently running "
2372e7437265Sahrens "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
2373e7437265Sahrens
2374d5b5bb25SRich Morris flags |= ZFS_ITER_RECURSE;
2375d5b5bb25SRich Morris ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2376ae1726b6SChris Gerhard NULL, NULL, 0, upgrade_list_callback, &cb);
2377e7437265Sahrens
2378e7437265Sahrens found = cb.cb_foundone;
2379e7437265Sahrens cb.cb_foundone = B_FALSE;
2380e7437265Sahrens cb.cb_newer = B_TRUE;
2381e7437265Sahrens
2382d5b5bb25SRich Morris ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2383ae1726b6SChris Gerhard NULL, NULL, 0, upgrade_list_callback, &cb);
2384e7437265Sahrens
2385e7437265Sahrens if (!cb.cb_foundone && !found) {
2386e7437265Sahrens (void) printf(gettext("All filesystems are "
2387e7437265Sahrens "formatted with the current version.\n"));
2388e7437265Sahrens }
2389e7437265Sahrens }
2390e7437265Sahrens
2391e7437265Sahrens return (ret);
2392e7437265Sahrens }
2393e7437265Sahrens
239489f5d17bSYuri Pankov /*
239589f5d17bSYuri Pankov * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
239689f5d17bSYuri Pankov * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
239789f5d17bSYuri Pankov * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
239889f5d17bSYuri Pankov * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2399f67950b2SNasf-Fan * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]
2400f67950b2SNasf-Fan * [-S field [-S field]...] filesystem | snapshot
240189f5d17bSYuri Pankov *
240289f5d17bSYuri Pankov * -H Scripted mode; elide headers and separate columns by tabs.
240389f5d17bSYuri Pankov * -i Translate SID to POSIX ID.
240489f5d17bSYuri Pankov * -n Print numeric ID instead of user/group name.
240589f5d17bSYuri Pankov * -o Control which fields to display.
240643d68d68SYuri Pankov * -p Use exact (parsable) numeric output.
240789f5d17bSYuri Pankov * -s Specify sort columns, descending order.
240889f5d17bSYuri Pankov * -S Specify sort columns, ascending order.
240989f5d17bSYuri Pankov * -t Control which object types to display.
241089f5d17bSYuri Pankov *
241189f5d17bSYuri Pankov * Displays space consumed by, and quotas on, each user in the specified
241289f5d17bSYuri Pankov * filesystem or snapshot.
241389f5d17bSYuri Pankov */
24141af68beaSAlexander Stetsenko
241589f5d17bSYuri Pankov /* us_field_types, us_field_hdr and us_field_names should be kept in sync */
241689f5d17bSYuri Pankov enum us_field_types {
241789f5d17bSYuri Pankov USFIELD_TYPE,
241889f5d17bSYuri Pankov USFIELD_NAME,
241989f5d17bSYuri Pankov USFIELD_USED,
2420f67950b2SNasf-Fan USFIELD_QUOTA,
2421f67950b2SNasf-Fan USFIELD_OBJUSED,
2422f67950b2SNasf-Fan USFIELD_OBJQUOTA
242389f5d17bSYuri Pankov };
2424f67950b2SNasf-Fan static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",
2425f67950b2SNasf-Fan "OBJUSED", "OBJQUOTA" };
2426f67950b2SNasf-Fan static char *us_field_names[] = { "type", "name", "used", "quota",
2427f67950b2SNasf-Fan "objused", "objquota" };
242889f5d17bSYuri Pankov #define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))
24291af68beaSAlexander Stetsenko
243089f5d17bSYuri Pankov #define USTYPE_PSX_GRP (1 << 0)
243189f5d17bSYuri Pankov #define USTYPE_PSX_USR (1 << 1)
243289f5d17bSYuri Pankov #define USTYPE_SMB_GRP (1 << 2)
243389f5d17bSYuri Pankov #define USTYPE_SMB_USR (1 << 3)
2434f67950b2SNasf-Fan #define USTYPE_PROJ (1 << 4)
243589f5d17bSYuri Pankov #define USTYPE_ALL \
2436f67950b2SNasf-Fan (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \
2437f67950b2SNasf-Fan USTYPE_PROJ)
24381af68beaSAlexander Stetsenko
243989f5d17bSYuri Pankov static int us_type_bits[] = {
244089f5d17bSYuri Pankov USTYPE_PSX_GRP,
244189f5d17bSYuri Pankov USTYPE_PSX_USR,
244289f5d17bSYuri Pankov USTYPE_SMB_GRP,
244389f5d17bSYuri Pankov USTYPE_SMB_USR,
244489f5d17bSYuri Pankov USTYPE_ALL
244589f5d17bSYuri Pankov };
2446f38cb554SJohn Wren Kennedy static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup",
244789f5d17bSYuri Pankov "smbuser", "all" };
24481af68beaSAlexander Stetsenko
24491af68beaSAlexander Stetsenko typedef struct us_node {
24501af68beaSAlexander Stetsenko nvlist_t *usn_nvl;
24511af68beaSAlexander Stetsenko uu_avl_node_t usn_avlnode;
24521af68beaSAlexander Stetsenko uu_list_node_t usn_listnode;
24531af68beaSAlexander Stetsenko } us_node_t;
24541af68beaSAlexander Stetsenko
24551af68beaSAlexander Stetsenko typedef struct us_cbdata {
24561af68beaSAlexander Stetsenko nvlist_t **cb_nvlp;
24571af68beaSAlexander Stetsenko uu_avl_pool_t *cb_avl_pool;
24581af68beaSAlexander Stetsenko uu_avl_t *cb_avl;
24591af68beaSAlexander Stetsenko boolean_t cb_numname;
24601af68beaSAlexander Stetsenko boolean_t cb_nicenum;
24611af68beaSAlexander Stetsenko boolean_t cb_sid2posix;
24621af68beaSAlexander Stetsenko zfs_userquota_prop_t cb_prop;
24631af68beaSAlexander Stetsenko zfs_sort_column_t *cb_sortcol;
246489f5d17bSYuri Pankov size_t cb_width[USFIELD_LAST];
24651af68beaSAlexander Stetsenko } us_cbdata_t;
24661af68beaSAlexander Stetsenko
246789f5d17bSYuri Pankov static boolean_t us_populated = B_FALSE;
246889f5d17bSYuri Pankov
24691af68beaSAlexander Stetsenko typedef struct {
24701af68beaSAlexander Stetsenko zfs_sort_column_t *si_sortcol;
247189f5d17bSYuri Pankov boolean_t si_numname;
24721af68beaSAlexander Stetsenko } us_sort_info_t;
24731af68beaSAlexander Stetsenko
24741af68beaSAlexander Stetsenko static int
us_field_index(char * field)247589f5d17bSYuri Pankov us_field_index(char *field)
247689f5d17bSYuri Pankov {
247789f5d17bSYuri Pankov int i;
247889f5d17bSYuri Pankov
247989f5d17bSYuri Pankov for (i = 0; i < USFIELD_LAST; i++) {
248089f5d17bSYuri Pankov if (strcmp(field, us_field_names[i]) == 0)
248189f5d17bSYuri Pankov return (i);
248289f5d17bSYuri Pankov }
248389f5d17bSYuri Pankov
248489f5d17bSYuri Pankov return (-1);
248589f5d17bSYuri Pankov }
248689f5d17bSYuri Pankov
248789f5d17bSYuri Pankov static int
us_compare(const void * larg,const void * rarg,void * unused)24881af68beaSAlexander Stetsenko us_compare(const void *larg, const void *rarg, void *unused)
24891af68beaSAlexander Stetsenko {
24901af68beaSAlexander Stetsenko const us_node_t *l = larg;
24911af68beaSAlexander Stetsenko const us_node_t *r = rarg;
24921af68beaSAlexander Stetsenko us_sort_info_t *si = (us_sort_info_t *)unused;
24931af68beaSAlexander Stetsenko zfs_sort_column_t *sortcol = si->si_sortcol;
249489f5d17bSYuri Pankov boolean_t numname = si->si_numname;
24951af68beaSAlexander Stetsenko nvlist_t *lnvl = l->usn_nvl;
24961af68beaSAlexander Stetsenko nvlist_t *rnvl = r->usn_nvl;
249789f5d17bSYuri Pankov int rc = 0;
249889f5d17bSYuri Pankov boolean_t lvb, rvb;
24991af68beaSAlexander Stetsenko
25001af68beaSAlexander Stetsenko for (; sortcol != NULL; sortcol = sortcol->sc_next) {
25011af68beaSAlexander Stetsenko char *lvstr = "";
25021af68beaSAlexander Stetsenko char *rvstr = "";
25031af68beaSAlexander Stetsenko uint32_t lv32 = 0;
25041af68beaSAlexander Stetsenko uint32_t rv32 = 0;
25051af68beaSAlexander Stetsenko uint64_t lv64 = 0;
25061af68beaSAlexander Stetsenko uint64_t rv64 = 0;
25071af68beaSAlexander Stetsenko zfs_prop_t prop = sortcol->sc_prop;
25081af68beaSAlexander Stetsenko const char *propname = NULL;
25091af68beaSAlexander Stetsenko boolean_t reverse = sortcol->sc_reverse;
25101af68beaSAlexander Stetsenko
25111af68beaSAlexander Stetsenko switch (prop) {
25121af68beaSAlexander Stetsenko case ZFS_PROP_TYPE:
25131af68beaSAlexander Stetsenko propname = "type";
25141af68beaSAlexander Stetsenko (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
25151af68beaSAlexander Stetsenko (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
25161af68beaSAlexander Stetsenko if (rv32 != lv32)
251789f5d17bSYuri Pankov rc = (rv32 < lv32) ? 1 : -1;
25181af68beaSAlexander Stetsenko break;
25191af68beaSAlexander Stetsenko case ZFS_PROP_NAME:
25201af68beaSAlexander Stetsenko propname = "name";
252189f5d17bSYuri Pankov if (numname) {
252289f5d17bSYuri Pankov (void) nvlist_lookup_uint64(lnvl, propname,
252389f5d17bSYuri Pankov &lv64);
252489f5d17bSYuri Pankov (void) nvlist_lookup_uint64(rnvl, propname,
252589f5d17bSYuri Pankov &rv64);
252689f5d17bSYuri Pankov if (rv64 != lv64)
252789f5d17bSYuri Pankov rc = (rv64 < lv64) ? 1 : -1;
25281af68beaSAlexander Stetsenko } else {
25291af68beaSAlexander Stetsenko (void) nvlist_lookup_string(lnvl, propname,
25301af68beaSAlexander Stetsenko &lvstr);
25311af68beaSAlexander Stetsenko (void) nvlist_lookup_string(rnvl, propname,
25321af68beaSAlexander Stetsenko &rvstr);
25331af68beaSAlexander Stetsenko rc = strcmp(lvstr, rvstr);
25341af68beaSAlexander Stetsenko }
25351af68beaSAlexander Stetsenko break;
25361af68beaSAlexander Stetsenko case ZFS_PROP_USED:
25371af68beaSAlexander Stetsenko case ZFS_PROP_QUOTA:
253889f5d17bSYuri Pankov if (!us_populated)
253989f5d17bSYuri Pankov break;
254089f5d17bSYuri Pankov if (prop == ZFS_PROP_USED)
25411af68beaSAlexander Stetsenko propname = "used";
25421af68beaSAlexander Stetsenko else
25431af68beaSAlexander Stetsenko propname = "quota";
25441af68beaSAlexander Stetsenko (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
25451af68beaSAlexander Stetsenko (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
25461af68beaSAlexander Stetsenko if (rv64 != lv64)
254789f5d17bSYuri Pankov rc = (rv64 < lv64) ? 1 : -1;
254889f5d17bSYuri Pankov break;
2549c16bcc45SIgor Kozhukhov
2550c16bcc45SIgor Kozhukhov default:
2551c16bcc45SIgor Kozhukhov break;
25521af68beaSAlexander Stetsenko }
25531af68beaSAlexander Stetsenko
255489f5d17bSYuri Pankov if (rc != 0) {
25551af68beaSAlexander Stetsenko if (rc < 0)
25561af68beaSAlexander Stetsenko return (reverse ? 1 : -1);
25571af68beaSAlexander Stetsenko else
25581af68beaSAlexander Stetsenko return (reverse ? -1 : 1);
25591af68beaSAlexander Stetsenko }
256089f5d17bSYuri Pankov }
25611af68beaSAlexander Stetsenko
256289f5d17bSYuri Pankov /*
256389f5d17bSYuri Pankov * If entries still seem to be the same, check if they are of the same
256489f5d17bSYuri Pankov * type (smbentity is added only if we are doing SID to POSIX ID
256589f5d17bSYuri Pankov * translation where we can have duplicate type/name combinations).
256689f5d17bSYuri Pankov */
256789f5d17bSYuri Pankov if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
256889f5d17bSYuri Pankov nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
256989f5d17bSYuri Pankov lvb != rvb)
257089f5d17bSYuri Pankov return (lvb < rvb ? -1 : 1);
257189f5d17bSYuri Pankov
257289f5d17bSYuri Pankov return (0);
25731af68beaSAlexander Stetsenko }
25741af68beaSAlexander Stetsenko
2575f67950b2SNasf-Fan static boolean_t
zfs_prop_is_user(unsigned p)2576f67950b2SNasf-Fan zfs_prop_is_user(unsigned p)
2577f67950b2SNasf-Fan {
2578f67950b2SNasf-Fan return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
2579f67950b2SNasf-Fan p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);
2580f67950b2SNasf-Fan }
2581f67950b2SNasf-Fan
2582f67950b2SNasf-Fan static boolean_t
zfs_prop_is_group(unsigned p)2583f67950b2SNasf-Fan zfs_prop_is_group(unsigned p)
2584f67950b2SNasf-Fan {
2585f67950b2SNasf-Fan return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
2586f67950b2SNasf-Fan p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);
2587f67950b2SNasf-Fan }
2588f67950b2SNasf-Fan
2589f67950b2SNasf-Fan static boolean_t
zfs_prop_is_project(unsigned p)2590f67950b2SNasf-Fan zfs_prop_is_project(unsigned p)
2591f67950b2SNasf-Fan {
2592f67950b2SNasf-Fan return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||
2593f67950b2SNasf-Fan p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);
2594f67950b2SNasf-Fan }
2595f67950b2SNasf-Fan
25961af68beaSAlexander Stetsenko static inline const char *
us_type2str(unsigned field_type)25971af68beaSAlexander Stetsenko us_type2str(unsigned field_type)
25981af68beaSAlexander Stetsenko {
25991af68beaSAlexander Stetsenko switch (field_type) {
26001af68beaSAlexander Stetsenko case USTYPE_PSX_USR:
26011af68beaSAlexander Stetsenko return ("POSIX User");
26021af68beaSAlexander Stetsenko case USTYPE_PSX_GRP:
26031af68beaSAlexander Stetsenko return ("POSIX Group");
26041af68beaSAlexander Stetsenko case USTYPE_SMB_USR:
26051af68beaSAlexander Stetsenko return ("SMB User");
26061af68beaSAlexander Stetsenko case USTYPE_SMB_GRP:
26071af68beaSAlexander Stetsenko return ("SMB Group");
2608f67950b2SNasf-Fan case USTYPE_PROJ:
2609f67950b2SNasf-Fan return ("Project");
26101af68beaSAlexander Stetsenko default:
26111af68beaSAlexander Stetsenko return ("Undefined");
26121af68beaSAlexander Stetsenko }
26131af68beaSAlexander Stetsenko }
26141af68beaSAlexander Stetsenko
26150aea4b19SMatthew Ahrens static int
userspace_cb(void * arg,const char * domain,uid_t rid,uint64_t space)261614843421SMatthew Ahrens userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
261714843421SMatthew Ahrens {
26181af68beaSAlexander Stetsenko us_cbdata_t *cb = (us_cbdata_t *)arg;
26191af68beaSAlexander Stetsenko zfs_userquota_prop_t prop = cb->cb_prop;
26200aea4b19SMatthew Ahrens char *name = NULL;
26211af68beaSAlexander Stetsenko char *propname;
262214843421SMatthew Ahrens char sizebuf[32];
26231af68beaSAlexander Stetsenko us_node_t *node;
26241af68beaSAlexander Stetsenko uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
26251af68beaSAlexander Stetsenko uu_avl_t *avl = cb->cb_avl;
26261af68beaSAlexander Stetsenko uu_avl_index_t idx;
26271af68beaSAlexander Stetsenko nvlist_t *props;
26281af68beaSAlexander Stetsenko us_node_t *n;
26291af68beaSAlexander Stetsenko zfs_sort_column_t *sortcol = cb->cb_sortcol;
2630c16bcc45SIgor Kozhukhov unsigned type = 0;
26311af68beaSAlexander Stetsenko const char *typestr;
26321af68beaSAlexander Stetsenko size_t namelen;
26331af68beaSAlexander Stetsenko size_t typelen;
26341af68beaSAlexander Stetsenko size_t sizelen;
263589f5d17bSYuri Pankov int typeidx, nameidx, sizeidx;
26361af68beaSAlexander Stetsenko us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
263789f5d17bSYuri Pankov boolean_t smbentity = B_FALSE;
263814843421SMatthew Ahrens
263989f5d17bSYuri Pankov if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
264089f5d17bSYuri Pankov nomem();
264189f5d17bSYuri Pankov node = safe_malloc(sizeof (us_node_t));
264289f5d17bSYuri Pankov uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
264389f5d17bSYuri Pankov node->usn_nvl = props;
264489f5d17bSYuri Pankov
264589f5d17bSYuri Pankov if (domain != NULL && domain[0] != '\0') {
264689f5d17bSYuri Pankov /* SMB */
26479adfa60dSMatthew Ahrens char sid[MAXNAMELEN + 32];
26481af68beaSAlexander Stetsenko uid_t id;
264989f5d17bSYuri Pankov int err;
26501ed6b69aSGordon Ross int flag = IDMAP_REQ_FLG_USE_CACHE;
26511af68beaSAlexander Stetsenko
265289f5d17bSYuri Pankov smbentity = B_TRUE;
265389f5d17bSYuri Pankov
26541af68beaSAlexander Stetsenko (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
265589f5d17bSYuri Pankov
26561af68beaSAlexander Stetsenko if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
26571af68beaSAlexander Stetsenko type = USTYPE_SMB_GRP;
26581af68beaSAlexander Stetsenko err = sid_to_id(sid, B_FALSE, &id);
26591af68beaSAlexander Stetsenko } else {
26601af68beaSAlexander Stetsenko type = USTYPE_SMB_USR;
26611af68beaSAlexander Stetsenko err = sid_to_id(sid, B_TRUE, &id);
266214843421SMatthew Ahrens }
266314843421SMatthew Ahrens
26641af68beaSAlexander Stetsenko if (err == 0) {
26651af68beaSAlexander Stetsenko rid = id;
266689f5d17bSYuri Pankov if (!cb->cb_sid2posix) {
26671ed6b69aSGordon Ross if (type == USTYPE_SMB_USR) {
26681ed6b69aSGordon Ross (void) idmap_getwinnamebyuid(rid, flag,
26691ed6b69aSGordon Ross &name, NULL);
26701ed6b69aSGordon Ross } else {
26711ed6b69aSGordon Ross (void) idmap_getwinnamebygid(rid, flag,
26721ed6b69aSGordon Ross &name, NULL);
26731ed6b69aSGordon Ross }
26741af68beaSAlexander Stetsenko if (name == NULL)
26751af68beaSAlexander Stetsenko name = sid;
26761af68beaSAlexander Stetsenko }
26771af68beaSAlexander Stetsenko }
26781af68beaSAlexander Stetsenko }
26791af68beaSAlexander Stetsenko
268089f5d17bSYuri Pankov if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
268189f5d17bSYuri Pankov /* POSIX or -i */
2682f67950b2SNasf-Fan if (zfs_prop_is_group(prop)) {
268389f5d17bSYuri Pankov type = USTYPE_PSX_GRP;
268489f5d17bSYuri Pankov if (!cb->cb_numname) {
268589f5d17bSYuri Pankov struct group *g;
268689f5d17bSYuri Pankov
268789f5d17bSYuri Pankov if ((g = getgrgid(rid)) != NULL)
268889f5d17bSYuri Pankov name = g->gr_name;
268989f5d17bSYuri Pankov }
2690f67950b2SNasf-Fan } else if (zfs_prop_is_user(prop)) {
269189f5d17bSYuri Pankov type = USTYPE_PSX_USR;
269289f5d17bSYuri Pankov if (!cb->cb_numname) {
269389f5d17bSYuri Pankov struct passwd *p;
269489f5d17bSYuri Pankov
269589f5d17bSYuri Pankov if ((p = getpwuid(rid)) != NULL)
269689f5d17bSYuri Pankov name = p->pw_name;
269789f5d17bSYuri Pankov }
2698f67950b2SNasf-Fan } else {
2699f67950b2SNasf-Fan type = USTYPE_PROJ;
270089f5d17bSYuri Pankov }
270189f5d17bSYuri Pankov }
270289f5d17bSYuri Pankov
270389f5d17bSYuri Pankov /*
270489f5d17bSYuri Pankov * Make sure that the type/name combination is unique when doing
270589f5d17bSYuri Pankov * SID to POSIX ID translation (hence changing the type from SMB to
270689f5d17bSYuri Pankov * POSIX).
270789f5d17bSYuri Pankov */
270889f5d17bSYuri Pankov if (cb->cb_sid2posix &&
270989f5d17bSYuri Pankov nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
271089f5d17bSYuri Pankov nomem();
271189f5d17bSYuri Pankov
271289f5d17bSYuri Pankov /* Calculate/update width of TYPE field */
271389f5d17bSYuri Pankov typestr = us_type2str(type);
271489f5d17bSYuri Pankov typelen = strlen(gettext(typestr));
271589f5d17bSYuri Pankov typeidx = us_field_index("type");
271689f5d17bSYuri Pankov if (typelen > cb->cb_width[typeidx])
271789f5d17bSYuri Pankov cb->cb_width[typeidx] = typelen;
27181af68beaSAlexander Stetsenko if (nvlist_add_uint32(props, "type", type) != 0)
27191af68beaSAlexander Stetsenko nomem();
27201af68beaSAlexander Stetsenko
272189f5d17bSYuri Pankov /* Calculate/update width of NAME field */
272289f5d17bSYuri Pankov if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
272389f5d17bSYuri Pankov if (nvlist_add_uint64(props, "name", rid) != 0)
27241af68beaSAlexander Stetsenko nomem();
272589f5d17bSYuri Pankov namelen = snprintf(NULL, 0, "%u", rid);
27261af68beaSAlexander Stetsenko } else {
27271af68beaSAlexander Stetsenko if (nvlist_add_string(props, "name", name) != 0)
27281af68beaSAlexander Stetsenko nomem();
27291af68beaSAlexander Stetsenko namelen = strlen(name);
27301af68beaSAlexander Stetsenko }
273189f5d17bSYuri Pankov nameidx = us_field_index("name");
273289f5d17bSYuri Pankov if (namelen > cb->cb_width[nameidx])
273389f5d17bSYuri Pankov cb->cb_width[nameidx] = namelen;
27341af68beaSAlexander Stetsenko
273589f5d17bSYuri Pankov /*
273689f5d17bSYuri Pankov * Check if this type/name combination is in the list and update it;
273789f5d17bSYuri Pankov * otherwise add new node to the list.
273889f5d17bSYuri Pankov */
273989f5d17bSYuri Pankov if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
27401af68beaSAlexander Stetsenko uu_avl_insert(avl, node, idx);
274189f5d17bSYuri Pankov } else {
27421af68beaSAlexander Stetsenko nvlist_free(props);
27431af68beaSAlexander Stetsenko free(node);
27441af68beaSAlexander Stetsenko node = n;
27451af68beaSAlexander Stetsenko props = node->usn_nvl;
27461af68beaSAlexander Stetsenko }
27471af68beaSAlexander Stetsenko
274889f5d17bSYuri Pankov /* Calculate/update width of USED/QUOTA fields */
2749f67950b2SNasf-Fan if (cb->cb_nicenum) {
2750f67950b2SNasf-Fan if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
2751f67950b2SNasf-Fan prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
2752f67950b2SNasf-Fan prop == ZFS_PROP_PROJECTUSED ||
2753f67950b2SNasf-Fan prop == ZFS_PROP_PROJECTQUOTA) {
27546520eed5SToomas Soome zfs_nicebytes(space, sizebuf, sizeof (sizebuf));
2755f67950b2SNasf-Fan } else {
2756f67950b2SNasf-Fan zfs_nicenum(space, sizebuf, sizeof (sizebuf));
2757f67950b2SNasf-Fan }
2758f67950b2SNasf-Fan } else {
275989f5d17bSYuri Pankov (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
2760f67950b2SNasf-Fan }
276189f5d17bSYuri Pankov sizelen = strlen(sizebuf);
2762f67950b2SNasf-Fan if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
2763f67950b2SNasf-Fan prop == ZFS_PROP_PROJECTUSED) {
276489f5d17bSYuri Pankov propname = "used";
276589f5d17bSYuri Pankov if (!nvlist_exists(props, "quota"))
276689f5d17bSYuri Pankov (void) nvlist_add_uint64(props, "quota", 0);
2767f67950b2SNasf-Fan } else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
2768f67950b2SNasf-Fan prop == ZFS_PROP_PROJECTQUOTA) {
276989f5d17bSYuri Pankov propname = "quota";
277089f5d17bSYuri Pankov if (!nvlist_exists(props, "used"))
277189f5d17bSYuri Pankov (void) nvlist_add_uint64(props, "used", 0);
2772f67950b2SNasf-Fan } else if (prop == ZFS_PROP_USEROBJUSED ||
2773f67950b2SNasf-Fan prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {
2774f67950b2SNasf-Fan propname = "objused";
2775f67950b2SNasf-Fan if (!nvlist_exists(props, "objquota"))
2776f67950b2SNasf-Fan (void) nvlist_add_uint64(props, "objquota", 0);
2777f67950b2SNasf-Fan } else if (prop == ZFS_PROP_USEROBJQUOTA ||
2778f67950b2SNasf-Fan prop == ZFS_PROP_GROUPOBJQUOTA ||
2779f67950b2SNasf-Fan prop == ZFS_PROP_PROJECTOBJQUOTA) {
2780f67950b2SNasf-Fan propname = "objquota";
2781f67950b2SNasf-Fan if (!nvlist_exists(props, "objused"))
2782f67950b2SNasf-Fan (void) nvlist_add_uint64(props, "objused", 0);
2783f67950b2SNasf-Fan } else {
2784f67950b2SNasf-Fan return (-1);
278589f5d17bSYuri Pankov }
278689f5d17bSYuri Pankov sizeidx = us_field_index(propname);
278789f5d17bSYuri Pankov if (sizelen > cb->cb_width[sizeidx])
278889f5d17bSYuri Pankov cb->cb_width[sizeidx] = sizelen;
278989f5d17bSYuri Pankov
27901af68beaSAlexander Stetsenko if (nvlist_add_uint64(props, propname, space) != 0)
27911af68beaSAlexander Stetsenko nomem();
27920aea4b19SMatthew Ahrens
27930aea4b19SMatthew Ahrens return (0);
279414843421SMatthew Ahrens }
279514843421SMatthew Ahrens
27961af68beaSAlexander Stetsenko static void
print_us_node(boolean_t scripted,boolean_t parsable,int * fields,int types,size_t * width,us_node_t * node)279789f5d17bSYuri Pankov print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
279889f5d17bSYuri Pankov size_t *width, us_node_t *node)
27991af68beaSAlexander Stetsenko {
28001af68beaSAlexander Stetsenko nvlist_t *nvl = node->usn_nvl;
28019adfa60dSMatthew Ahrens char valstr[MAXNAMELEN];
28021af68beaSAlexander Stetsenko boolean_t first = B_TRUE;
280389f5d17bSYuri Pankov int cfield = 0;
280489f5d17bSYuri Pankov int field;
280589f5d17bSYuri Pankov uint32_t ustype;
28061af68beaSAlexander Stetsenko
280789f5d17bSYuri Pankov /* Check type */
280889f5d17bSYuri Pankov (void) nvlist_lookup_uint32(nvl, "type", &ustype);
280989f5d17bSYuri Pankov if (!(ustype & types))
281089f5d17bSYuri Pankov return;
281189f5d17bSYuri Pankov
281289f5d17bSYuri Pankov while ((field = fields[cfield]) != USFIELD_LAST) {
281389f5d17bSYuri Pankov nvpair_t *nvp = NULL;
281489f5d17bSYuri Pankov data_type_t type;
281589f5d17bSYuri Pankov uint32_t val32;
281689f5d17bSYuri Pankov uint64_t val64;
2817f67950b2SNasf-Fan char *strval = "-";
28181af68beaSAlexander Stetsenko
28191af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
282089f5d17bSYuri Pankov if (strcmp(nvpair_name(nvp),
282189f5d17bSYuri Pankov us_field_names[field]) == 0)
28221af68beaSAlexander Stetsenko break;
28231af68beaSAlexander Stetsenko }
28241af68beaSAlexander Stetsenko
2825f67950b2SNasf-Fan type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);
28261af68beaSAlexander Stetsenko switch (type) {
28271af68beaSAlexander Stetsenko case DATA_TYPE_UINT32:
28281af68beaSAlexander Stetsenko (void) nvpair_value_uint32(nvp, &val32);
28291af68beaSAlexander Stetsenko break;
28301af68beaSAlexander Stetsenko case DATA_TYPE_UINT64:
28311af68beaSAlexander Stetsenko (void) nvpair_value_uint64(nvp, &val64);
28321af68beaSAlexander Stetsenko break;
28331af68beaSAlexander Stetsenko case DATA_TYPE_STRING:
28341af68beaSAlexander Stetsenko (void) nvpair_value_string(nvp, &strval);
28351af68beaSAlexander Stetsenko break;
2836f67950b2SNasf-Fan case DATA_TYPE_UNKNOWN:
2837f67950b2SNasf-Fan break;
28381af68beaSAlexander Stetsenko default:
283989f5d17bSYuri Pankov (void) fprintf(stderr, "invalid data type\n");
28401af68beaSAlexander Stetsenko }
28411af68beaSAlexander Stetsenko
28421af68beaSAlexander Stetsenko switch (field) {
28431af68beaSAlexander Stetsenko case USFIELD_TYPE:
2844f67950b2SNasf-Fan if (type == DATA_TYPE_UINT32)
28451af68beaSAlexander Stetsenko strval = (char *)us_type2str(val32);
28461af68beaSAlexander Stetsenko break;
28471af68beaSAlexander Stetsenko case USFIELD_NAME:
28481af68beaSAlexander Stetsenko if (type == DATA_TYPE_UINT64) {
28491af68beaSAlexander Stetsenko (void) sprintf(valstr, "%llu", val64);
28501af68beaSAlexander Stetsenko strval = valstr;
28511af68beaSAlexander Stetsenko }
28521af68beaSAlexander Stetsenko break;
28531af68beaSAlexander Stetsenko case USFIELD_USED:
28541af68beaSAlexander Stetsenko case USFIELD_QUOTA:
28556520eed5SToomas Soome if (type == DATA_TYPE_UINT64) {
28566520eed5SToomas Soome if (parsable) {
28576520eed5SToomas Soome (void) sprintf(valstr, "%llu", val64);
28586520eed5SToomas Soome strval = valstr;
28596520eed5SToomas Soome } else if (field == USFIELD_QUOTA &&
28606520eed5SToomas Soome val64 == 0) {
28616520eed5SToomas Soome strval = "none";
28626520eed5SToomas Soome } else {
28636520eed5SToomas Soome zfs_nicebytes(val64, valstr,
28646520eed5SToomas Soome sizeof (valstr));
28656520eed5SToomas Soome strval = valstr;
28666520eed5SToomas Soome }
28676520eed5SToomas Soome }
28686520eed5SToomas Soome break;
2869f67950b2SNasf-Fan case USFIELD_OBJUSED:
2870f67950b2SNasf-Fan case USFIELD_OBJQUOTA:
28711af68beaSAlexander Stetsenko if (type == DATA_TYPE_UINT64) {
287289f5d17bSYuri Pankov if (parsable) {
28731af68beaSAlexander Stetsenko (void) sprintf(valstr, "%llu", val64);
28746520eed5SToomas Soome strval = valstr;
28756520eed5SToomas Soome } else if (field == USFIELD_OBJQUOTA &&
28766520eed5SToomas Soome val64 == 0) {
28776520eed5SToomas Soome strval = "none";
287889f5d17bSYuri Pankov } else {
28791af68beaSAlexander Stetsenko zfs_nicenum(val64, valstr,
28801af68beaSAlexander Stetsenko sizeof (valstr));
28811af68beaSAlexander Stetsenko strval = valstr;
28821af68beaSAlexander Stetsenko }
28836520eed5SToomas Soome }
28841af68beaSAlexander Stetsenko break;
28851af68beaSAlexander Stetsenko }
28861af68beaSAlexander Stetsenko
288789f5d17bSYuri Pankov if (!first) {
288889f5d17bSYuri Pankov if (scripted)
288989f5d17bSYuri Pankov (void) printf("\t");
28901af68beaSAlexander Stetsenko else
289189f5d17bSYuri Pankov (void) printf(" ");
28921af68beaSAlexander Stetsenko }
289389f5d17bSYuri Pankov if (scripted)
289489f5d17bSYuri Pankov (void) printf("%s", strval);
289589f5d17bSYuri Pankov else if (field == USFIELD_TYPE || field == USFIELD_NAME)
289689f5d17bSYuri Pankov (void) printf("%-*s", width[field], strval);
289789f5d17bSYuri Pankov else
289889f5d17bSYuri Pankov (void) printf("%*s", width[field], strval);
28991af68beaSAlexander Stetsenko
29001af68beaSAlexander Stetsenko first = B_FALSE;
290189f5d17bSYuri Pankov cfield++;
29021af68beaSAlexander Stetsenko }
29031af68beaSAlexander Stetsenko
29041af68beaSAlexander Stetsenko (void) printf("\n");
29051af68beaSAlexander Stetsenko }
29061af68beaSAlexander Stetsenko
29071af68beaSAlexander Stetsenko static void
print_us(boolean_t scripted,boolean_t parsable,int * fields,int types,size_t * width,boolean_t rmnode,uu_avl_t * avl)290889f5d17bSYuri Pankov print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
290989f5d17bSYuri Pankov size_t *width, boolean_t rmnode, uu_avl_t *avl)
29101af68beaSAlexander Stetsenko {
29111af68beaSAlexander Stetsenko us_node_t *node;
29121af68beaSAlexander Stetsenko const char *col;
291389f5d17bSYuri Pankov int cfield = 0;
291489f5d17bSYuri Pankov int field;
29151af68beaSAlexander Stetsenko
29161af68beaSAlexander Stetsenko if (!scripted) {
29171af68beaSAlexander Stetsenko boolean_t first = B_TRUE;
29181af68beaSAlexander Stetsenko
291989f5d17bSYuri Pankov while ((field = fields[cfield]) != USFIELD_LAST) {
292089f5d17bSYuri Pankov col = gettext(us_field_hdr[field]);
292189f5d17bSYuri Pankov if (field == USFIELD_TYPE || field == USFIELD_NAME) {
292289f5d17bSYuri Pankov (void) printf(first ? "%-*s" : " %-*s",
292389f5d17bSYuri Pankov width[field], col);
292489f5d17bSYuri Pankov } else {
292589f5d17bSYuri Pankov (void) printf(first ? "%*s" : " %*s",
292689f5d17bSYuri Pankov width[field], col);
292789f5d17bSYuri Pankov }
29281af68beaSAlexander Stetsenko first = B_FALSE;
292989f5d17bSYuri Pankov cfield++;
29301af68beaSAlexander Stetsenko }
29311af68beaSAlexander Stetsenko (void) printf("\n");
29321af68beaSAlexander Stetsenko }
29331af68beaSAlexander Stetsenko
293489f5d17bSYuri Pankov for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
293589f5d17bSYuri Pankov print_us_node(scripted, parsable, fields, types, width, node);
29361af68beaSAlexander Stetsenko if (rmnode)
29371af68beaSAlexander Stetsenko nvlist_free(node->usn_nvl);
29381af68beaSAlexander Stetsenko }
29391af68beaSAlexander Stetsenko }
29401af68beaSAlexander Stetsenko
294114843421SMatthew Ahrens static int
zfs_do_userspace(int argc,char ** argv)294214843421SMatthew Ahrens zfs_do_userspace(int argc, char **argv)
294314843421SMatthew Ahrens {
294414843421SMatthew Ahrens zfs_handle_t *zhp;
294514843421SMatthew Ahrens zfs_userquota_prop_t p;
29461af68beaSAlexander Stetsenko uu_avl_pool_t *avl_pool;
29471af68beaSAlexander Stetsenko uu_avl_t *avl_tree;
29481af68beaSAlexander Stetsenko uu_avl_walk_t *walk;
294989f5d17bSYuri Pankov char *delim;
2950f67950b2SNasf-Fan char deffields[] = "type,name,used,quota,objused,objquota";
295189f5d17bSYuri Pankov char *ofield = NULL;
295289f5d17bSYuri Pankov char *tfield = NULL;
295389f5d17bSYuri Pankov int cfield = 0;
295489f5d17bSYuri Pankov int fields[256];
295589f5d17bSYuri Pankov int i;
29561af68beaSAlexander Stetsenko boolean_t scripted = B_FALSE;
29571af68beaSAlexander Stetsenko boolean_t prtnum = B_FALSE;
295889f5d17bSYuri Pankov boolean_t parsable = B_FALSE;
29591af68beaSAlexander Stetsenko boolean_t sid2posix = B_FALSE;
296070f56fa6SYuri Pankov int ret = 0;
29611af68beaSAlexander Stetsenko int c;
29621af68beaSAlexander Stetsenko zfs_sort_column_t *sortcol = NULL;
296389f5d17bSYuri Pankov int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
29641af68beaSAlexander Stetsenko us_cbdata_t cb;
29651af68beaSAlexander Stetsenko us_node_t *node;
296689f5d17bSYuri Pankov us_node_t *rmnode;
296789f5d17bSYuri Pankov uu_list_pool_t *listpool;
296889f5d17bSYuri Pankov uu_list_t *list;
296989f5d17bSYuri Pankov uu_avl_index_t idx = 0;
297089f5d17bSYuri Pankov uu_list_index_t idx2 = 0;
297114843421SMatthew Ahrens
29721af68beaSAlexander Stetsenko if (argc < 2)
29731af68beaSAlexander Stetsenko usage(B_FALSE);
297414843421SMatthew Ahrens
2975f67950b2SNasf-Fan if (strcmp(argv[0], "groupspace") == 0) {
297689f5d17bSYuri Pankov /* Toggle default group types */
29771af68beaSAlexander Stetsenko types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
2978f67950b2SNasf-Fan } else if (strcmp(argv[0], "projectspace") == 0) {
2979f67950b2SNasf-Fan types = USTYPE_PROJ;
2980f67950b2SNasf-Fan prtnum = B_TRUE;
2981f67950b2SNasf-Fan }
29821af68beaSAlexander Stetsenko
29831af68beaSAlexander Stetsenko while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
29841af68beaSAlexander Stetsenko switch (c) {
29851af68beaSAlexander Stetsenko case 'n':
2986f67950b2SNasf-Fan if (types == USTYPE_PROJ) {
2987f67950b2SNasf-Fan (void) fprintf(stderr,
2988f67950b2SNasf-Fan gettext("invalid option 'n'\n"));
2989f67950b2SNasf-Fan usage(B_FALSE);
2990f67950b2SNasf-Fan }
29911af68beaSAlexander Stetsenko prtnum = B_TRUE;
29921af68beaSAlexander Stetsenko break;
29931af68beaSAlexander Stetsenko case 'H':
29941af68beaSAlexander Stetsenko scripted = B_TRUE;
29951af68beaSAlexander Stetsenko break;
29961af68beaSAlexander Stetsenko case 'p':
299789f5d17bSYuri Pankov parsable = B_TRUE;
29981af68beaSAlexander Stetsenko break;
29991af68beaSAlexander Stetsenko case 'o':
300089f5d17bSYuri Pankov ofield = optarg;
30011af68beaSAlexander Stetsenko break;
30021af68beaSAlexander Stetsenko case 's':
30031af68beaSAlexander Stetsenko case 'S':
30041af68beaSAlexander Stetsenko if (zfs_add_sort_column(&sortcol, optarg,
300589f5d17bSYuri Pankov c == 's' ? B_FALSE : B_TRUE) != 0) {
30061af68beaSAlexander Stetsenko (void) fprintf(stderr,
300789f5d17bSYuri Pankov gettext("invalid field '%s'\n"), optarg);
30081af68beaSAlexander Stetsenko usage(B_FALSE);
30091af68beaSAlexander Stetsenko }
30101af68beaSAlexander Stetsenko break;
30111af68beaSAlexander Stetsenko case 't':
3012f67950b2SNasf-Fan if (types == USTYPE_PROJ) {
3013f67950b2SNasf-Fan (void) fprintf(stderr,
3014f67950b2SNasf-Fan gettext("invalid option 't'\n"));
3015f67950b2SNasf-Fan usage(B_FALSE);
3016f67950b2SNasf-Fan }
301789f5d17bSYuri Pankov tfield = optarg;
30181af68beaSAlexander Stetsenko break;
30191af68beaSAlexander Stetsenko case 'i':
3020f67950b2SNasf-Fan if (types == USTYPE_PROJ) {
3021f67950b2SNasf-Fan (void) fprintf(stderr,
3022f67950b2SNasf-Fan gettext("invalid option 'i'\n"));
3023f67950b2SNasf-Fan usage(B_FALSE);
3024f67950b2SNasf-Fan }
30251af68beaSAlexander Stetsenko sid2posix = B_TRUE;
30261af68beaSAlexander Stetsenko break;
30271af68beaSAlexander Stetsenko case ':':
30281af68beaSAlexander Stetsenko (void) fprintf(stderr, gettext("missing argument for "
30291af68beaSAlexander Stetsenko "'%c' option\n"), optopt);
30301af68beaSAlexander Stetsenko usage(B_FALSE);
30311af68beaSAlexander Stetsenko break;
30321af68beaSAlexander Stetsenko case '?':
30331af68beaSAlexander Stetsenko (void) fprintf(stderr, gettext("invalid option '%c'\n"),
30341af68beaSAlexander Stetsenko optopt);
30351af68beaSAlexander Stetsenko usage(B_FALSE);
30361af68beaSAlexander Stetsenko }
30371af68beaSAlexander Stetsenko }
30381af68beaSAlexander Stetsenko
30391af68beaSAlexander Stetsenko argc -= optind;
30401af68beaSAlexander Stetsenko argv += optind;
30411af68beaSAlexander Stetsenko
304289f5d17bSYuri Pankov if (argc < 1) {
304389f5d17bSYuri Pankov (void) fprintf(stderr, gettext("missing dataset name\n"));
304489f5d17bSYuri Pankov usage(B_FALSE);
304589f5d17bSYuri Pankov }
304689f5d17bSYuri Pankov if (argc > 1) {
304789f5d17bSYuri Pankov (void) fprintf(stderr, gettext("too many arguments\n"));
304889f5d17bSYuri Pankov usage(B_FALSE);
304989f5d17bSYuri Pankov }
305089f5d17bSYuri Pankov
305189f5d17bSYuri Pankov /* Use default output fields if not specified using -o */
305289f5d17bSYuri Pankov if (ofield == NULL)
305389f5d17bSYuri Pankov ofield = deffields;
305489f5d17bSYuri Pankov do {
305589f5d17bSYuri Pankov if ((delim = strchr(ofield, ',')) != NULL)
305689f5d17bSYuri Pankov *delim = '\0';
305789f5d17bSYuri Pankov if ((fields[cfield++] = us_field_index(ofield)) == -1) {
305889f5d17bSYuri Pankov (void) fprintf(stderr, gettext("invalid type '%s' "
305989f5d17bSYuri Pankov "for -o option\n"), ofield);
306089f5d17bSYuri Pankov return (-1);
306189f5d17bSYuri Pankov }
306289f5d17bSYuri Pankov if (delim != NULL)
306389f5d17bSYuri Pankov ofield = delim + 1;
306489f5d17bSYuri Pankov } while (delim != NULL);
306589f5d17bSYuri Pankov fields[cfield] = USFIELD_LAST;
306689f5d17bSYuri Pankov
306789f5d17bSYuri Pankov /* Override output types (-t option) */
306889f5d17bSYuri Pankov if (tfield != NULL) {
306989f5d17bSYuri Pankov types = 0;
307089f5d17bSYuri Pankov
307189f5d17bSYuri Pankov do {
307289f5d17bSYuri Pankov boolean_t found = B_FALSE;
307389f5d17bSYuri Pankov
307489f5d17bSYuri Pankov if ((delim = strchr(tfield, ',')) != NULL)
307589f5d17bSYuri Pankov *delim = '\0';
307689f5d17bSYuri Pankov for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
307789f5d17bSYuri Pankov i++) {
307889f5d17bSYuri Pankov if (strcmp(tfield, us_type_names[i]) == 0) {
307989f5d17bSYuri Pankov found = B_TRUE;
308089f5d17bSYuri Pankov types |= us_type_bits[i];
30811af68beaSAlexander Stetsenko break;
30821af68beaSAlexander Stetsenko }
30831af68beaSAlexander Stetsenko }
308489f5d17bSYuri Pankov if (!found) {
308589f5d17bSYuri Pankov (void) fprintf(stderr, gettext("invalid type "
308689f5d17bSYuri Pankov "'%s' for -t option\n"), tfield);
308789f5d17bSYuri Pankov return (-1);
308889f5d17bSYuri Pankov }
308989f5d17bSYuri Pankov if (delim != NULL)
309089f5d17bSYuri Pankov tfield = delim + 1;
309189f5d17bSYuri Pankov } while (delim != NULL);
30921af68beaSAlexander Stetsenko }
30931af68beaSAlexander Stetsenko
309489f5d17bSYuri Pankov if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
309514843421SMatthew Ahrens return (1);
309614843421SMatthew Ahrens
30971af68beaSAlexander Stetsenko if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
309889f5d17bSYuri Pankov offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
30991af68beaSAlexander Stetsenko nomem();
31001af68beaSAlexander Stetsenko if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
31011af68beaSAlexander Stetsenko nomem();
31021af68beaSAlexander Stetsenko
310389f5d17bSYuri Pankov /* Always add default sorting columns */
310489f5d17bSYuri Pankov (void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
310589f5d17bSYuri Pankov (void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
310689f5d17bSYuri Pankov
31071af68beaSAlexander Stetsenko cb.cb_sortcol = sortcol;
31081af68beaSAlexander Stetsenko cb.cb_numname = prtnum;
310989f5d17bSYuri Pankov cb.cb_nicenum = !parsable;
31101af68beaSAlexander Stetsenko cb.cb_avl_pool = avl_pool;
31111af68beaSAlexander Stetsenko cb.cb_avl = avl_tree;
31121af68beaSAlexander Stetsenko cb.cb_sid2posix = sid2posix;
311389f5d17bSYuri Pankov
311489f5d17bSYuri Pankov for (i = 0; i < USFIELD_LAST; i++)
311589f5d17bSYuri Pankov cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
311614843421SMatthew Ahrens
311714843421SMatthew Ahrens for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
3118f67950b2SNasf-Fan if ((zfs_prop_is_user(p) &&
311989f5d17bSYuri Pankov !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
3120f67950b2SNasf-Fan (zfs_prop_is_group(p) &&
3121f67950b2SNasf-Fan !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||
3122f67950b2SNasf-Fan (zfs_prop_is_project(p) && types != USTYPE_PROJ))
31231af68beaSAlexander Stetsenko continue;
3124f67950b2SNasf-Fan
31251af68beaSAlexander Stetsenko cb.cb_prop = p;
312670f56fa6SYuri Pankov if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
312770f56fa6SYuri Pankov return (ret);
312814843421SMatthew Ahrens }
31291af68beaSAlexander Stetsenko
313089f5d17bSYuri Pankov /* Sort the list */
313170f56fa6SYuri Pankov if ((node = uu_avl_first(avl_tree)) == NULL)
313270f56fa6SYuri Pankov return (0);
313370f56fa6SYuri Pankov
313489f5d17bSYuri Pankov us_populated = B_TRUE;
313570f56fa6SYuri Pankov
31361af68beaSAlexander Stetsenko listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
313789f5d17bSYuri Pankov offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
31381af68beaSAlexander Stetsenko list = uu_list_create(listpool, NULL, UU_DEFAULT);
31391af68beaSAlexander Stetsenko uu_list_node_init(node, &node->usn_listnode, listpool);
314089f5d17bSYuri Pankov
31411af68beaSAlexander Stetsenko while (node != NULL) {
31421af68beaSAlexander Stetsenko rmnode = node;
31431af68beaSAlexander Stetsenko node = uu_avl_next(avl_tree, node);
31441af68beaSAlexander Stetsenko uu_avl_remove(avl_tree, rmnode);
314589f5d17bSYuri Pankov if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
31461af68beaSAlexander Stetsenko uu_list_insert(list, rmnode, idx2);
31471af68beaSAlexander Stetsenko }
31481af68beaSAlexander Stetsenko
31491af68beaSAlexander Stetsenko for (node = uu_list_first(list); node != NULL;
31501af68beaSAlexander Stetsenko node = uu_list_next(list, node)) {
31511af68beaSAlexander Stetsenko us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
315289f5d17bSYuri Pankov
315389f5d17bSYuri Pankov if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
31541af68beaSAlexander Stetsenko uu_avl_insert(avl_tree, node, idx);
31551af68beaSAlexander Stetsenko }
31561af68beaSAlexander Stetsenko
31571af68beaSAlexander Stetsenko uu_list_destroy(list);
315889f5d17bSYuri Pankov uu_list_pool_destroy(listpool);
31591af68beaSAlexander Stetsenko
316089f5d17bSYuri Pankov /* Print and free node nvlist memory */
316189f5d17bSYuri Pankov print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
316289f5d17bSYuri Pankov cb.cb_avl);
31631af68beaSAlexander Stetsenko
31641af68beaSAlexander Stetsenko zfs_free_sort_columns(sortcol);
31651af68beaSAlexander Stetsenko
316689f5d17bSYuri Pankov /* Clean up the AVL tree */
31671af68beaSAlexander Stetsenko if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
31681af68beaSAlexander Stetsenko nomem();
31691af68beaSAlexander Stetsenko
31701af68beaSAlexander Stetsenko while ((node = uu_avl_walk_next(walk)) != NULL) {
31711af68beaSAlexander Stetsenko uu_avl_remove(cb.cb_avl, node);
31721af68beaSAlexander Stetsenko free(node);
31731af68beaSAlexander Stetsenko }
31741af68beaSAlexander Stetsenko
31751af68beaSAlexander Stetsenko uu_avl_walk_end(walk);
31761af68beaSAlexander Stetsenko uu_avl_destroy(avl_tree);
31771af68beaSAlexander Stetsenko uu_avl_pool_destroy(avl_pool);
31781af68beaSAlexander Stetsenko
317970f56fa6SYuri Pankov return (ret);
318014843421SMatthew Ahrens }
318114843421SMatthew Ahrens
318214843421SMatthew Ahrens /*
318343d68d68SYuri Pankov * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ...
318443d68d68SYuri Pankov * [-t type[,...]] [filesystem|volume|snapshot] ...
3185fa9e4066Sahrens *
318643d68d68SYuri Pankov * -H Scripted mode; elide headers and separate columns by tabs.
318743d68d68SYuri Pankov * -p Display values in parsable (literal) format.
318843d68d68SYuri Pankov * -r Recurse over all children.
3189ae1726b6SChris Gerhard * -d Limit recursion by depth.
3190fa9e4066Sahrens * -o Control which fields to display.
3191b6825278Ssjelinek * -s Specify sort columns, descending order.
3192b6825278Ssjelinek * -S Specify sort columns, ascending order.
319343d68d68SYuri Pankov * -t Control which object types to display.
3194fa9e4066Sahrens *
319543d68d68SYuri Pankov * When given no arguments, list all filesystems in the system.
3196fa9e4066Sahrens * Otherwise, list the specified datasets, optionally recursing down them if
3197fa9e4066Sahrens * '-r' is specified.
3198fa9e4066Sahrens */
3199fa9e4066Sahrens typedef struct list_cbdata {
320099653d4eSeschrock boolean_t cb_first;
320143d68d68SYuri Pankov boolean_t cb_literal;
320299653d4eSeschrock boolean_t cb_scripted;
3203990b4856Slling zprop_list_t *cb_proplist;
3204fa9e4066Sahrens } list_cbdata_t;
3205fa9e4066Sahrens
3206fa9e4066Sahrens /*
3207fa9e4066Sahrens * Given a list of columns to display, output appropriate headers for each one.
3208fa9e4066Sahrens */
3209fa9e4066Sahrens static void
print_header(list_cbdata_t * cb)321043d68d68SYuri Pankov print_header(list_cbdata_t *cb)
3211fa9e4066Sahrens {
321243d68d68SYuri Pankov zprop_list_t *pl = cb->cb_proplist;
3213e9dbad6fSeschrock char headerbuf[ZFS_MAXPROPLEN];
3214e9dbad6fSeschrock const char *header;
3215fa9e4066Sahrens int i;
3216e9dbad6fSeschrock boolean_t first = B_TRUE;
3217e9dbad6fSeschrock boolean_t right_justify;
3218fa9e4066Sahrens
3219e9dbad6fSeschrock for (; pl != NULL; pl = pl->pl_next) {
3220e9dbad6fSeschrock if (!first) {
3221fa9e4066Sahrens (void) printf(" ");
3222e9dbad6fSeschrock } else {
3223e9dbad6fSeschrock first = B_FALSE;
3224e9dbad6fSeschrock }
3225e9dbad6fSeschrock
3226e9dbad6fSeschrock right_justify = B_FALSE;
3227990b4856Slling if (pl->pl_prop != ZPROP_INVAL) {
3228e9dbad6fSeschrock header = zfs_prop_column_name(pl->pl_prop);
3229e9dbad6fSeschrock right_justify = zfs_prop_align_right(pl->pl_prop);
3230e9dbad6fSeschrock } else {
3231e9dbad6fSeschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
3232e9dbad6fSeschrock headerbuf[i] = toupper(pl->pl_user_prop[i]);
3233e9dbad6fSeschrock headerbuf[i] = '\0';
3234e9dbad6fSeschrock header = headerbuf;
3235e9dbad6fSeschrock }
3236e9dbad6fSeschrock
3237e9dbad6fSeschrock if (pl->pl_next == NULL && !right_justify)
3238e9dbad6fSeschrock (void) printf("%s", header);
3239e9dbad6fSeschrock else if (right_justify)
3240e9dbad6fSeschrock (void) printf("%*s", pl->pl_width, header);
3241e9dbad6fSeschrock else
3242e9dbad6fSeschrock (void) printf("%-*s", pl->pl_width, header);
3243fa9e4066Sahrens }
3244fa9e4066Sahrens
3245fa9e4066Sahrens (void) printf("\n");
3246fa9e4066Sahrens }
3247fa9e4066Sahrens
3248fa9e4066Sahrens /*
3249fa9e4066Sahrens * Given a dataset and a list of fields, print out all the properties according
3250fa9e4066Sahrens * to the described layout.
3251fa9e4066Sahrens */
3252fa9e4066Sahrens static void
print_dataset(zfs_handle_t * zhp,list_cbdata_t * cb)325343d68d68SYuri Pankov print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
3254fa9e4066Sahrens {
325543d68d68SYuri Pankov zprop_list_t *pl = cb->cb_proplist;
3256e9dbad6fSeschrock boolean_t first = B_TRUE;
3257fa9e4066Sahrens char property[ZFS_MAXPROPLEN];
3258e9dbad6fSeschrock nvlist_t *userprops = zfs_get_user_props(zhp);
3259e9dbad6fSeschrock nvlist_t *propval;
3260e9dbad6fSeschrock char *propstr;
3261e9dbad6fSeschrock boolean_t right_justify;
3262fa9e4066Sahrens
3263e9dbad6fSeschrock for (; pl != NULL; pl = pl->pl_next) {
3264e9dbad6fSeschrock if (!first) {
326543d68d68SYuri Pankov if (cb->cb_scripted)
3266fa9e4066Sahrens (void) printf("\t");
3267fa9e4066Sahrens else
3268fa9e4066Sahrens (void) printf(" ");
3269e9dbad6fSeschrock } else {
3270e9dbad6fSeschrock first = B_FALSE;
3271fa9e4066Sahrens }
3272fa9e4066Sahrens
32730d8fa8f8SMartin Matuska if (pl->pl_prop == ZFS_PROP_NAME) {
32740d8fa8f8SMartin Matuska (void) strlcpy(property, zfs_get_name(zhp),
32750d8fa8f8SMartin Matuska sizeof (property));
32760d8fa8f8SMartin Matuska propstr = property;
32770d8fa8f8SMartin Matuska right_justify = zfs_prop_align_right(pl->pl_prop);
32780d8fa8f8SMartin Matuska } else if (pl->pl_prop != ZPROP_INVAL) {
3279e9dbad6fSeschrock if (zfs_prop_get(zhp, pl->pl_prop, property,
328043d68d68SYuri Pankov sizeof (property), NULL, NULL, 0,
328143d68d68SYuri Pankov cb->cb_literal) != 0)
3282e9dbad6fSeschrock propstr = "-";
3283e9dbad6fSeschrock else
3284e9dbad6fSeschrock propstr = property;
3285e9dbad6fSeschrock right_justify = zfs_prop_align_right(pl->pl_prop);
328614843421SMatthew Ahrens } else if (zfs_prop_userquota(pl->pl_user_prop)) {
328714843421SMatthew Ahrens if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
328843d68d68SYuri Pankov property, sizeof (property), cb->cb_literal) != 0)
328914843421SMatthew Ahrens propstr = "-";
329014843421SMatthew Ahrens else
329114843421SMatthew Ahrens propstr = property;
329214843421SMatthew Ahrens right_justify = B_TRUE;
329319b94df9SMatthew Ahrens } else if (zfs_prop_written(pl->pl_user_prop)) {
329419b94df9SMatthew Ahrens if (zfs_prop_get_written(zhp, pl->pl_user_prop,
329543d68d68SYuri Pankov property, sizeof (property), cb->cb_literal) != 0)
329619b94df9SMatthew Ahrens propstr = "-";
329719b94df9SMatthew Ahrens else
329819b94df9SMatthew Ahrens propstr = property;
329919b94df9SMatthew Ahrens right_justify = B_TRUE;
3300e9dbad6fSeschrock } else {
3301e9dbad6fSeschrock if (nvlist_lookup_nvlist(userprops,
3302e9dbad6fSeschrock pl->pl_user_prop, &propval) != 0)
3303e9dbad6fSeschrock propstr = "-";
3304e9dbad6fSeschrock else
3305e9dbad6fSeschrock verify(nvlist_lookup_string(propval,
3306990b4856Slling ZPROP_VALUE, &propstr) == 0);
330714843421SMatthew Ahrens right_justify = B_FALSE;
3308e9dbad6fSeschrock }
3309e9dbad6fSeschrock
331007ba0419Seschrock /*
331107ba0419Seschrock * If this is being called in scripted mode, or if this is the
331207ba0419Seschrock * last column and it is left-justified, don't include a width
331307ba0419Seschrock * format specifier.
331407ba0419Seschrock */
331543d68d68SYuri Pankov if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
3316e9dbad6fSeschrock (void) printf("%s", propstr);
3317e9dbad6fSeschrock else if (right_justify)
331843d68d68SYuri Pankov (void) printf("%*s", pl->pl_width, propstr);
3319e9dbad6fSeschrock else
332043d68d68SYuri Pankov (void) printf("%-*s", pl->pl_width, propstr);
3321fa9e4066Sahrens }
3322fa9e4066Sahrens
3323fa9e4066Sahrens (void) printf("\n");
3324fa9e4066Sahrens }
3325fa9e4066Sahrens
3326fa9e4066Sahrens /*
3327fa9e4066Sahrens * Generic callback function to list a dataset or snapshot.
3328fa9e4066Sahrens */
3329fa9e4066Sahrens static int
list_callback(zfs_handle_t * zhp,void * data)3330fa9e4066Sahrens list_callback(zfs_handle_t *zhp, void *data)
3331fa9e4066Sahrens {
3332fa9e4066Sahrens list_cbdata_t *cbp = data;
3333fa9e4066Sahrens
3334fa9e4066Sahrens if (cbp->cb_first) {
3335fa9e4066Sahrens if (!cbp->cb_scripted)
333643d68d68SYuri Pankov print_header(cbp);
333799653d4eSeschrock cbp->cb_first = B_FALSE;
3338fa9e4066Sahrens }
3339fa9e4066Sahrens
334043d68d68SYuri Pankov print_dataset(zhp, cbp);
3341fa9e4066Sahrens
3342fa9e4066Sahrens return (0);
3343fa9e4066Sahrens }
3344fa9e4066Sahrens
3345fa9e4066Sahrens static int
zfs_do_list(int argc,char ** argv)3346fa9e4066Sahrens zfs_do_list(int argc, char **argv)
3347fa9e4066Sahrens {
3348fa9e4066Sahrens int c;
3349fa9e4066Sahrens static char default_fields[] =
3350fa9e4066Sahrens "name,used,available,referenced,mountpoint";
33517f73c863SRich Morris int types = ZFS_TYPE_DATASET;
3352d5b5bb25SRich Morris boolean_t types_specified = B_FALSE;
3353fa9e4066Sahrens char *fields = NULL;
3354fa9e4066Sahrens list_cbdata_t cb = { 0 };
3355fa9e4066Sahrens char *value;
3356ae1726b6SChris Gerhard int limit = 0;
335705c998d6SRichard Lowe int ret = 0;
3358b6825278Ssjelinek zfs_sort_column_t *sortcol = NULL;
3359d5b5bb25SRich Morris int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
3360fa9e4066Sahrens
3361fa9e4066Sahrens /* check options */
336243d68d68SYuri Pankov while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
3363fa9e4066Sahrens switch (c) {
3364fa9e4066Sahrens case 'o':
3365fa9e4066Sahrens fields = optarg;
3366fa9e4066Sahrens break;
336743d68d68SYuri Pankov case 'p':
336843d68d68SYuri Pankov cb.cb_literal = B_TRUE;
336943d68d68SYuri Pankov flags |= ZFS_ITER_LITERAL_PROPS;
337043d68d68SYuri Pankov break;
3371ae1726b6SChris Gerhard case 'd':
3372ae1726b6SChris Gerhard limit = parse_depth(optarg, &flags);
3373ae1726b6SChris Gerhard break;
3374fa9e4066Sahrens case 'r':
3375d5b5bb25SRich Morris flags |= ZFS_ITER_RECURSE;
3376fa9e4066Sahrens break;
3377fa9e4066Sahrens case 'H':
337843d68d68SYuri Pankov cb.cb_scripted = B_TRUE;
3379fa9e4066Sahrens break;
3380b6825278Ssjelinek case 's':
3381e9dbad6fSeschrock if (zfs_add_sort_column(&sortcol, optarg,
3382e9dbad6fSeschrock B_FALSE) != 0) {
3383b6825278Ssjelinek (void) fprintf(stderr,
3384b6825278Ssjelinek gettext("invalid property '%s'\n"), optarg);
3385b6825278Ssjelinek usage(B_FALSE);
3386b6825278Ssjelinek }
3387b6825278Ssjelinek break;
3388b6825278Ssjelinek case 'S':
3389e9dbad6fSeschrock if (zfs_add_sort_column(&sortcol, optarg,
3390e9dbad6fSeschrock B_TRUE) != 0) {
3391b6825278Ssjelinek (void) fprintf(stderr,
3392b6825278Ssjelinek gettext("invalid property '%s'\n"), optarg);
3393b6825278Ssjelinek usage(B_FALSE);
3394b6825278Ssjelinek }
3395b6825278Ssjelinek break;
3396fa9e4066Sahrens case 't':
3397fa9e4066Sahrens types = 0;
3398d5b5bb25SRich Morris types_specified = B_TRUE;
3399d5b5bb25SRich Morris flags &= ~ZFS_ITER_PROP_LISTSNAPS;
3400fa9e4066Sahrens while (*optarg != '\0') {
3401d5b5bb25SRich Morris static char *type_subopts[] = { "filesystem",
34027dbbcd83SAdam Stevko "volume", "snapshot", "snap", "bookmark",
340378f17100SMatthew Ahrens "all", NULL };
3404d5b5bb25SRich Morris
3405fa9e4066Sahrens switch (getsubopt(&optarg, type_subopts,
3406fa9e4066Sahrens &value)) {
3407fa9e4066Sahrens case 0:
3408fa9e4066Sahrens types |= ZFS_TYPE_FILESYSTEM;
3409fa9e4066Sahrens break;
3410fa9e4066Sahrens case 1:
3411fa9e4066Sahrens types |= ZFS_TYPE_VOLUME;
3412fa9e4066Sahrens break;
3413fa9e4066Sahrens case 2:
34147dbbcd83SAdam Stevko case 3:
3415fa9e4066Sahrens types |= ZFS_TYPE_SNAPSHOT;
3416fa9e4066Sahrens break;
34177dbbcd83SAdam Stevko case 4:
341878f17100SMatthew Ahrens types |= ZFS_TYPE_BOOKMARK;
3419d5b5bb25SRich Morris break;
34207dbbcd83SAdam Stevko case 5:
342178f17100SMatthew Ahrens types = ZFS_TYPE_DATASET |
342278f17100SMatthew Ahrens ZFS_TYPE_BOOKMARK;
342378f17100SMatthew Ahrens break;
3424fa9e4066Sahrens default:
3425fa9e4066Sahrens (void) fprintf(stderr,
3426fa9e4066Sahrens gettext("invalid type '%s'\n"),
3427fa9e4066Sahrens value);
342899653d4eSeschrock usage(B_FALSE);
3429fa9e4066Sahrens }
3430fa9e4066Sahrens }
3431fa9e4066Sahrens break;
3432fa9e4066Sahrens case ':':
3433fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for "
3434fa9e4066Sahrens "'%c' option\n"), optopt);
343599653d4eSeschrock usage(B_FALSE);
3436fa9e4066Sahrens break;
3437fa9e4066Sahrens case '?':
3438fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3439fa9e4066Sahrens optopt);
344099653d4eSeschrock usage(B_FALSE);
3441fa9e4066Sahrens }
3442fa9e4066Sahrens }
3443fa9e4066Sahrens
3444fa9e4066Sahrens argc -= optind;
3445fa9e4066Sahrens argv += optind;
3446fa9e4066Sahrens
3447fa9e4066Sahrens if (fields == NULL)
344874e7dc98SMatthew Ahrens fields = default_fields;
344974e7dc98SMatthew Ahrens
345074e7dc98SMatthew Ahrens /*
34510d8fa8f8SMartin Matuska * If we are only going to list snapshot names and sort by name,
34520d8fa8f8SMartin Matuska * then we can use faster version.
34530d8fa8f8SMartin Matuska */
34540d8fa8f8SMartin Matuska if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
34550d8fa8f8SMartin Matuska flags |= ZFS_ITER_SIMPLE;
34560d8fa8f8SMartin Matuska
34570d8fa8f8SMartin Matuska /*
3458d5b5bb25SRich Morris * If "-o space" and no types were specified, don't display snapshots.
345974e7dc98SMatthew Ahrens */
3460d5b5bb25SRich Morris if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
346174e7dc98SMatthew Ahrens types &= ~ZFS_TYPE_SNAPSHOT;
3462fa9e4066Sahrens
346307ba0419Seschrock /*
3464990b4856Slling * If the user specifies '-o all', the zprop_get_list() doesn't
346507ba0419Seschrock * normally include the name of the dataset. For 'zfs list', we always
346607ba0419Seschrock * want this property to be first.
346707ba0419Seschrock */
3468990b4856Slling if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
3469990b4856Slling != 0)
347099653d4eSeschrock usage(B_FALSE);
3471fa9e4066Sahrens
347299653d4eSeschrock cb.cb_first = B_TRUE;
3473fa9e4066Sahrens
3474d5b5bb25SRich Morris ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
3475ae1726b6SChris Gerhard limit, list_callback, &cb);
3476b6825278Ssjelinek
3477990b4856Slling zprop_free_list(cb.cb_proplist);
3478b6825278Ssjelinek zfs_free_sort_columns(sortcol);
3479fa9e4066Sahrens
3480fce7d82bSmmusante if (ret == 0 && cb.cb_first && !cb.cb_scripted)
3481fa9e4066Sahrens (void) printf(gettext("no datasets available\n"));
3482fa9e4066Sahrens
3483fa9e4066Sahrens return (ret);
3484fa9e4066Sahrens }
3485fa9e4066Sahrens
3486fa9e4066Sahrens /*
34876a9cb0eaSEric Schrock * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
34886a9cb0eaSEric Schrock * zfs rename [-f] -p <fs | vol> <fs | vol>
34897f1f55eaSvb160487 * zfs rename -r <snap> <snap>
3490fa9e4066Sahrens *
3491fa9e4066Sahrens * Renames the given dataset to another of the same type.
34927f1f55eaSvb160487 *
34937f1f55eaSvb160487 * The '-p' flag creates all the non-existing ancestors of the target first.
3494fa9e4066Sahrens */
3495fa9e4066Sahrens /* ARGSUSED */
3496fa9e4066Sahrens static int
zfs_do_rename(int argc,char ** argv)3497fa9e4066Sahrens zfs_do_rename(int argc, char **argv)
3498fa9e4066Sahrens {
3499fa9e4066Sahrens zfs_handle_t *zhp;
3500cdf5b4caSmmusante int c;
350105c998d6SRichard Lowe int ret = 0;
35027f1f55eaSvb160487 boolean_t recurse = B_FALSE;
35037f1f55eaSvb160487 boolean_t parents = B_FALSE;
35046a9cb0eaSEric Schrock boolean_t force_unmount = B_FALSE;
3505fa9e4066Sahrens
3506fa9e4066Sahrens /* check options */
35076a9cb0eaSEric Schrock while ((c = getopt(argc, argv, "prf")) != -1) {
3508cdf5b4caSmmusante switch (c) {
35097f1f55eaSvb160487 case 'p':
35107f1f55eaSvb160487 parents = B_TRUE;
35117f1f55eaSvb160487 break;
3512cdf5b4caSmmusante case 'r':
35137f1f55eaSvb160487 recurse = B_TRUE;
3514cdf5b4caSmmusante break;
35156a9cb0eaSEric Schrock case 'f':
35166a9cb0eaSEric Schrock force_unmount = B_TRUE;
35176a9cb0eaSEric Schrock break;
3518cdf5b4caSmmusante case '?':
3519cdf5b4caSmmusante default:
3520fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3521cdf5b4caSmmusante optopt);
352299653d4eSeschrock usage(B_FALSE);
3523fa9e4066Sahrens }
3524cdf5b4caSmmusante }
3525cdf5b4caSmmusante
3526cdf5b4caSmmusante argc -= optind;
3527cdf5b4caSmmusante argv += optind;
3528fa9e4066Sahrens
3529fa9e4066Sahrens /* check number of arguments */
3530cdf5b4caSmmusante if (argc < 1) {
3531fa9e4066Sahrens (void) fprintf(stderr, gettext("missing source dataset "
3532fa9e4066Sahrens "argument\n"));
353399653d4eSeschrock usage(B_FALSE);
3534fa9e4066Sahrens }
3535cdf5b4caSmmusante if (argc < 2) {
3536fa9e4066Sahrens (void) fprintf(stderr, gettext("missing target dataset "
3537fa9e4066Sahrens "argument\n"));
353899653d4eSeschrock usage(B_FALSE);
3539fa9e4066Sahrens }
3540cdf5b4caSmmusante if (argc > 2) {
3541fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
354299653d4eSeschrock usage(B_FALSE);
3543fa9e4066Sahrens }
3544fa9e4066Sahrens
35457f1f55eaSvb160487 if (recurse && parents) {
35467f1f55eaSvb160487 (void) fprintf(stderr, gettext("-p and -r options are mutually "
35477f1f55eaSvb160487 "exclusive\n"));
35487f1f55eaSvb160487 usage(B_FALSE);
35497f1f55eaSvb160487 }
35507f1f55eaSvb160487
3551cdf5b4caSmmusante if (recurse && strchr(argv[0], '@') == 0) {
3552cdf5b4caSmmusante (void) fprintf(stderr, gettext("source dataset for recursive "
3553cdf5b4caSmmusante "rename must be a snapshot\n"));
3554cdf5b4caSmmusante usage(B_FALSE);
3555cdf5b4caSmmusante }
3556cdf5b4caSmmusante
35577f1f55eaSvb160487 if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
3558990b4856Slling ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
3559fa9e4066Sahrens return (1);
3560fa9e4066Sahrens
35617f1f55eaSvb160487 /* If we were asked and the name looks good, try to create ancestors. */
35627f1f55eaSvb160487 if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
35637f1f55eaSvb160487 zfs_create_ancestors(g_zfs, argv[1]) != 0) {
35647f1f55eaSvb160487 zfs_close(zhp);
35657f1f55eaSvb160487 return (1);
35667f1f55eaSvb160487 }
35677f1f55eaSvb160487
35686a9cb0eaSEric Schrock ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
3569fa9e4066Sahrens
357099653d4eSeschrock zfs_close(zhp);
357199653d4eSeschrock return (ret);
357299653d4eSeschrock }
357399653d4eSeschrock
357499653d4eSeschrock /*
357599653d4eSeschrock * zfs promote <fs>
357699653d4eSeschrock *
357799653d4eSeschrock * Promotes the given clone fs to be the parent
357899653d4eSeschrock */
357999653d4eSeschrock /* ARGSUSED */
358099653d4eSeschrock static int
zfs_do_promote(int argc,char ** argv)358199653d4eSeschrock zfs_do_promote(int argc, char **argv)
358299653d4eSeschrock {
358399653d4eSeschrock zfs_handle_t *zhp;
358405c998d6SRichard Lowe int ret = 0;
358599653d4eSeschrock
358699653d4eSeschrock /* check options */
358799653d4eSeschrock if (argc > 1 && argv[1][0] == '-') {
358899653d4eSeschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"),
358999653d4eSeschrock argv[1][1]);
359099653d4eSeschrock usage(B_FALSE);
359199653d4eSeschrock }
359299653d4eSeschrock
359399653d4eSeschrock /* check number of arguments */
359499653d4eSeschrock if (argc < 2) {
359599653d4eSeschrock (void) fprintf(stderr, gettext("missing clone filesystem"
359699653d4eSeschrock " argument\n"));
359799653d4eSeschrock usage(B_FALSE);
359899653d4eSeschrock }
359999653d4eSeschrock if (argc > 2) {
360099653d4eSeschrock (void) fprintf(stderr, gettext("too many arguments\n"));
360199653d4eSeschrock usage(B_FALSE);
360299653d4eSeschrock }
360399653d4eSeschrock
360499653d4eSeschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
360599653d4eSeschrock if (zhp == NULL)
360699653d4eSeschrock return (1);
360799653d4eSeschrock
360899653d4eSeschrock ret = (zfs_promote(zhp) != 0);
360999653d4eSeschrock
361006eeb2adSek110237
3611fa9e4066Sahrens zfs_close(zhp);
3612fa9e4066Sahrens return (ret);
3613fa9e4066Sahrens }
3614fa9e4066Sahrens
3615fa9e4066Sahrens /*
3616f17c7ff8Sahrens * zfs rollback [-rRf] <snapshot>
3617fa9e4066Sahrens *
3618fa9e4066Sahrens * -r Delete any intervening snapshots before doing rollback
3619fa9e4066Sahrens * -R Delete any snapshots and their clones
3620f17c7ff8Sahrens * -f ignored for backwards compatability
3621fa9e4066Sahrens *
3622fa9e4066Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes
3623fa9e4066Sahrens * since then and making it the active dataset. If more recent snapshots exist,
3624fa9e4066Sahrens * the command will complain unless the '-r' flag is given.
3625fa9e4066Sahrens */
3626fa9e4066Sahrens typedef struct rollback_cbdata {
3627fa9e4066Sahrens uint64_t cb_create;
362899653d4eSeschrock boolean_t cb_first;
3629fa9e4066Sahrens int cb_doclones;
3630fa9e4066Sahrens char *cb_target;
3631fa9e4066Sahrens int cb_error;
363299653d4eSeschrock boolean_t cb_recurse;
3633fa9e4066Sahrens } rollback_cbdata_t;
3634fa9e4066Sahrens
363578f17100SMatthew Ahrens static int
rollback_check_dependent(zfs_handle_t * zhp,void * data)363678f17100SMatthew Ahrens rollback_check_dependent(zfs_handle_t *zhp, void *data)
363778f17100SMatthew Ahrens {
363878f17100SMatthew Ahrens rollback_cbdata_t *cbp = data;
363978f17100SMatthew Ahrens
364078f17100SMatthew Ahrens if (cbp->cb_first && cbp->cb_recurse) {
364178f17100SMatthew Ahrens (void) fprintf(stderr, gettext("cannot rollback to "
364278f17100SMatthew Ahrens "'%s': clones of previous snapshots exist\n"),
364378f17100SMatthew Ahrens cbp->cb_target);
364478f17100SMatthew Ahrens (void) fprintf(stderr, gettext("use '-R' to "
364578f17100SMatthew Ahrens "force deletion of the following clones and "
364678f17100SMatthew Ahrens "dependents:\n"));
364778f17100SMatthew Ahrens cbp->cb_first = 0;
364878f17100SMatthew Ahrens cbp->cb_error = 1;
364978f17100SMatthew Ahrens }
365078f17100SMatthew Ahrens
365178f17100SMatthew Ahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
365278f17100SMatthew Ahrens
365378f17100SMatthew Ahrens zfs_close(zhp);
365478f17100SMatthew Ahrens return (0);
365578f17100SMatthew Ahrens }
36565d7b4d43SMatthew Ahrens
3657fa9e4066Sahrens /*
3658fa9e4066Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is
3659fa9e4066Sahrens * not specified. We reuse this same callback for the snapshot dependents - if
3660fa9e4066Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it
3661fa9e4066Sahrens * without checking the transaction group.
3662fa9e4066Sahrens */
3663fa9e4066Sahrens static int
rollback_check(zfs_handle_t * zhp,void * data)3664fa9e4066Sahrens rollback_check(zfs_handle_t *zhp, void *data)
3665fa9e4066Sahrens {
3666fa9e4066Sahrens rollback_cbdata_t *cbp = data;
3667fa9e4066Sahrens
366899653d4eSeschrock if (cbp->cb_doclones) {
366999653d4eSeschrock zfs_close(zhp);
3670fa9e4066Sahrens return (0);
367199653d4eSeschrock }
3672fa9e4066Sahrens
367378f17100SMatthew Ahrens if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
3674fa9e4066Sahrens if (cbp->cb_first && !cbp->cb_recurse) {
3675fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot "
3676fa9e4066Sahrens "rollback to '%s': more recent snapshots "
367778f17100SMatthew Ahrens "or bookmarks exist\n"),
3678fa9e4066Sahrens cbp->cb_target);
3679fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-r' to "
3680fa9e4066Sahrens "force deletion of the following "
368178f17100SMatthew Ahrens "snapshots and bookmarks:\n"));
3682fa9e4066Sahrens cbp->cb_first = 0;
3683fa9e4066Sahrens cbp->cb_error = 1;
3684fa9e4066Sahrens }
3685fa9e4066Sahrens
3686fa9e4066Sahrens if (cbp->cb_recurse) {
36873bb79becSeschrock if (zfs_iter_dependents(zhp, B_TRUE,
368878f17100SMatthew Ahrens rollback_check_dependent, cbp) != 0) {
36893bb79becSeschrock zfs_close(zhp);
36903bb79becSeschrock return (-1);
36913bb79becSeschrock }
3692fa9e4066Sahrens } else {
3693fa9e4066Sahrens (void) fprintf(stderr, "%s\n",
3694fa9e4066Sahrens zfs_get_name(zhp));
3695fa9e4066Sahrens }
3696fa9e4066Sahrens }
3697fa9e4066Sahrens zfs_close(zhp);
3698fa9e4066Sahrens return (0);
3699fa9e4066Sahrens }
3700fa9e4066Sahrens
3701fa9e4066Sahrens static int
zfs_do_rollback(int argc,char ** argv)3702fa9e4066Sahrens zfs_do_rollback(int argc, char **argv)
3703fa9e4066Sahrens {
370405c998d6SRichard Lowe int ret = 0;
3705fa9e4066Sahrens int c;
3706c391e322Sahrens boolean_t force = B_FALSE;
3707fa9e4066Sahrens rollback_cbdata_t cb = { 0 };
3708fa9e4066Sahrens zfs_handle_t *zhp, *snap;
37099adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN];
3710fa9e4066Sahrens char *delim;
3711fa9e4066Sahrens
3712fa9e4066Sahrens /* check options */
3713f17c7ff8Sahrens while ((c = getopt(argc, argv, "rRf")) != -1) {
3714fa9e4066Sahrens switch (c) {
3715fa9e4066Sahrens case 'r':
3716fa9e4066Sahrens cb.cb_recurse = 1;
3717fa9e4066Sahrens break;
3718fa9e4066Sahrens case 'R':
3719fa9e4066Sahrens cb.cb_recurse = 1;
3720fa9e4066Sahrens cb.cb_doclones = 1;
3721fa9e4066Sahrens break;
3722f17c7ff8Sahrens case 'f':
3723c391e322Sahrens force = B_TRUE;
3724f17c7ff8Sahrens break;
3725fa9e4066Sahrens case '?':
3726fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3727fa9e4066Sahrens optopt);
372899653d4eSeschrock usage(B_FALSE);
3729fa9e4066Sahrens }
3730fa9e4066Sahrens }
3731fa9e4066Sahrens
3732fa9e4066Sahrens argc -= optind;
3733fa9e4066Sahrens argv += optind;
3734fa9e4066Sahrens
3735fa9e4066Sahrens /* check number of arguments */
3736fa9e4066Sahrens if (argc < 1) {
3737fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n"));
373899653d4eSeschrock usage(B_FALSE);
3739fa9e4066Sahrens }
3740fa9e4066Sahrens if (argc > 1) {
3741fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
374299653d4eSeschrock usage(B_FALSE);
3743fa9e4066Sahrens }
3744fa9e4066Sahrens
3745fa9e4066Sahrens /* open the snapshot */
374699653d4eSeschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
3747fa9e4066Sahrens return (1);
3748fa9e4066Sahrens
3749b12a1c38Slling /* open the parent dataset */
3750b12a1c38Slling (void) strlcpy(parentname, argv[0], sizeof (parentname));
3751fa9e4066Sahrens verify((delim = strrchr(parentname, '@')) != NULL);
3752fa9e4066Sahrens *delim = '\0';
3753990b4856Slling if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
3754fa9e4066Sahrens zfs_close(snap);
3755fa9e4066Sahrens return (1);
3756fa9e4066Sahrens }
3757fa9e4066Sahrens
3758fa9e4066Sahrens /*
3759fa9e4066Sahrens * Check for more recent snapshots and/or clones based on the presence
3760fa9e4066Sahrens * of '-r' and '-R'.
3761fa9e4066Sahrens */
3762b12a1c38Slling cb.cb_target = argv[0];
3763b12a1c38Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
376499653d4eSeschrock cb.cb_first = B_TRUE;
3765fa9e4066Sahrens cb.cb_error = 0;
37660d8fa8f8SMartin Matuska if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0)
376778f17100SMatthew Ahrens goto out;
376878f17100SMatthew Ahrens if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
37693bb79becSeschrock goto out;
3770fa9e4066Sahrens
3771fa9e4066Sahrens if ((ret = cb.cb_error) != 0)
3772fa9e4066Sahrens goto out;
3773fa9e4066Sahrens
3774fa9e4066Sahrens /*
3775b12a1c38Slling * Rollback parent to the given snapshot.
3776fa9e4066Sahrens */
3777c391e322Sahrens ret = zfs_rollback(zhp, snap, force);
3778fa9e4066Sahrens
3779fa9e4066Sahrens out:
3780fa9e4066Sahrens zfs_close(snap);
3781fa9e4066Sahrens zfs_close(zhp);
3782fa9e4066Sahrens
3783fa9e4066Sahrens if (ret == 0)
3784fa9e4066Sahrens return (0);
3785fa9e4066Sahrens else
3786fa9e4066Sahrens return (1);
3787fa9e4066Sahrens }
3788fa9e4066Sahrens
3789fa9e4066Sahrens /*
379030925561SChris Williamson * zfs set property=value ... { fs | snap | vol } ...
3791fa9e4066Sahrens *
379230925561SChris Williamson * Sets the given properties for all datasets specified on the command line.
3793fa9e4066Sahrens */
3794fa9e4066Sahrens
3795fa9e4066Sahrens static int
set_callback(zfs_handle_t * zhp,void * data)3796fa9e4066Sahrens set_callback(zfs_handle_t *zhp, void *data)
3797fa9e4066Sahrens {
379830925561SChris Williamson nvlist_t *props = data;
3799fa9e4066Sahrens
380030925561SChris Williamson if (zfs_prop_set_list(zhp, props) != 0) {
3801efc555ebSnd150628 switch (libzfs_errno(g_zfs)) {
3802efc555ebSnd150628 case EZFS_MOUNTFAILED:
3803efc555ebSnd150628 (void) fprintf(stderr, gettext("property may be set "
3804efc555ebSnd150628 "but unable to remount filesystem\n"));
3805efc555ebSnd150628 break;
3806f3861e1aSahl case EZFS_SHARENFSFAILED:
3807efc555ebSnd150628 (void) fprintf(stderr, gettext("property may be set "
3808efc555ebSnd150628 "but unable to reshare filesystem\n"));
3809efc555ebSnd150628 break;
3810efc555ebSnd150628 }
3811fa9e4066Sahrens return (1);
3812efc555ebSnd150628 }
38135ad82045Snd150628 return (0);
3814fa9e4066Sahrens }
3815fa9e4066Sahrens
3816fa9e4066Sahrens static int
zfs_do_set(int argc,char ** argv)3817fa9e4066Sahrens zfs_do_set(int argc, char **argv)
3818fa9e4066Sahrens {
381930925561SChris Williamson nvlist_t *props = NULL;
382030925561SChris Williamson int ds_start = -1; /* argv idx of first dataset arg */
382105c998d6SRichard Lowe int ret = 0;
3822fa9e4066Sahrens
3823fa9e4066Sahrens /* check for options */
3824fa9e4066Sahrens if (argc > 1 && argv[1][0] == '-') {
3825fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3826fa9e4066Sahrens argv[1][1]);
382799653d4eSeschrock usage(B_FALSE);
3828fa9e4066Sahrens }
3829fa9e4066Sahrens
3830fa9e4066Sahrens /* check number of arguments */
3831fa9e4066Sahrens if (argc < 2) {
383230925561SChris Williamson (void) fprintf(stderr, gettext("missing arguments\n"));
383399653d4eSeschrock usage(B_FALSE);
3834fa9e4066Sahrens }
3835fa9e4066Sahrens if (argc < 3) {
383630925561SChris Williamson if (strchr(argv[1], '=') == NULL) {
383730925561SChris Williamson (void) fprintf(stderr, gettext("missing property=value "
383830925561SChris Williamson "argument(s)\n"));
383930925561SChris Williamson } else {
384030925561SChris Williamson (void) fprintf(stderr, gettext("missing dataset "
384130925561SChris Williamson "name(s)\n"));
384230925561SChris Williamson }
384399653d4eSeschrock usage(B_FALSE);
3844fa9e4066Sahrens }
3845fa9e4066Sahrens
384630925561SChris Williamson /* validate argument order: prop=val args followed by dataset args */
384730925561SChris Williamson for (int i = 1; i < argc; i++) {
384830925561SChris Williamson if (strchr(argv[i], '=') != NULL) {
384930925561SChris Williamson if (ds_start > 0) {
385030925561SChris Williamson /* out-of-order prop=val argument */
385130925561SChris Williamson (void) fprintf(stderr, gettext("invalid "
385230925561SChris Williamson "argument order\n"), i);
385330925561SChris Williamson usage(B_FALSE);
385430925561SChris Williamson }
385530925561SChris Williamson } else if (ds_start < 0) {
385630925561SChris Williamson ds_start = i;
385730925561SChris Williamson }
385830925561SChris Williamson }
385930925561SChris Williamson if (ds_start < 0) {
386030925561SChris Williamson (void) fprintf(stderr, gettext("missing dataset name(s)\n"));
386199653d4eSeschrock usage(B_FALSE);
3862fa9e4066Sahrens }
3863fa9e4066Sahrens
386430925561SChris Williamson /* Populate a list of property settings */
386530925561SChris Williamson if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
386630925561SChris Williamson nomem();
386730925561SChris Williamson for (int i = 1; i < ds_start; i++) {
38686ccda740Sloli10K if (!parseprop(props, argv[i])) {
38696ccda740Sloli10K ret = -1;
387030925561SChris Williamson goto error;
3871fa9e4066Sahrens }
38726ccda740Sloli10K }
3873fa9e4066Sahrens
387430925561SChris Williamson ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
387530925561SChris Williamson ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
387606eeb2adSek110237
387730925561SChris Williamson error:
387830925561SChris Williamson nvlist_free(props);
387906eeb2adSek110237 return (ret);
3880fa9e4066Sahrens }
3881fa9e4066Sahrens
38824445fffbSMatthew Ahrens typedef struct snap_cbdata {
38834445fffbSMatthew Ahrens nvlist_t *sd_nvl;
38844445fffbSMatthew Ahrens boolean_t sd_recursive;
38854445fffbSMatthew Ahrens const char *sd_snapname;
38864445fffbSMatthew Ahrens } snap_cbdata_t;
38874445fffbSMatthew Ahrens
38884445fffbSMatthew Ahrens static int
zfs_snapshot_cb(zfs_handle_t * zhp,void * arg)38894445fffbSMatthew Ahrens zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
38904445fffbSMatthew Ahrens {
38914445fffbSMatthew Ahrens snap_cbdata_t *sd = arg;
38924445fffbSMatthew Ahrens char *name;
38934445fffbSMatthew Ahrens int rv = 0;
38944445fffbSMatthew Ahrens int error;
38954445fffbSMatthew Ahrens
3896ca48f36fSKeith M Wesolowski if (sd->sd_recursive &&
3897ca48f36fSKeith M Wesolowski zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
3898ca48f36fSKeith M Wesolowski zfs_close(zhp);
3899ca48f36fSKeith M Wesolowski return (0);
3900ca48f36fSKeith M Wesolowski }
3901ca48f36fSKeith M Wesolowski
39024445fffbSMatthew Ahrens error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
39034445fffbSMatthew Ahrens if (error == -1)
39044445fffbSMatthew Ahrens nomem();
39054445fffbSMatthew Ahrens fnvlist_add_boolean(sd->sd_nvl, name);
39064445fffbSMatthew Ahrens free(name);
39074445fffbSMatthew Ahrens
39084445fffbSMatthew Ahrens if (sd->sd_recursive)
39094445fffbSMatthew Ahrens rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
39104445fffbSMatthew Ahrens zfs_close(zhp);
39114445fffbSMatthew Ahrens return (rv);
39124445fffbSMatthew Ahrens }
39134445fffbSMatthew Ahrens
3914fa9e4066Sahrens /*
3915bb0ade09Sahrens * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
3916fa9e4066Sahrens *
3917fa9e4066Sahrens * Creates a snapshot with the given name. While functionally equivalent to
3918da6c28aaSamw * 'zfs create', it is a separate command to differentiate intent.
3919fa9e4066Sahrens */
3920fa9e4066Sahrens static int
zfs_do_snapshot(int argc,char ** argv)3921fa9e4066Sahrens zfs_do_snapshot(int argc, char **argv)
3922fa9e4066Sahrens {
392305c998d6SRichard Lowe int ret = 0;
3924ef150c2bSRichard Lowe int c;
3925bb0ade09Sahrens nvlist_t *props;
39264445fffbSMatthew Ahrens snap_cbdata_t sd = { 0 };
39274445fffbSMatthew Ahrens boolean_t multiple_snaps = B_FALSE;
3928bb0ade09Sahrens
3929a8b6ddafSMark J Musante if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3930a8b6ddafSMark J Musante nomem();
39314445fffbSMatthew Ahrens if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
39324445fffbSMatthew Ahrens nomem();
39331d452cf5Sahrens
3934fa9e4066Sahrens /* check options */
3935bb0ade09Sahrens while ((c = getopt(argc, argv, "ro:")) != -1) {
39361d452cf5Sahrens switch (c) {
3937bb0ade09Sahrens case 'o':
39386ccda740Sloli10K if (!parseprop(props, optarg)) {
39396ccda740Sloli10K nvlist_free(sd.sd_nvl);
39406ccda740Sloli10K nvlist_free(props);
3941bb0ade09Sahrens return (1);
39426ccda740Sloli10K }
3943bb0ade09Sahrens break;
39441d452cf5Sahrens case 'r':
39454445fffbSMatthew Ahrens sd.sd_recursive = B_TRUE;
39464445fffbSMatthew Ahrens multiple_snaps = B_TRUE;
39471d452cf5Sahrens break;
39481d452cf5Sahrens case '?':
3949fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
39501d452cf5Sahrens optopt);
3951bb0ade09Sahrens goto usage;
3952fa9e4066Sahrens }
39531d452cf5Sahrens }
39541d452cf5Sahrens
39551d452cf5Sahrens argc -= optind;
39561d452cf5Sahrens argv += optind;
3957fa9e4066Sahrens
3958fa9e4066Sahrens /* check number of arguments */
39591d452cf5Sahrens if (argc < 1) {
3960fa9e4066Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3961bb0ade09Sahrens goto usage;
3962fa9e4066Sahrens }
39634445fffbSMatthew Ahrens
39644445fffbSMatthew Ahrens if (argc > 1)
39654445fffbSMatthew Ahrens multiple_snaps = B_TRUE;
39664445fffbSMatthew Ahrens for (; argc > 0; argc--, argv++) {
39674445fffbSMatthew Ahrens char *atp;
39684445fffbSMatthew Ahrens zfs_handle_t *zhp;
39694445fffbSMatthew Ahrens
39704445fffbSMatthew Ahrens atp = strchr(argv[0], '@');
39714445fffbSMatthew Ahrens if (atp == NULL)
39724445fffbSMatthew Ahrens goto usage;
39734445fffbSMatthew Ahrens *atp = '\0';
39744445fffbSMatthew Ahrens sd.sd_snapname = atp + 1;
39754445fffbSMatthew Ahrens zhp = zfs_open(g_zfs, argv[0],
39764445fffbSMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
39774445fffbSMatthew Ahrens if (zhp == NULL)
39784445fffbSMatthew Ahrens goto usage;
39794445fffbSMatthew Ahrens if (zfs_snapshot_cb(zhp, &sd) != 0)
3980bb0ade09Sahrens goto usage;
3981fa9e4066Sahrens }
3982fa9e4066Sahrens
39834445fffbSMatthew Ahrens ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
39844445fffbSMatthew Ahrens nvlist_free(sd.sd_nvl);
3985bb0ade09Sahrens nvlist_free(props);
39864445fffbSMatthew Ahrens if (ret != 0 && multiple_snaps)
39871d452cf5Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n"));
39881d452cf5Sahrens return (ret != 0);
3989bb0ade09Sahrens
3990bb0ade09Sahrens usage:
39914445fffbSMatthew Ahrens nvlist_free(sd.sd_nvl);
3992bb0ade09Sahrens nvlist_free(props);
3993bb0ade09Sahrens usage(B_FALSE);
3994bb0ade09Sahrens return (-1);
3995fa9e4066Sahrens }
3996fa9e4066Sahrens
3997fa9e4066Sahrens /*
3998fa9e4066Sahrens * Send a backup stream to stdout.
3999fa9e4066Sahrens */
4000fa9e4066Sahrens static int
zfs_do_send(int argc,char ** argv)4001f2a3c691Sahrens zfs_do_send(int argc, char **argv)
4002fa9e4066Sahrens {
4003fa9e4066Sahrens char *fromname = NULL;
40043cb34c60Sahrens char *toname = NULL;
40059c3fd121SMatthew Ahrens char *resume_token = NULL;
4006a2eea2e1Sahrens char *cp;
4007a2eea2e1Sahrens zfs_handle_t *zhp;
40089e69d7d0SLori Alt sendflags_t flags = { 0 };
4009fa9e4066Sahrens int c, err;
401019b94df9SMatthew Ahrens nvlist_t *dbgnv = NULL;
40113f9d6ad7SLin Ling boolean_t extraverbose = B_FALSE;
4012fa9e4066Sahrens
40135602294fSDan Kimmel struct option long_options[] = {
40145602294fSDan Kimmel {"replicate", no_argument, NULL, 'R'},
40155602294fSDan Kimmel {"props", no_argument, NULL, 'p'},
40165602294fSDan Kimmel {"parsable", no_argument, NULL, 'P'},
40175602294fSDan Kimmel {"dedup", no_argument, NULL, 'D'},
40185602294fSDan Kimmel {"verbose", no_argument, NULL, 'v'},
40195602294fSDan Kimmel {"dryrun", no_argument, NULL, 'n'},
40205602294fSDan Kimmel {"large-block", no_argument, NULL, 'L'},
40215602294fSDan Kimmel {"embed", no_argument, NULL, 'e'},
40225602294fSDan Kimmel {"resume", required_argument, NULL, 't'},
40235602294fSDan Kimmel {"compressed", no_argument, NULL, 'c'},
4024eb633035STom Caputi {"raw", no_argument, NULL, 'w'},
40256ccda740Sloli10K {"backup", no_argument, NULL, 'b'},
40266ccda740Sloli10K {"holds", no_argument, NULL, 'h'},
40275602294fSDan Kimmel {0, 0, 0, 0}
40285602294fSDan Kimmel };
40295602294fSDan Kimmel
4030fa9e4066Sahrens /* check options */
40316ccda740Sloli10K while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwb", long_options,
40325602294fSDan Kimmel NULL)) != -1) {
4033fa9e4066Sahrens switch (c) {
4034fa9e4066Sahrens case 'i':
4035a2eea2e1Sahrens if (fromname)
4036a2eea2e1Sahrens usage(B_FALSE);
4037fa9e4066Sahrens fromname = optarg;
4038fa9e4066Sahrens break;
40393cb34c60Sahrens case 'I':
40403cb34c60Sahrens if (fromname)
40413cb34c60Sahrens usage(B_FALSE);
40423cb34c60Sahrens fromname = optarg;
40439e69d7d0SLori Alt flags.doall = B_TRUE;
40443cb34c60Sahrens break;
40453cb34c60Sahrens case 'R':
40469e69d7d0SLori Alt flags.replicate = B_TRUE;
40473cb34c60Sahrens break;
404892241e0bSTom Erickson case 'p':
404992241e0bSTom Erickson flags.props = B_TRUE;
405092241e0bSTom Erickson break;
40516ccda740Sloli10K case 'b':
40526ccda740Sloli10K flags.backup = B_TRUE;
40536ccda740Sloli10K break;
40546ccda740Sloli10K case 'h':
40556ccda740Sloli10K flags.holds = B_TRUE;
40566ccda740Sloli10K break;
405719b94df9SMatthew Ahrens case 'P':
405819b94df9SMatthew Ahrens flags.parsable = B_TRUE;
405919b94df9SMatthew Ahrens flags.verbose = B_TRUE;
406019b94df9SMatthew Ahrens break;
40613cb34c60Sahrens case 'v':
40623f9d6ad7SLin Ling if (flags.verbose)
40633f9d6ad7SLin Ling extraverbose = B_TRUE;
40649e69d7d0SLori Alt flags.verbose = B_TRUE;
40654e3c9f44SBill Pijewski flags.progress = B_TRUE;
40669e69d7d0SLori Alt break;
40679e69d7d0SLori Alt case 'D':
40689e69d7d0SLori Alt flags.dedup = B_TRUE;
40693cb34c60Sahrens break;
407019b94df9SMatthew Ahrens case 'n':
407119b94df9SMatthew Ahrens flags.dryrun = B_TRUE;
407219b94df9SMatthew Ahrens break;
4073b5152584SMatthew Ahrens case 'L':
4074b5152584SMatthew Ahrens flags.largeblock = B_TRUE;
4075b5152584SMatthew Ahrens break;
40765d7b4d43SMatthew Ahrens case 'e':
40775d7b4d43SMatthew Ahrens flags.embed_data = B_TRUE;
40785d7b4d43SMatthew Ahrens break;
40799c3fd121SMatthew Ahrens case 't':
40809c3fd121SMatthew Ahrens resume_token = optarg;
40819c3fd121SMatthew Ahrens break;
40825602294fSDan Kimmel case 'c':
40835602294fSDan Kimmel flags.compress = B_TRUE;
40845602294fSDan Kimmel break;
4085eb633035STom Caputi case 'w':
4086eb633035STom Caputi flags.raw = B_TRUE;
4087eb633035STom Caputi flags.compress = B_TRUE;
4088eb633035STom Caputi flags.embed_data = B_TRUE;
4089eb633035STom Caputi flags.largeblock = B_TRUE;
4090eb633035STom Caputi break;
4091fa9e4066Sahrens case ':':
40925c653870SPaul Dagnelie /*
40935c653870SPaul Dagnelie * If a parameter was not passed, optopt contains the
40945c653870SPaul Dagnelie * value that would normally lead us into the
40955c653870SPaul Dagnelie * appropriate case statement. If it's > 256, then this
40965c653870SPaul Dagnelie * must be a longopt and we should look at argv to get
40975c653870SPaul Dagnelie * the string. Otherwise it's just the character, so we
40985c653870SPaul Dagnelie * should use it directly.
40995c653870SPaul Dagnelie */
41005c653870SPaul Dagnelie if (optopt <= UINT8_MAX) {
41015c653870SPaul Dagnelie (void) fprintf(stderr,
41025c653870SPaul Dagnelie gettext("missing argument for '%c' "
41035c653870SPaul Dagnelie "option\n"), optopt);
41045c653870SPaul Dagnelie } else {
41055c653870SPaul Dagnelie (void) fprintf(stderr,
41065c653870SPaul Dagnelie gettext("missing argument for '%s' "
41075c653870SPaul Dagnelie "option\n"), argv[optind - 1]);
41085c653870SPaul Dagnelie }
410999653d4eSeschrock usage(B_FALSE);
4110fa9e4066Sahrens break;
4111fa9e4066Sahrens case '?':
41125602294fSDan Kimmel /*FALLTHROUGH*/
41135602294fSDan Kimmel default:
41145c653870SPaul Dagnelie /*
41155c653870SPaul Dagnelie * If an invalid flag was passed, optopt contains the
41165c653870SPaul Dagnelie * character if it was a short flag, or 0 if it was a
41175c653870SPaul Dagnelie * longopt.
41185c653870SPaul Dagnelie */
41195c653870SPaul Dagnelie if (optopt != 0) {
41205c653870SPaul Dagnelie (void) fprintf(stderr,
41215c653870SPaul Dagnelie gettext("invalid option '%c'\n"), optopt);
41225c653870SPaul Dagnelie } else {
41235c653870SPaul Dagnelie (void) fprintf(stderr,
41245c653870SPaul Dagnelie gettext("invalid option '%s'\n"),
41255c653870SPaul Dagnelie argv[optind - 1]);
41265c653870SPaul Dagnelie
41275c653870SPaul Dagnelie }
412899653d4eSeschrock usage(B_FALSE);
4129fa9e4066Sahrens }
4130fa9e4066Sahrens }
4131fa9e4066Sahrens
4132fa9e4066Sahrens argc -= optind;
4133fa9e4066Sahrens argv += optind;
4134fa9e4066Sahrens
41359c3fd121SMatthew Ahrens if (resume_token != NULL) {
41369c3fd121SMatthew Ahrens if (fromname != NULL || flags.replicate || flags.props ||
41376ccda740Sloli10K flags.backup || flags.dedup) {
41389c3fd121SMatthew Ahrens (void) fprintf(stderr,
41399c3fd121SMatthew Ahrens gettext("invalid flags combined with -t\n"));
41409c3fd121SMatthew Ahrens usage(B_FALSE);
41419c3fd121SMatthew Ahrens }
41429c3fd121SMatthew Ahrens if (argc != 0) {
41439c3fd121SMatthew Ahrens (void) fprintf(stderr, gettext("no additional "
41449c3fd121SMatthew Ahrens "arguments are permitted with -t\n"));
41459c3fd121SMatthew Ahrens usage(B_FALSE);
41469c3fd121SMatthew Ahrens }
41479c3fd121SMatthew Ahrens } else {
4148fa9e4066Sahrens if (argc < 1) {
41499c3fd121SMatthew Ahrens (void) fprintf(stderr,
41509c3fd121SMatthew Ahrens gettext("missing snapshot argument\n"));
415199653d4eSeschrock usage(B_FALSE);
4152fa9e4066Sahrens }
4153fa9e4066Sahrens if (argc > 1) {
4154fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
415599653d4eSeschrock usage(B_FALSE);
4156fa9e4066Sahrens }
41579c3fd121SMatthew Ahrens }
4158fa9e4066Sahrens
415919b94df9SMatthew Ahrens if (!flags.dryrun && isatty(STDOUT_FILENO)) {
4160fa9e4066Sahrens (void) fprintf(stderr,
4161a2eea2e1Sahrens gettext("Error: Stream can not be written to a terminal.\n"
4162fa9e4066Sahrens "You must redirect standard output.\n"));
4163fa9e4066Sahrens return (1);
4164fa9e4066Sahrens }
4165fa9e4066Sahrens
41669c3fd121SMatthew Ahrens if (resume_token != NULL) {
41679c3fd121SMatthew Ahrens return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
41689c3fd121SMatthew Ahrens resume_token));
41699c3fd121SMatthew Ahrens }
41709c3fd121SMatthew Ahrens
417178f17100SMatthew Ahrens /*
417278f17100SMatthew Ahrens * Special case sending a filesystem, or from a bookmark.
417378f17100SMatthew Ahrens */
417478f17100SMatthew Ahrens if (strchr(argv[0], '@') == NULL ||
417578f17100SMatthew Ahrens (fromname && strchr(fromname, '#') != NULL)) {
41769adfa60dSMatthew Ahrens char frombuf[ZFS_MAX_DATASET_NAME_LEN];
41775d7b4d43SMatthew Ahrens enum lzc_send_flags lzc_flags = 0;
417878f17100SMatthew Ahrens
417978f17100SMatthew Ahrens if (flags.replicate || flags.doall || flags.props ||
41806ccda740Sloli10K flags.backup || flags.dedup || flags.holds ||
41816ccda740Sloli10K flags.dryrun || flags.verbose || flags.progress) {
41823cb34c60Sahrens (void) fprintf(stderr,
418378f17100SMatthew Ahrens gettext("Error: "
418478f17100SMatthew Ahrens "Unsupported flag with filesystem or bookmark.\n"));
418578f17100SMatthew Ahrens return (1);
41863cb34c60Sahrens }
418778f17100SMatthew Ahrens
418878f17100SMatthew Ahrens zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
418978f17100SMatthew Ahrens if (zhp == NULL)
419078f17100SMatthew Ahrens return (1);
419178f17100SMatthew Ahrens
4192b5152584SMatthew Ahrens if (flags.largeblock)
4193b5152584SMatthew Ahrens lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
41945d7b4d43SMatthew Ahrens if (flags.embed_data)
41955d7b4d43SMatthew Ahrens lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
41965602294fSDan Kimmel if (flags.compress)
41975602294fSDan Kimmel lzc_flags |= LZC_SEND_FLAG_COMPRESS;
4198eb633035STom Caputi if (flags.raw)
4199eb633035STom Caputi lzc_flags |= LZC_SEND_FLAG_RAW;
42005d7b4d43SMatthew Ahrens
420178f17100SMatthew Ahrens if (fromname != NULL &&
420278f17100SMatthew Ahrens (fromname[0] == '#' || fromname[0] == '@')) {
420378f17100SMatthew Ahrens /*
420478f17100SMatthew Ahrens * Incremental source name begins with # or @.
420578f17100SMatthew Ahrens * Default to same fs as target.
420678f17100SMatthew Ahrens */
42073382f241Sluozhengzheng (void) strlcpy(frombuf, argv[0], sizeof (frombuf));
420878f17100SMatthew Ahrens cp = strchr(frombuf, '@');
420978f17100SMatthew Ahrens if (cp != NULL)
421078f17100SMatthew Ahrens *cp = '\0';
421178f17100SMatthew Ahrens (void) strlcat(frombuf, fromname, sizeof (frombuf));
421278f17100SMatthew Ahrens fromname = frombuf;
421378f17100SMatthew Ahrens }
42145d7b4d43SMatthew Ahrens err = zfs_send_one(zhp, fromname, STDOUT_FILENO, lzc_flags);
421578f17100SMatthew Ahrens zfs_close(zhp);
421678f17100SMatthew Ahrens return (err != 0);
421778f17100SMatthew Ahrens }
421878f17100SMatthew Ahrens
421978f17100SMatthew Ahrens cp = strchr(argv[0], '@');
42203cb34c60Sahrens *cp = '\0';
42213cb34c60Sahrens toname = cp + 1;
42223cb34c60Sahrens zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
42233cb34c60Sahrens if (zhp == NULL)
422498579b20Snd150628 return (1);
422598579b20Snd150628
422698579b20Snd150628 /*
4227a2eea2e1Sahrens * If they specified the full path to the snapshot, chop off
42283cb34c60Sahrens * everything except the short name of the snapshot, but special
42293cb34c60Sahrens * case if they specify the origin.
423098579b20Snd150628 */
4231a2eea2e1Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) {
42329adfa60dSMatthew Ahrens char origin[ZFS_MAX_DATASET_NAME_LEN];
42333cb34c60Sahrens zprop_source_t src;
42343cb34c60Sahrens
42353cb34c60Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
42363cb34c60Sahrens origin, sizeof (origin), &src, NULL, 0, B_FALSE);
42373cb34c60Sahrens
42383cb34c60Sahrens if (strcmp(origin, fromname) == 0) {
42393cb34c60Sahrens fromname = NULL;
42409e69d7d0SLori Alt flags.fromorigin = B_TRUE;
42413cb34c60Sahrens } else {
42423cb34c60Sahrens *cp = '\0';
42433cb34c60Sahrens if (cp != fromname && strcmp(argv[0], fromname)) {
4244a2eea2e1Sahrens (void) fprintf(stderr,
4245a2eea2e1Sahrens gettext("incremental source must be "
4246a2eea2e1Sahrens "in same filesystem\n"));
4247a2eea2e1Sahrens usage(B_FALSE);
4248a2eea2e1Sahrens }
4249a2eea2e1Sahrens fromname = cp + 1;
4250a2eea2e1Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) {
4251a2eea2e1Sahrens (void) fprintf(stderr,
4252a2eea2e1Sahrens gettext("invalid incremental source\n"));
4253a2eea2e1Sahrens usage(B_FALSE);
4254a2eea2e1Sahrens }
425598579b20Snd150628 }
42563cb34c60Sahrens }
425798579b20Snd150628
42589e69d7d0SLori Alt if (flags.replicate && fromname == NULL)
42599e69d7d0SLori Alt flags.doall = B_TRUE;
42603cb34c60Sahrens
426119b94df9SMatthew Ahrens err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
42623f9d6ad7SLin Ling extraverbose ? &dbgnv : NULL);
42633f9d6ad7SLin Ling
426419b94df9SMatthew Ahrens if (extraverbose && dbgnv != NULL) {
42653f9d6ad7SLin Ling /*
42663f9d6ad7SLin Ling * dump_nvlist prints to stdout, but that's been
42673f9d6ad7SLin Ling * redirected to a file. Make it print to stderr
42683f9d6ad7SLin Ling * instead.
42693f9d6ad7SLin Ling */
42703f9d6ad7SLin Ling (void) dup2(STDERR_FILENO, STDOUT_FILENO);
42713f9d6ad7SLin Ling dump_nvlist(dbgnv, 0);
42723f9d6ad7SLin Ling nvlist_free(dbgnv);
42733f9d6ad7SLin Ling }
4274a2eea2e1Sahrens zfs_close(zhp);
4275fa9e4066Sahrens
4276fa9e4066Sahrens return (err != 0);
4277fa9e4066Sahrens }
4278fa9e4066Sahrens
4279fa9e4066Sahrens /*
4280fa9e4066Sahrens * Restore a backup stream from stdin.
4281fa9e4066Sahrens */
4282fa9e4066Sahrens static int
zfs_do_receive(int argc,char ** argv)4283f2a3c691Sahrens zfs_do_receive(int argc, char **argv)
4284fa9e4066Sahrens {
4285c16bcc45SIgor Kozhukhov int c, err = 0;
428692241e0bSTom Erickson recvflags_t flags = { 0 };
42879c3fd121SMatthew Ahrens boolean_t abort_resumable = B_FALSE;
4288a2cdcdd2SPaul Dagnelie nvlist_t *props;
4289a2cdcdd2SPaul Dagnelie
4290a2cdcdd2SPaul Dagnelie if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
4291a2cdcdd2SPaul Dagnelie nomem();
4292fa9e4066Sahrens
4293fa9e4066Sahrens /* check options */
42946ccda740Sloli10K while ((c = getopt(argc, argv, ":o:x:dehnuvFsA")) != -1) {
4295fa9e4066Sahrens switch (c) {
4296a2cdcdd2SPaul Dagnelie case 'o':
42976ccda740Sloli10K if (!parseprop(props, optarg)) {
42986ccda740Sloli10K nvlist_free(props);
42996ccda740Sloli10K usage(B_FALSE);
43006ccda740Sloli10K }
43016ccda740Sloli10K break;
43026ccda740Sloli10K case 'x':
43036ccda740Sloli10K if (!parsepropname(props, optarg)) {
43046ccda740Sloli10K nvlist_free(props);
43056ccda740Sloli10K usage(B_FALSE);
43066ccda740Sloli10K }
4307a2cdcdd2SPaul Dagnelie break;
4308fa9e4066Sahrens case 'd':
43093cb34c60Sahrens flags.isprefix = B_TRUE;
4310fa9e4066Sahrens break;
4311f64930f5STom Erickson case 'e':
4312f64930f5STom Erickson flags.isprefix = B_TRUE;
4313f64930f5STom Erickson flags.istail = B_TRUE;
4314f64930f5STom Erickson break;
43156ccda740Sloli10K case 'h':
43166ccda740Sloli10K flags.skipholds = B_TRUE;
43176ccda740Sloli10K break;
4318fa9e4066Sahrens case 'n':
43193cb34c60Sahrens flags.dryrun = B_TRUE;
4320fa9e4066Sahrens break;
432133408eefSLori Alt case 'u':
432233408eefSLori Alt flags.nomount = B_TRUE;
432333408eefSLori Alt break;
4324fa9e4066Sahrens case 'v':
43253cb34c60Sahrens flags.verbose = B_TRUE;
4326fa9e4066Sahrens break;
43279c3fd121SMatthew Ahrens case 's':
43289c3fd121SMatthew Ahrens flags.resumable = B_TRUE;
43299c3fd121SMatthew Ahrens break;
433098579b20Snd150628 case 'F':
43313cb34c60Sahrens flags.force = B_TRUE;
433298579b20Snd150628 break;
43339c3fd121SMatthew Ahrens case 'A':
43349c3fd121SMatthew Ahrens abort_resumable = B_TRUE;
43359c3fd121SMatthew Ahrens break;
4336fa9e4066Sahrens case ':':
4337fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for "
4338fa9e4066Sahrens "'%c' option\n"), optopt);
433999653d4eSeschrock usage(B_FALSE);
4340fa9e4066Sahrens break;
4341fa9e4066Sahrens case '?':
4342fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
4343fa9e4066Sahrens optopt);
434499653d4eSeschrock usage(B_FALSE);
4345fa9e4066Sahrens }
4346fa9e4066Sahrens }
4347fa9e4066Sahrens
4348fa9e4066Sahrens argc -= optind;
4349fa9e4066Sahrens argv += optind;
4350fa9e4066Sahrens
4351fa9e4066Sahrens /* check number of arguments */
4352fa9e4066Sahrens if (argc < 1) {
4353fa9e4066Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n"));
435499653d4eSeschrock usage(B_FALSE);
4355fa9e4066Sahrens }
4356fa9e4066Sahrens if (argc > 1) {
4357fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
435899653d4eSeschrock usage(B_FALSE);
4359fa9e4066Sahrens }
4360fa9e4066Sahrens
43619c3fd121SMatthew Ahrens if (abort_resumable) {
43629c3fd121SMatthew Ahrens if (flags.isprefix || flags.istail || flags.dryrun ||
43639c3fd121SMatthew Ahrens flags.resumable || flags.nomount) {
43649c3fd121SMatthew Ahrens (void) fprintf(stderr, gettext("invalid option"));
43659c3fd121SMatthew Ahrens usage(B_FALSE);
43669c3fd121SMatthew Ahrens }
43679c3fd121SMatthew Ahrens
43689adfa60dSMatthew Ahrens char namebuf[ZFS_MAX_DATASET_NAME_LEN];
43699c3fd121SMatthew Ahrens (void) snprintf(namebuf, sizeof (namebuf),
43709c3fd121SMatthew Ahrens "%s/%%recv", argv[0]);
43719c3fd121SMatthew Ahrens
43729c3fd121SMatthew Ahrens if (zfs_dataset_exists(g_zfs, namebuf,
43739c3fd121SMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
43749c3fd121SMatthew Ahrens zfs_handle_t *zhp = zfs_open(g_zfs,
43759c3fd121SMatthew Ahrens namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
43769c3fd121SMatthew Ahrens if (zhp == NULL)
43779c3fd121SMatthew Ahrens return (1);
43789c3fd121SMatthew Ahrens err = zfs_destroy(zhp, B_FALSE);
43799c3fd121SMatthew Ahrens } else {
43809c3fd121SMatthew Ahrens zfs_handle_t *zhp = zfs_open(g_zfs,
43819c3fd121SMatthew Ahrens argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
43829c3fd121SMatthew Ahrens if (zhp == NULL)
43839c3fd121SMatthew Ahrens usage(B_FALSE);
43849c3fd121SMatthew Ahrens if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
43859c3fd121SMatthew Ahrens zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
43869c3fd121SMatthew Ahrens NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
43879c3fd121SMatthew Ahrens (void) fprintf(stderr,
43889c3fd121SMatthew Ahrens gettext("'%s' does not have any "
43899c3fd121SMatthew Ahrens "resumable receive state to abort\n"),
43909c3fd121SMatthew Ahrens argv[0]);
43919c3fd121SMatthew Ahrens return (1);
43929c3fd121SMatthew Ahrens }
43939c3fd121SMatthew Ahrens err = zfs_destroy(zhp, B_FALSE);
43949c3fd121SMatthew Ahrens }
43959c3fd121SMatthew Ahrens
43969c3fd121SMatthew Ahrens return (err != 0);
43979c3fd121SMatthew Ahrens }
43989c3fd121SMatthew Ahrens
4399fa9e4066Sahrens if (isatty(STDIN_FILENO)) {
4400fa9e4066Sahrens (void) fprintf(stderr,
4401fa9e4066Sahrens gettext("Error: Backup stream can not be read "
4402fa9e4066Sahrens "from a terminal.\n"
4403fa9e4066Sahrens "You must redirect standard input.\n"));
4404fa9e4066Sahrens return (1);
4405fa9e4066Sahrens }
4406a2cdcdd2SPaul Dagnelie err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
440706eeb2adSek110237
4408ecd6cf80Smarks return (err != 0);
440906eeb2adSek110237 }
441006eeb2adSek110237
44111af68beaSAlexander Stetsenko /*
44121af68beaSAlexander Stetsenko * allow/unallow stuff
44131af68beaSAlexander Stetsenko */
44141af68beaSAlexander Stetsenko /* copied from zfs/sys/dsl_deleg.h */
44151af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_CREATE "create"
44161af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_DESTROY "destroy"
44171af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
44181af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_ROLLBACK "rollback"
44191af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_CLONE "clone"
44201af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_PROMOTE "promote"
44211af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_RENAME "rename"
44221af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_MOUNT "mount"
44231af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_SHARE "share"
44241af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_SEND "send"
44251af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_RECEIVE "receive"
44261af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_ALLOW "allow"
44271af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_USERPROP "userprop"
44281af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */
44291af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_USERQUOTA "userquota"
44301af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
44311af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_USERUSED "userused"
44321af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_GROUPUSED "groupused"
4433f67950b2SNasf-Fan #define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota"
4434f67950b2SNasf-Fan #define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota"
4435f67950b2SNasf-Fan #define ZFS_DELEG_PERM_USEROBJUSED "userobjused"
4436f67950b2SNasf-Fan #define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused"
4437f67950b2SNasf-Fan
44381af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_HOLD "hold"
44391af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_RELEASE "release"
44401af68beaSAlexander Stetsenko #define ZFS_DELEG_PERM_DIFF "diff"
444178f17100SMatthew Ahrens #define ZFS_DELEG_PERM_BOOKMARK "bookmark"
44425cabbc6bSPrashanth Sreenivasa #define ZFS_DELEG_PERM_REMAP "remap"
4443eb633035STom Caputi #define ZFS_DELEG_PERM_LOAD_KEY "load-key"
4444eb633035STom Caputi #define ZFS_DELEG_PERM_CHANGE_KEY "change-key"
44451af68beaSAlexander Stetsenko
4446f67950b2SNasf-Fan #define ZFS_DELEG_PERM_PROJECTUSED "projectused"
4447f67950b2SNasf-Fan #define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota"
4448f67950b2SNasf-Fan #define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused"
4449f67950b2SNasf-Fan #define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota"
4450f67950b2SNasf-Fan
44511af68beaSAlexander Stetsenko #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
44521af68beaSAlexander Stetsenko
44531af68beaSAlexander Stetsenko static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
44541af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
44551af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
44561af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
44571af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
44581af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
44591af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
44601af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
44611af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
44621af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
44631af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
44641af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
44651af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
44661af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
44671af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
44681af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
446978f17100SMatthew Ahrens { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
44705cabbc6bSPrashanth Sreenivasa { ZFS_DELEG_PERM_REMAP, ZFS_DELEG_NOTE_REMAP },
4471eb633035STom Caputi { ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },
4472eb633035STom Caputi { ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },
44731af68beaSAlexander Stetsenko
44741af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
44751af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
44761af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
44771af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
44781af68beaSAlexander Stetsenko { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
4479f67950b2SNasf-Fan { ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },
4480f67950b2SNasf-Fan { ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },
4481f67950b2SNasf-Fan { ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },
4482f67950b2SNasf-Fan { ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },
4483f67950b2SNasf-Fan { ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },
4484f67950b2SNasf-Fan { ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },
4485f67950b2SNasf-Fan { ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },
4486f67950b2SNasf-Fan { ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },
44871af68beaSAlexander Stetsenko { NULL, ZFS_DELEG_NOTE_NONE }
44881af68beaSAlexander Stetsenko };
44891af68beaSAlexander Stetsenko
44901af68beaSAlexander Stetsenko /* permission structure */
44911af68beaSAlexander Stetsenko typedef struct deleg_perm {
44921af68beaSAlexander Stetsenko zfs_deleg_who_type_t dp_who_type;
44931af68beaSAlexander Stetsenko const char *dp_name;
44941af68beaSAlexander Stetsenko boolean_t dp_local;
44951af68beaSAlexander Stetsenko boolean_t dp_descend;
44961af68beaSAlexander Stetsenko } deleg_perm_t;
44971af68beaSAlexander Stetsenko
44981af68beaSAlexander Stetsenko /* */
44991af68beaSAlexander Stetsenko typedef struct deleg_perm_node {
45001af68beaSAlexander Stetsenko deleg_perm_t dpn_perm;
45011af68beaSAlexander Stetsenko
45021af68beaSAlexander Stetsenko uu_avl_node_t dpn_avl_node;
45031af68beaSAlexander Stetsenko } deleg_perm_node_t;
45041af68beaSAlexander Stetsenko
45051af68beaSAlexander Stetsenko typedef struct fs_perm fs_perm_t;
45061af68beaSAlexander Stetsenko
45071af68beaSAlexander Stetsenko /* permissions set */
45081af68beaSAlexander Stetsenko typedef struct who_perm {
45091af68beaSAlexander Stetsenko zfs_deleg_who_type_t who_type;
45101af68beaSAlexander Stetsenko const char *who_name; /* id */
45111af68beaSAlexander Stetsenko char who_ug_name[256]; /* user/group name */
45121af68beaSAlexander Stetsenko fs_perm_t *who_fsperm; /* uplink */
45131af68beaSAlexander Stetsenko
45141af68beaSAlexander Stetsenko uu_avl_t *who_deleg_perm_avl; /* permissions */
45151af68beaSAlexander Stetsenko } who_perm_t;
45161af68beaSAlexander Stetsenko
45171af68beaSAlexander Stetsenko /* */
45181af68beaSAlexander Stetsenko typedef struct who_perm_node {
45191af68beaSAlexander Stetsenko who_perm_t who_perm;
45201af68beaSAlexander Stetsenko uu_avl_node_t who_avl_node;
45211af68beaSAlexander Stetsenko } who_perm_node_t;
45221af68beaSAlexander Stetsenko
45231af68beaSAlexander Stetsenko typedef struct fs_perm_set fs_perm_set_t;
45241af68beaSAlexander Stetsenko /* fs permissions */
45251af68beaSAlexander Stetsenko struct fs_perm {
45261af68beaSAlexander Stetsenko const char *fsp_name;
45271af68beaSAlexander Stetsenko
45281af68beaSAlexander Stetsenko uu_avl_t *fsp_sc_avl; /* sets,create */
45291af68beaSAlexander Stetsenko uu_avl_t *fsp_uge_avl; /* user,group,everyone */
45301af68beaSAlexander Stetsenko
45311af68beaSAlexander Stetsenko fs_perm_set_t *fsp_set; /* uplink */
45321af68beaSAlexander Stetsenko };
45331af68beaSAlexander Stetsenko
45341af68beaSAlexander Stetsenko /* */
45351af68beaSAlexander Stetsenko typedef struct fs_perm_node {
45361af68beaSAlexander Stetsenko fs_perm_t fspn_fsperm;
45371af68beaSAlexander Stetsenko uu_avl_t *fspn_avl;
45381af68beaSAlexander Stetsenko
45391af68beaSAlexander Stetsenko uu_list_node_t fspn_list_node;
45401af68beaSAlexander Stetsenko } fs_perm_node_t;
45411af68beaSAlexander Stetsenko
45421af68beaSAlexander Stetsenko /* top level structure */
45431af68beaSAlexander Stetsenko struct fs_perm_set {
45441af68beaSAlexander Stetsenko uu_list_pool_t *fsps_list_pool;
45451af68beaSAlexander Stetsenko uu_list_t *fsps_list; /* list of fs_perms */
45461af68beaSAlexander Stetsenko
45471af68beaSAlexander Stetsenko uu_avl_pool_t *fsps_named_set_avl_pool;
45481af68beaSAlexander Stetsenko uu_avl_pool_t *fsps_who_perm_avl_pool;
45491af68beaSAlexander Stetsenko uu_avl_pool_t *fsps_deleg_perm_avl_pool;
45501af68beaSAlexander Stetsenko };
45511af68beaSAlexander Stetsenko
45521af68beaSAlexander Stetsenko static inline const char *
deleg_perm_type(zfs_deleg_note_t note)45531af68beaSAlexander Stetsenko deleg_perm_type(zfs_deleg_note_t note)
45541af68beaSAlexander Stetsenko {
45551af68beaSAlexander Stetsenko /* subcommands */
45561af68beaSAlexander Stetsenko switch (note) {
45571af68beaSAlexander Stetsenko /* SUBCOMMANDS */
45581af68beaSAlexander Stetsenko /* OTHER */
45591af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_GROUPQUOTA:
45601af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_GROUPUSED:
45611af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERPROP:
45621af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERQUOTA:
45631af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERUSED:
4564f67950b2SNasf-Fan case ZFS_DELEG_NOTE_USEROBJQUOTA:
4565f67950b2SNasf-Fan case ZFS_DELEG_NOTE_USEROBJUSED:
4566f67950b2SNasf-Fan case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
4567f67950b2SNasf-Fan case ZFS_DELEG_NOTE_GROUPOBJUSED:
4568f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTUSED:
4569f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTQUOTA:
4570f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTOBJUSED:
4571f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
45721af68beaSAlexander Stetsenko /* other */
45731af68beaSAlexander Stetsenko return (gettext("other"));
45741af68beaSAlexander Stetsenko default:
45751af68beaSAlexander Stetsenko return (gettext("subcommand"));
45761af68beaSAlexander Stetsenko }
45771af68beaSAlexander Stetsenko }
45781af68beaSAlexander Stetsenko
4579c16bcc45SIgor Kozhukhov static int
who_type2weight(zfs_deleg_who_type_t who_type)45801af68beaSAlexander Stetsenko who_type2weight(zfs_deleg_who_type_t who_type)
45811af68beaSAlexander Stetsenko {
45821af68beaSAlexander Stetsenko int res;
45831af68beaSAlexander Stetsenko switch (who_type) {
45841af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET_SETS:
45851af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET:
45861af68beaSAlexander Stetsenko res = 0;
45871af68beaSAlexander Stetsenko break;
45881af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE_SETS:
45891af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE:
45901af68beaSAlexander Stetsenko res = 1;
45911af68beaSAlexander Stetsenko break;
45921af68beaSAlexander Stetsenko case ZFS_DELEG_USER_SETS:
45931af68beaSAlexander Stetsenko case ZFS_DELEG_USER:
45941af68beaSAlexander Stetsenko res = 2;
45951af68beaSAlexander Stetsenko break;
45961af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP_SETS:
45971af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP:
45981af68beaSAlexander Stetsenko res = 3;
45991af68beaSAlexander Stetsenko break;
46001af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE_SETS:
46011af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE:
46021af68beaSAlexander Stetsenko res = 4;
46031af68beaSAlexander Stetsenko break;
46041af68beaSAlexander Stetsenko default:
46051af68beaSAlexander Stetsenko res = -1;
46061af68beaSAlexander Stetsenko }
46071af68beaSAlexander Stetsenko
46081af68beaSAlexander Stetsenko return (res);
46091af68beaSAlexander Stetsenko }
46101af68beaSAlexander Stetsenko
46111af68beaSAlexander Stetsenko /* ARGSUSED */
46121af68beaSAlexander Stetsenko static int
who_perm_compare(const void * larg,const void * rarg,void * unused)46131af68beaSAlexander Stetsenko who_perm_compare(const void *larg, const void *rarg, void *unused)
46141af68beaSAlexander Stetsenko {
46151af68beaSAlexander Stetsenko const who_perm_node_t *l = larg;
46161af68beaSAlexander Stetsenko const who_perm_node_t *r = rarg;
46171af68beaSAlexander Stetsenko zfs_deleg_who_type_t ltype = l->who_perm.who_type;
46181af68beaSAlexander Stetsenko zfs_deleg_who_type_t rtype = r->who_perm.who_type;
46191af68beaSAlexander Stetsenko int lweight = who_type2weight(ltype);
46201af68beaSAlexander Stetsenko int rweight = who_type2weight(rtype);
46211af68beaSAlexander Stetsenko int res = lweight - rweight;
46221af68beaSAlexander Stetsenko if (res == 0)
46231af68beaSAlexander Stetsenko res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
46241af68beaSAlexander Stetsenko ZFS_MAX_DELEG_NAME-1);
46251af68beaSAlexander Stetsenko
46261af68beaSAlexander Stetsenko if (res == 0)
46271af68beaSAlexander Stetsenko return (0);
46281af68beaSAlexander Stetsenko if (res > 0)
46291af68beaSAlexander Stetsenko return (1);
46301af68beaSAlexander Stetsenko else
46311af68beaSAlexander Stetsenko return (-1);
46321af68beaSAlexander Stetsenko }
46331af68beaSAlexander Stetsenko
46341af68beaSAlexander Stetsenko /* ARGSUSED */
46351af68beaSAlexander Stetsenko static int
deleg_perm_compare(const void * larg,const void * rarg,void * unused)46361af68beaSAlexander Stetsenko deleg_perm_compare(const void *larg, const void *rarg, void *unused)
46371af68beaSAlexander Stetsenko {
46381af68beaSAlexander Stetsenko const deleg_perm_node_t *l = larg;
46391af68beaSAlexander Stetsenko const deleg_perm_node_t *r = rarg;
46401af68beaSAlexander Stetsenko int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
46411af68beaSAlexander Stetsenko ZFS_MAX_DELEG_NAME-1);
46421af68beaSAlexander Stetsenko
46431af68beaSAlexander Stetsenko if (res == 0)
46441af68beaSAlexander Stetsenko return (0);
46451af68beaSAlexander Stetsenko
46461af68beaSAlexander Stetsenko if (res > 0)
46471af68beaSAlexander Stetsenko return (1);
46481af68beaSAlexander Stetsenko else
46491af68beaSAlexander Stetsenko return (-1);
46501af68beaSAlexander Stetsenko }
46511af68beaSAlexander Stetsenko
46521af68beaSAlexander Stetsenko static inline void
fs_perm_set_init(fs_perm_set_t * fspset)46531af68beaSAlexander Stetsenko fs_perm_set_init(fs_perm_set_t *fspset)
46541af68beaSAlexander Stetsenko {
46551af68beaSAlexander Stetsenko bzero(fspset, sizeof (fs_perm_set_t));
46561af68beaSAlexander Stetsenko
46571af68beaSAlexander Stetsenko if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
46581af68beaSAlexander Stetsenko sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
46591af68beaSAlexander Stetsenko NULL, UU_DEFAULT)) == NULL)
46601af68beaSAlexander Stetsenko nomem();
46611af68beaSAlexander Stetsenko if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
46621af68beaSAlexander Stetsenko UU_DEFAULT)) == NULL)
46631af68beaSAlexander Stetsenko nomem();
46641af68beaSAlexander Stetsenko
46651af68beaSAlexander Stetsenko if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
46661af68beaSAlexander Stetsenko "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
46671af68beaSAlexander Stetsenko who_perm_node_t, who_avl_node), who_perm_compare,
46681af68beaSAlexander Stetsenko UU_DEFAULT)) == NULL)
46691af68beaSAlexander Stetsenko nomem();
46701af68beaSAlexander Stetsenko
46711af68beaSAlexander Stetsenko if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
46721af68beaSAlexander Stetsenko "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
46731af68beaSAlexander Stetsenko who_perm_node_t, who_avl_node), who_perm_compare,
46741af68beaSAlexander Stetsenko UU_DEFAULT)) == NULL)
46751af68beaSAlexander Stetsenko nomem();
46761af68beaSAlexander Stetsenko
46771af68beaSAlexander Stetsenko if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
46781af68beaSAlexander Stetsenko "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
46791af68beaSAlexander Stetsenko deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
46801af68beaSAlexander Stetsenko == NULL)
46811af68beaSAlexander Stetsenko nomem();
46821af68beaSAlexander Stetsenko }
46831af68beaSAlexander Stetsenko
46841af68beaSAlexander Stetsenko static inline void fs_perm_fini(fs_perm_t *);
46851af68beaSAlexander Stetsenko static inline void who_perm_fini(who_perm_t *);
46861af68beaSAlexander Stetsenko
46871af68beaSAlexander Stetsenko static inline void
fs_perm_set_fini(fs_perm_set_t * fspset)46881af68beaSAlexander Stetsenko fs_perm_set_fini(fs_perm_set_t *fspset)
46891af68beaSAlexander Stetsenko {
46901af68beaSAlexander Stetsenko fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
46911af68beaSAlexander Stetsenko
46921af68beaSAlexander Stetsenko while (node != NULL) {
46931af68beaSAlexander Stetsenko fs_perm_node_t *next_node =
46941af68beaSAlexander Stetsenko uu_list_next(fspset->fsps_list, node);
46951af68beaSAlexander Stetsenko fs_perm_t *fsperm = &node->fspn_fsperm;
46961af68beaSAlexander Stetsenko fs_perm_fini(fsperm);
46971af68beaSAlexander Stetsenko uu_list_remove(fspset->fsps_list, node);
46981af68beaSAlexander Stetsenko free(node);
46991af68beaSAlexander Stetsenko node = next_node;
47001af68beaSAlexander Stetsenko }
47011af68beaSAlexander Stetsenko
47021af68beaSAlexander Stetsenko uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
47031af68beaSAlexander Stetsenko uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
47041af68beaSAlexander Stetsenko uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
47051af68beaSAlexander Stetsenko }
47061af68beaSAlexander Stetsenko
47071af68beaSAlexander Stetsenko static inline void
deleg_perm_init(deleg_perm_t * deleg_perm,zfs_deleg_who_type_t type,const char * name)47081af68beaSAlexander Stetsenko deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
47091af68beaSAlexander Stetsenko const char *name)
47101af68beaSAlexander Stetsenko {
47111af68beaSAlexander Stetsenko deleg_perm->dp_who_type = type;
47121af68beaSAlexander Stetsenko deleg_perm->dp_name = name;
47131af68beaSAlexander Stetsenko }
47141af68beaSAlexander Stetsenko
47151af68beaSAlexander Stetsenko static inline void
who_perm_init(who_perm_t * who_perm,fs_perm_t * fsperm,zfs_deleg_who_type_t type,const char * name)47161af68beaSAlexander Stetsenko who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
47171af68beaSAlexander Stetsenko zfs_deleg_who_type_t type, const char *name)
47181af68beaSAlexander Stetsenko {
47191af68beaSAlexander Stetsenko uu_avl_pool_t *pool;
47201af68beaSAlexander Stetsenko pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
47211af68beaSAlexander Stetsenko
47221af68beaSAlexander Stetsenko bzero(who_perm, sizeof (who_perm_t));
47231af68beaSAlexander Stetsenko
47241af68beaSAlexander Stetsenko if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
47251af68beaSAlexander Stetsenko UU_DEFAULT)) == NULL)
47261af68beaSAlexander Stetsenko nomem();
47271af68beaSAlexander Stetsenko
47281af68beaSAlexander Stetsenko who_perm->who_type = type;
47291af68beaSAlexander Stetsenko who_perm->who_name = name;
47301af68beaSAlexander Stetsenko who_perm->who_fsperm = fsperm;
47311af68beaSAlexander Stetsenko }
47321af68beaSAlexander Stetsenko
47331af68beaSAlexander Stetsenko static inline void
who_perm_fini(who_perm_t * who_perm)47341af68beaSAlexander Stetsenko who_perm_fini(who_perm_t *who_perm)
47351af68beaSAlexander Stetsenko {
47361af68beaSAlexander Stetsenko deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
47371af68beaSAlexander Stetsenko
47381af68beaSAlexander Stetsenko while (node != NULL) {
47391af68beaSAlexander Stetsenko deleg_perm_node_t *next_node =
47401af68beaSAlexander Stetsenko uu_avl_next(who_perm->who_deleg_perm_avl, node);
47411af68beaSAlexander Stetsenko
47421af68beaSAlexander Stetsenko uu_avl_remove(who_perm->who_deleg_perm_avl, node);
47431af68beaSAlexander Stetsenko free(node);
47441af68beaSAlexander Stetsenko node = next_node;
47451af68beaSAlexander Stetsenko }
47461af68beaSAlexander Stetsenko
47471af68beaSAlexander Stetsenko uu_avl_destroy(who_perm->who_deleg_perm_avl);
47481af68beaSAlexander Stetsenko }
47491af68beaSAlexander Stetsenko
47501af68beaSAlexander Stetsenko static inline void
fs_perm_init(fs_perm_t * fsperm,fs_perm_set_t * fspset,const char * fsname)47511af68beaSAlexander Stetsenko fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
47521af68beaSAlexander Stetsenko {
47531af68beaSAlexander Stetsenko uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;
47541af68beaSAlexander Stetsenko uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;
47551af68beaSAlexander Stetsenko
47561af68beaSAlexander Stetsenko bzero(fsperm, sizeof (fs_perm_t));
47571af68beaSAlexander Stetsenko
47581af68beaSAlexander Stetsenko if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
47591af68beaSAlexander Stetsenko == NULL)
47601af68beaSAlexander Stetsenko nomem();
47611af68beaSAlexander Stetsenko
47621af68beaSAlexander Stetsenko if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
47631af68beaSAlexander Stetsenko == NULL)
47641af68beaSAlexander Stetsenko nomem();
47651af68beaSAlexander Stetsenko
47661af68beaSAlexander Stetsenko fsperm->fsp_set = fspset;
47671af68beaSAlexander Stetsenko fsperm->fsp_name = fsname;
47681af68beaSAlexander Stetsenko }
47691af68beaSAlexander Stetsenko
47701af68beaSAlexander Stetsenko static inline void
fs_perm_fini(fs_perm_t * fsperm)47711af68beaSAlexander Stetsenko fs_perm_fini(fs_perm_t *fsperm)
47721af68beaSAlexander Stetsenko {
47731af68beaSAlexander Stetsenko who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
47741af68beaSAlexander Stetsenko while (node != NULL) {
47751af68beaSAlexander Stetsenko who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
47761af68beaSAlexander Stetsenko node);
47771af68beaSAlexander Stetsenko who_perm_t *who_perm = &node->who_perm;
47781af68beaSAlexander Stetsenko who_perm_fini(who_perm);
47791af68beaSAlexander Stetsenko uu_avl_remove(fsperm->fsp_sc_avl, node);
47801af68beaSAlexander Stetsenko free(node);
47811af68beaSAlexander Stetsenko node = next_node;
47821af68beaSAlexander Stetsenko }
47831af68beaSAlexander Stetsenko
47841af68beaSAlexander Stetsenko node = uu_avl_first(fsperm->fsp_uge_avl);
47851af68beaSAlexander Stetsenko while (node != NULL) {
47861af68beaSAlexander Stetsenko who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
47871af68beaSAlexander Stetsenko node);
47881af68beaSAlexander Stetsenko who_perm_t *who_perm = &node->who_perm;
47891af68beaSAlexander Stetsenko who_perm_fini(who_perm);
47901af68beaSAlexander Stetsenko uu_avl_remove(fsperm->fsp_uge_avl, node);
47911af68beaSAlexander Stetsenko free(node);
47921af68beaSAlexander Stetsenko node = next_node;
47931af68beaSAlexander Stetsenko }
47941af68beaSAlexander Stetsenko
47951af68beaSAlexander Stetsenko uu_avl_destroy(fsperm->fsp_sc_avl);
47961af68beaSAlexander Stetsenko uu_avl_destroy(fsperm->fsp_uge_avl);
47971af68beaSAlexander Stetsenko }
47981af68beaSAlexander Stetsenko
4799c16bcc45SIgor Kozhukhov static void
set_deleg_perm_node(uu_avl_t * avl,deleg_perm_node_t * node,zfs_deleg_who_type_t who_type,const char * name,char locality)48001af68beaSAlexander Stetsenko set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
48011af68beaSAlexander Stetsenko zfs_deleg_who_type_t who_type, const char *name, char locality)
48021af68beaSAlexander Stetsenko {
48031af68beaSAlexander Stetsenko uu_avl_index_t idx = 0;
48041af68beaSAlexander Stetsenko
48051af68beaSAlexander Stetsenko deleg_perm_node_t *found_node = NULL;
48061af68beaSAlexander Stetsenko deleg_perm_t *deleg_perm = &node->dpn_perm;
48071af68beaSAlexander Stetsenko
48081af68beaSAlexander Stetsenko deleg_perm_init(deleg_perm, who_type, name);
48091af68beaSAlexander Stetsenko
48101af68beaSAlexander Stetsenko if ((found_node = uu_avl_find(avl, node, NULL, &idx))
48111af68beaSAlexander Stetsenko == NULL)
48121af68beaSAlexander Stetsenko uu_avl_insert(avl, node, idx);
48131af68beaSAlexander Stetsenko else {
48141af68beaSAlexander Stetsenko node = found_node;
48151af68beaSAlexander Stetsenko deleg_perm = &node->dpn_perm;
48161af68beaSAlexander Stetsenko }
48171af68beaSAlexander Stetsenko
48181af68beaSAlexander Stetsenko
48191af68beaSAlexander Stetsenko switch (locality) {
48201af68beaSAlexander Stetsenko case ZFS_DELEG_LOCAL:
48211af68beaSAlexander Stetsenko deleg_perm->dp_local = B_TRUE;
48221af68beaSAlexander Stetsenko break;
48231af68beaSAlexander Stetsenko case ZFS_DELEG_DESCENDENT:
48241af68beaSAlexander Stetsenko deleg_perm->dp_descend = B_TRUE;
48251af68beaSAlexander Stetsenko break;
48261af68beaSAlexander Stetsenko case ZFS_DELEG_NA:
48271af68beaSAlexander Stetsenko break;
48281af68beaSAlexander Stetsenko default:
48291af68beaSAlexander Stetsenko assert(B_FALSE); /* invalid locality */
48301af68beaSAlexander Stetsenko }
48311af68beaSAlexander Stetsenko }
48321af68beaSAlexander Stetsenko
48331af68beaSAlexander Stetsenko static inline int
parse_who_perm(who_perm_t * who_perm,nvlist_t * nvl,char locality)48341af68beaSAlexander Stetsenko parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
48351af68beaSAlexander Stetsenko {
48361af68beaSAlexander Stetsenko nvpair_t *nvp = NULL;
48371af68beaSAlexander Stetsenko fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
48381af68beaSAlexander Stetsenko uu_avl_t *avl = who_perm->who_deleg_perm_avl;
48391af68beaSAlexander Stetsenko zfs_deleg_who_type_t who_type = who_perm->who_type;
48401af68beaSAlexander Stetsenko
48411af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
48421af68beaSAlexander Stetsenko const char *name = nvpair_name(nvp);
48431af68beaSAlexander Stetsenko data_type_t type = nvpair_type(nvp);
48441af68beaSAlexander Stetsenko uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
48451af68beaSAlexander Stetsenko deleg_perm_node_t *node =
48461af68beaSAlexander Stetsenko safe_malloc(sizeof (deleg_perm_node_t));
48471af68beaSAlexander Stetsenko
48481af68beaSAlexander Stetsenko assert(type == DATA_TYPE_BOOLEAN);
48491af68beaSAlexander Stetsenko
48501af68beaSAlexander Stetsenko uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
48511af68beaSAlexander Stetsenko set_deleg_perm_node(avl, node, who_type, name, locality);
48521af68beaSAlexander Stetsenko }
48531af68beaSAlexander Stetsenko
48541af68beaSAlexander Stetsenko return (0);
48551af68beaSAlexander Stetsenko }
48561af68beaSAlexander Stetsenko
48571af68beaSAlexander Stetsenko static inline int
parse_fs_perm(fs_perm_t * fsperm,nvlist_t * nvl)48581af68beaSAlexander Stetsenko parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
48591af68beaSAlexander Stetsenko {
48601af68beaSAlexander Stetsenko nvpair_t *nvp = NULL;
48611af68beaSAlexander Stetsenko fs_perm_set_t *fspset = fsperm->fsp_set;
48621af68beaSAlexander Stetsenko
48631af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
48641af68beaSAlexander Stetsenko nvlist_t *nvl2 = NULL;
48651af68beaSAlexander Stetsenko const char *name = nvpair_name(nvp);
48661af68beaSAlexander Stetsenko uu_avl_t *avl = NULL;
4867c16bcc45SIgor Kozhukhov uu_avl_pool_t *avl_pool = NULL;
48681af68beaSAlexander Stetsenko zfs_deleg_who_type_t perm_type = name[0];
48691af68beaSAlexander Stetsenko char perm_locality = name[1];
48701af68beaSAlexander Stetsenko const char *perm_name = name + 3;
48711af68beaSAlexander Stetsenko boolean_t is_set = B_TRUE;
48721af68beaSAlexander Stetsenko who_perm_t *who_perm = NULL;
48731af68beaSAlexander Stetsenko
48741af68beaSAlexander Stetsenko assert('$' == name[2]);
48751af68beaSAlexander Stetsenko
48761af68beaSAlexander Stetsenko if (nvpair_value_nvlist(nvp, &nvl2) != 0)
48771af68beaSAlexander Stetsenko return (-1);
48781af68beaSAlexander Stetsenko
48791af68beaSAlexander Stetsenko switch (perm_type) {
48801af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE:
48811af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE_SETS:
48821af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET:
48831af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET_SETS:
48841af68beaSAlexander Stetsenko avl_pool = fspset->fsps_named_set_avl_pool;
48851af68beaSAlexander Stetsenko avl = fsperm->fsp_sc_avl;
48861af68beaSAlexander Stetsenko break;
48871af68beaSAlexander Stetsenko case ZFS_DELEG_USER:
48881af68beaSAlexander Stetsenko case ZFS_DELEG_USER_SETS:
48891af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP:
48901af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP_SETS:
48911af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE:
48921af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE_SETS:
48931af68beaSAlexander Stetsenko avl_pool = fspset->fsps_who_perm_avl_pool;
48941af68beaSAlexander Stetsenko avl = fsperm->fsp_uge_avl;
48951af68beaSAlexander Stetsenko break;
4896c16bcc45SIgor Kozhukhov
4897c16bcc45SIgor Kozhukhov default:
4898c16bcc45SIgor Kozhukhov assert(!"unhandled zfs_deleg_who_type_t");
48991af68beaSAlexander Stetsenko }
49001af68beaSAlexander Stetsenko
49011af68beaSAlexander Stetsenko if (is_set) {
49021af68beaSAlexander Stetsenko who_perm_node_t *found_node = NULL;
49031af68beaSAlexander Stetsenko who_perm_node_t *node = safe_malloc(
49041af68beaSAlexander Stetsenko sizeof (who_perm_node_t));
49051af68beaSAlexander Stetsenko who_perm = &node->who_perm;
49061af68beaSAlexander Stetsenko uu_avl_index_t idx = 0;
49071af68beaSAlexander Stetsenko
49081af68beaSAlexander Stetsenko uu_avl_node_init(node, &node->who_avl_node, avl_pool);
49091af68beaSAlexander Stetsenko who_perm_init(who_perm, fsperm, perm_type, perm_name);
49101af68beaSAlexander Stetsenko
49111af68beaSAlexander Stetsenko if ((found_node = uu_avl_find(avl, node, NULL, &idx))
49121af68beaSAlexander Stetsenko == NULL) {
49131af68beaSAlexander Stetsenko if (avl == fsperm->fsp_uge_avl) {
49141af68beaSAlexander Stetsenko uid_t rid = 0;
49151af68beaSAlexander Stetsenko struct passwd *p = NULL;
49161af68beaSAlexander Stetsenko struct group *g = NULL;
49171af68beaSAlexander Stetsenko const char *nice_name = NULL;
49181af68beaSAlexander Stetsenko
49191af68beaSAlexander Stetsenko switch (perm_type) {
49201af68beaSAlexander Stetsenko case ZFS_DELEG_USER_SETS:
49211af68beaSAlexander Stetsenko case ZFS_DELEG_USER:
49221af68beaSAlexander Stetsenko rid = atoi(perm_name);
49231af68beaSAlexander Stetsenko p = getpwuid(rid);
49241af68beaSAlexander Stetsenko if (p)
49251af68beaSAlexander Stetsenko nice_name = p->pw_name;
49261af68beaSAlexander Stetsenko break;
49271af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP_SETS:
49281af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP:
49291af68beaSAlexander Stetsenko rid = atoi(perm_name);
49301af68beaSAlexander Stetsenko g = getgrgid(rid);
49311af68beaSAlexander Stetsenko if (g)
49321af68beaSAlexander Stetsenko nice_name = g->gr_name;
49331af68beaSAlexander Stetsenko break;
4934c16bcc45SIgor Kozhukhov
4935c16bcc45SIgor Kozhukhov default:
4936c16bcc45SIgor Kozhukhov break;
49371af68beaSAlexander Stetsenko }
49381af68beaSAlexander Stetsenko
49391af68beaSAlexander Stetsenko if (nice_name != NULL)
49401af68beaSAlexander Stetsenko (void) strlcpy(
49411af68beaSAlexander Stetsenko node->who_perm.who_ug_name,
49421af68beaSAlexander Stetsenko nice_name, 256);
49431af68beaSAlexander Stetsenko }
49441af68beaSAlexander Stetsenko
49451af68beaSAlexander Stetsenko uu_avl_insert(avl, node, idx);
49461af68beaSAlexander Stetsenko } else {
49471af68beaSAlexander Stetsenko node = found_node;
49481af68beaSAlexander Stetsenko who_perm = &node->who_perm;
49491af68beaSAlexander Stetsenko }
49501af68beaSAlexander Stetsenko }
49511af68beaSAlexander Stetsenko
49521af68beaSAlexander Stetsenko (void) parse_who_perm(who_perm, nvl2, perm_locality);
49531af68beaSAlexander Stetsenko }
49541af68beaSAlexander Stetsenko
49551af68beaSAlexander Stetsenko return (0);
49561af68beaSAlexander Stetsenko }
49571af68beaSAlexander Stetsenko
49581af68beaSAlexander Stetsenko static inline int
parse_fs_perm_set(fs_perm_set_t * fspset,nvlist_t * nvl)49591af68beaSAlexander Stetsenko parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
49601af68beaSAlexander Stetsenko {
49611af68beaSAlexander Stetsenko nvpair_t *nvp = NULL;
49621af68beaSAlexander Stetsenko uu_avl_index_t idx = 0;
49631af68beaSAlexander Stetsenko
49641af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
49651af68beaSAlexander Stetsenko nvlist_t *nvl2 = NULL;
49661af68beaSAlexander Stetsenko const char *fsname = nvpair_name(nvp);
49671af68beaSAlexander Stetsenko data_type_t type = nvpair_type(nvp);
49681af68beaSAlexander Stetsenko fs_perm_t *fsperm = NULL;
49691af68beaSAlexander Stetsenko fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
49701af68beaSAlexander Stetsenko if (node == NULL)
49711af68beaSAlexander Stetsenko nomem();
49721af68beaSAlexander Stetsenko
49731af68beaSAlexander Stetsenko fsperm = &node->fspn_fsperm;
49741af68beaSAlexander Stetsenko
49751af68beaSAlexander Stetsenko assert(DATA_TYPE_NVLIST == type);
49761af68beaSAlexander Stetsenko
49771af68beaSAlexander Stetsenko uu_list_node_init(node, &node->fspn_list_node,
49781af68beaSAlexander Stetsenko fspset->fsps_list_pool);
49791af68beaSAlexander Stetsenko
49801af68beaSAlexander Stetsenko idx = uu_list_numnodes(fspset->fsps_list);
49811af68beaSAlexander Stetsenko fs_perm_init(fsperm, fspset, fsname);
49821af68beaSAlexander Stetsenko
49831af68beaSAlexander Stetsenko if (nvpair_value_nvlist(nvp, &nvl2) != 0)
49841af68beaSAlexander Stetsenko return (-1);
49851af68beaSAlexander Stetsenko
49861af68beaSAlexander Stetsenko (void) parse_fs_perm(fsperm, nvl2);
49871af68beaSAlexander Stetsenko
49881af68beaSAlexander Stetsenko uu_list_insert(fspset->fsps_list, node, idx);
49891af68beaSAlexander Stetsenko }
49901af68beaSAlexander Stetsenko
49911af68beaSAlexander Stetsenko return (0);
49921af68beaSAlexander Stetsenko }
49931af68beaSAlexander Stetsenko
49941af68beaSAlexander Stetsenko static inline const char *
deleg_perm_comment(zfs_deleg_note_t note)49951af68beaSAlexander Stetsenko deleg_perm_comment(zfs_deleg_note_t note)
49961af68beaSAlexander Stetsenko {
49971af68beaSAlexander Stetsenko const char *str = "";
49981af68beaSAlexander Stetsenko
49991af68beaSAlexander Stetsenko /* subcommands */
50001af68beaSAlexander Stetsenko switch (note) {
50011af68beaSAlexander Stetsenko /* SUBCOMMANDS */
50021af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_ALLOW:
50031af68beaSAlexander Stetsenko str = gettext("Must also have the permission that is being"
50041af68beaSAlexander Stetsenko "\n\t\t\t\tallowed");
50051af68beaSAlexander Stetsenko break;
50061af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_CLONE:
50071af68beaSAlexander Stetsenko str = gettext("Must also have the 'create' ability and 'mount'"
50081af68beaSAlexander Stetsenko "\n\t\t\t\tability in the origin file system");
50091af68beaSAlexander Stetsenko break;
50101af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_CREATE:
50111af68beaSAlexander Stetsenko str = gettext("Must also have the 'mount' ability");
50121af68beaSAlexander Stetsenko break;
50131af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_DESTROY:
50141af68beaSAlexander Stetsenko str = gettext("Must also have the 'mount' ability");
50151af68beaSAlexander Stetsenko break;
50161af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_DIFF:
50171af68beaSAlexander Stetsenko str = gettext("Allows lookup of paths within a dataset;"
50181af68beaSAlexander Stetsenko "\n\t\t\t\tgiven an object number. Ordinary users need this"
50191af68beaSAlexander Stetsenko "\n\t\t\t\tin order to use zfs diff");
50201af68beaSAlexander Stetsenko break;
50211af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_HOLD:
50221af68beaSAlexander Stetsenko str = gettext("Allows adding a user hold to a snapshot");
50231af68beaSAlexander Stetsenko break;
50241af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_MOUNT:
50251af68beaSAlexander Stetsenko str = gettext("Allows mount/umount of ZFS datasets");
50261af68beaSAlexander Stetsenko break;
50271af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_PROMOTE:
50281af68beaSAlexander Stetsenko str = gettext("Must also have the 'mount'\n\t\t\t\tand"
50291af68beaSAlexander Stetsenko " 'promote' ability in the origin file system");
50301af68beaSAlexander Stetsenko break;
50311af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_RECEIVE:
50321af68beaSAlexander Stetsenko str = gettext("Must also have the 'mount' and 'create'"
50331af68beaSAlexander Stetsenko " ability");
50341af68beaSAlexander Stetsenko break;
50351af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_RELEASE:
50361af68beaSAlexander Stetsenko str = gettext("Allows releasing a user hold which\n\t\t\t\t"
50371af68beaSAlexander Stetsenko "might destroy the snapshot");
50381af68beaSAlexander Stetsenko break;
50391af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_RENAME:
50401af68beaSAlexander Stetsenko str = gettext("Must also have the 'mount' and 'create'"
50411af68beaSAlexander Stetsenko "\n\t\t\t\tability in the new parent");
50421af68beaSAlexander Stetsenko break;
50431af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_ROLLBACK:
50441af68beaSAlexander Stetsenko str = gettext("");
50451af68beaSAlexander Stetsenko break;
50461af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_SEND:
50471af68beaSAlexander Stetsenko str = gettext("");
50481af68beaSAlexander Stetsenko break;
50491af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_SHARE:
50501af68beaSAlexander Stetsenko str = gettext("Allows sharing file systems over NFS or SMB"
50511af68beaSAlexander Stetsenko "\n\t\t\t\tprotocols");
50521af68beaSAlexander Stetsenko break;
50531af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_SNAPSHOT:
50541af68beaSAlexander Stetsenko str = gettext("");
50551af68beaSAlexander Stetsenko break;
5056eb633035STom Caputi case ZFS_DELEG_NOTE_LOAD_KEY:
5057eb633035STom Caputi str = gettext("Allows loading or unloading an encryption key");
5058eb633035STom Caputi break;
5059eb633035STom Caputi case ZFS_DELEG_NOTE_CHANGE_KEY:
5060eb633035STom Caputi str = gettext("Allows changing or adding an encryption key");
5061eb633035STom Caputi break;
50621af68beaSAlexander Stetsenko /*
50631af68beaSAlexander Stetsenko * case ZFS_DELEG_NOTE_VSCAN:
50641af68beaSAlexander Stetsenko * str = gettext("");
50651af68beaSAlexander Stetsenko * break;
50661af68beaSAlexander Stetsenko */
50671af68beaSAlexander Stetsenko /* OTHER */
50681af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_GROUPQUOTA:
50691af68beaSAlexander Stetsenko str = gettext("Allows accessing any groupquota@... property");
50701af68beaSAlexander Stetsenko break;
50711af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_GROUPUSED:
50721af68beaSAlexander Stetsenko str = gettext("Allows reading any groupused@... property");
50731af68beaSAlexander Stetsenko break;
50741af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERPROP:
50751af68beaSAlexander Stetsenko str = gettext("Allows changing any user property");
50761af68beaSAlexander Stetsenko break;
50771af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERQUOTA:
50781af68beaSAlexander Stetsenko str = gettext("Allows accessing any userquota@... property");
50791af68beaSAlexander Stetsenko break;
50801af68beaSAlexander Stetsenko case ZFS_DELEG_NOTE_USERUSED:
50811af68beaSAlexander Stetsenko str = gettext("Allows reading any userused@... property");
50821af68beaSAlexander Stetsenko break;
5083f67950b2SNasf-Fan case ZFS_DELEG_NOTE_USEROBJQUOTA:
5084f67950b2SNasf-Fan str = gettext("Allows accessing any userobjquota@... property");
5085f67950b2SNasf-Fan break;
5086f67950b2SNasf-Fan case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
5087f67950b2SNasf-Fan str = gettext("Allows accessing any \n\t\t\t\t"
5088f67950b2SNasf-Fan "groupobjquota@... property");
5089f67950b2SNasf-Fan break;
5090f67950b2SNasf-Fan case ZFS_DELEG_NOTE_GROUPOBJUSED:
5091f67950b2SNasf-Fan str = gettext("Allows reading any groupobjused@... property");
5092f67950b2SNasf-Fan break;
5093f67950b2SNasf-Fan case ZFS_DELEG_NOTE_USEROBJUSED:
5094f67950b2SNasf-Fan str = gettext("Allows reading any userobjused@... property");
5095f67950b2SNasf-Fan break;
5096f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTQUOTA:
5097f67950b2SNasf-Fan str = gettext("Allows accessing any projectquota@... property");
5098f67950b2SNasf-Fan break;
5099f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
5100f67950b2SNasf-Fan str = gettext("Allows accessing any \n\t\t\t\t"
5101f67950b2SNasf-Fan "projectobjquota@... property");
5102f67950b2SNasf-Fan break;
5103f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTUSED:
5104f67950b2SNasf-Fan str = gettext("Allows reading any projectused@... property");
5105f67950b2SNasf-Fan break;
5106f67950b2SNasf-Fan case ZFS_DELEG_NOTE_PROJECTOBJUSED:
5107f67950b2SNasf-Fan str = gettext("Allows accessing any \n\t\t\t\t"
5108f67950b2SNasf-Fan "projectobjused@... property");
5109f67950b2SNasf-Fan break;
51101af68beaSAlexander Stetsenko /* other */
51111af68beaSAlexander Stetsenko default:
51121af68beaSAlexander Stetsenko str = "";
51131af68beaSAlexander Stetsenko }
51141af68beaSAlexander Stetsenko
51151af68beaSAlexander Stetsenko return (str);
51161af68beaSAlexander Stetsenko }
51171af68beaSAlexander Stetsenko
51181af68beaSAlexander Stetsenko struct allow_opts {
51191af68beaSAlexander Stetsenko boolean_t local;
51201af68beaSAlexander Stetsenko boolean_t descend;
51211af68beaSAlexander Stetsenko boolean_t user;
51221af68beaSAlexander Stetsenko boolean_t group;
51231af68beaSAlexander Stetsenko boolean_t everyone;
51241af68beaSAlexander Stetsenko boolean_t create;
51251af68beaSAlexander Stetsenko boolean_t set;
51261af68beaSAlexander Stetsenko boolean_t recursive; /* unallow only */
51271af68beaSAlexander Stetsenko boolean_t prt_usage;
51281af68beaSAlexander Stetsenko
51291af68beaSAlexander Stetsenko boolean_t prt_perms;
51301af68beaSAlexander Stetsenko char *who;
51311af68beaSAlexander Stetsenko char *perms;
51321af68beaSAlexander Stetsenko const char *dataset;
51331af68beaSAlexander Stetsenko };
51341af68beaSAlexander Stetsenko
51351af68beaSAlexander Stetsenko static inline int
prop_cmp(const void * a,const void * b)51361af68beaSAlexander Stetsenko prop_cmp(const void *a, const void *b)
51371af68beaSAlexander Stetsenko {
51381af68beaSAlexander Stetsenko const char *str1 = *(const char **)a;
51391af68beaSAlexander Stetsenko const char *str2 = *(const char **)b;
51401af68beaSAlexander Stetsenko return (strcmp(str1, str2));
51411af68beaSAlexander Stetsenko }
51421af68beaSAlexander Stetsenko
51431af68beaSAlexander Stetsenko static void
allow_usage(boolean_t un,boolean_t requested,const char * msg)51441af68beaSAlexander Stetsenko allow_usage(boolean_t un, boolean_t requested, const char *msg)
51451af68beaSAlexander Stetsenko {
51461af68beaSAlexander Stetsenko const char *opt_desc[] = {
51471af68beaSAlexander Stetsenko "-h", gettext("show this help message and exit"),
51481af68beaSAlexander Stetsenko "-l", gettext("set permission locally"),
51491af68beaSAlexander Stetsenko "-d", gettext("set permission for descents"),
51501af68beaSAlexander Stetsenko "-u", gettext("set permission for user"),
51511af68beaSAlexander Stetsenko "-g", gettext("set permission for group"),
51521af68beaSAlexander Stetsenko "-e", gettext("set permission for everyone"),
51531af68beaSAlexander Stetsenko "-c", gettext("set create time permission"),
51541af68beaSAlexander Stetsenko "-s", gettext("define permission set"),
51551af68beaSAlexander Stetsenko /* unallow only */
51561af68beaSAlexander Stetsenko "-r", gettext("remove permissions recursively"),
51571af68beaSAlexander Stetsenko };
51581af68beaSAlexander Stetsenko size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
51591af68beaSAlexander Stetsenko size_t allow_size = unallow_size - 2;
51601af68beaSAlexander Stetsenko const char *props[ZFS_NUM_PROPS];
51611af68beaSAlexander Stetsenko int i;
51621af68beaSAlexander Stetsenko size_t count = 0;
51631af68beaSAlexander Stetsenko FILE *fp = requested ? stdout : stderr;
51641af68beaSAlexander Stetsenko zprop_desc_t *pdtbl = zfs_prop_get_table();
51651af68beaSAlexander Stetsenko const char *fmt = gettext("%-16s %-14s\t%s\n");
51661af68beaSAlexander Stetsenko
51671af68beaSAlexander Stetsenko (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
51681af68beaSAlexander Stetsenko HELP_ALLOW));
51691af68beaSAlexander Stetsenko (void) fprintf(fp, gettext("Options:\n"));
51701af68beaSAlexander Stetsenko for (int i = 0; i < (un ? unallow_size : allow_size); i++) {
51711af68beaSAlexander Stetsenko const char *opt = opt_desc[i++];
51721af68beaSAlexander Stetsenko const char *optdsc = opt_desc[i];
51731af68beaSAlexander Stetsenko (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);
51741af68beaSAlexander Stetsenko }
51751af68beaSAlexander Stetsenko
51761af68beaSAlexander Stetsenko (void) fprintf(fp, gettext("\nThe following permissions are "
51771af68beaSAlexander Stetsenko "supported:\n\n"));
51781af68beaSAlexander Stetsenko (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
51791af68beaSAlexander Stetsenko gettext("NOTES"));
51801af68beaSAlexander Stetsenko for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
51811af68beaSAlexander Stetsenko const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
51821af68beaSAlexander Stetsenko zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
51831af68beaSAlexander Stetsenko const char *perm_type = deleg_perm_type(perm_note);
51841af68beaSAlexander Stetsenko const char *perm_comment = deleg_perm_comment(perm_note);
51851af68beaSAlexander Stetsenko (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
51861af68beaSAlexander Stetsenko }
51871af68beaSAlexander Stetsenko
51881af68beaSAlexander Stetsenko for (i = 0; i < ZFS_NUM_PROPS; i++) {
51891af68beaSAlexander Stetsenko zprop_desc_t *pd = &pdtbl[i];
51901af68beaSAlexander Stetsenko if (pd->pd_visible != B_TRUE)
51911af68beaSAlexander Stetsenko continue;
51921af68beaSAlexander Stetsenko
51931af68beaSAlexander Stetsenko if (pd->pd_attr == PROP_READONLY)
51941af68beaSAlexander Stetsenko continue;
51951af68beaSAlexander Stetsenko
51961af68beaSAlexander Stetsenko props[count++] = pd->pd_name;
51971af68beaSAlexander Stetsenko }
51981af68beaSAlexander Stetsenko props[count] = NULL;
51991af68beaSAlexander Stetsenko
52001af68beaSAlexander Stetsenko qsort(props, count, sizeof (char *), prop_cmp);
52011af68beaSAlexander Stetsenko
52021af68beaSAlexander Stetsenko for (i = 0; i < count; i++)
52031af68beaSAlexander Stetsenko (void) fprintf(fp, fmt, props[i], gettext("property"), "");
52041af68beaSAlexander Stetsenko
52051af68beaSAlexander Stetsenko if (msg != NULL)
52061af68beaSAlexander Stetsenko (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
52071af68beaSAlexander Stetsenko
52081af68beaSAlexander Stetsenko exit(requested ? 0 : 2);
52091af68beaSAlexander Stetsenko }
52101af68beaSAlexander Stetsenko
52111af68beaSAlexander Stetsenko static inline const char *
munge_args(int argc,char ** argv,boolean_t un,size_t expected_argc,char ** permsp)52121af68beaSAlexander Stetsenko munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
52131af68beaSAlexander Stetsenko char **permsp)
52141af68beaSAlexander Stetsenko {
52151af68beaSAlexander Stetsenko if (un && argc == expected_argc - 1)
52161af68beaSAlexander Stetsenko *permsp = NULL;
52171af68beaSAlexander Stetsenko else if (argc == expected_argc)
52181af68beaSAlexander Stetsenko *permsp = argv[argc - 2];
52191af68beaSAlexander Stetsenko else
52201af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52211af68beaSAlexander Stetsenko gettext("wrong number of parameters\n"));
52221af68beaSAlexander Stetsenko
52231af68beaSAlexander Stetsenko return (argv[argc - 1]);
52241af68beaSAlexander Stetsenko }
52251af68beaSAlexander Stetsenko
52261af68beaSAlexander Stetsenko static void
parse_allow_args(int argc,char ** argv,boolean_t un,struct allow_opts * opts)52271af68beaSAlexander Stetsenko parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
52281af68beaSAlexander Stetsenko {
52291af68beaSAlexander Stetsenko int uge_sum = opts->user + opts->group + opts->everyone;
52301af68beaSAlexander Stetsenko int csuge_sum = opts->create + opts->set + uge_sum;
52311af68beaSAlexander Stetsenko int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
52321af68beaSAlexander Stetsenko int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
52331af68beaSAlexander Stetsenko
52341af68beaSAlexander Stetsenko if (uge_sum > 1)
52351af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52361af68beaSAlexander Stetsenko gettext("-u, -g, and -e are mutually exclusive\n"));
52371af68beaSAlexander Stetsenko
5238c16bcc45SIgor Kozhukhov if (opts->prt_usage) {
52391af68beaSAlexander Stetsenko if (argc == 0 && all_sum == 0)
52401af68beaSAlexander Stetsenko allow_usage(un, B_TRUE, NULL);
52411af68beaSAlexander Stetsenko else
52421af68beaSAlexander Stetsenko usage(B_FALSE);
5243c16bcc45SIgor Kozhukhov }
52441af68beaSAlexander Stetsenko
52451af68beaSAlexander Stetsenko if (opts->set) {
52461af68beaSAlexander Stetsenko if (csuge_sum > 1)
52471af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52481af68beaSAlexander Stetsenko gettext("invalid options combined with -s\n"));
52491af68beaSAlexander Stetsenko
52501af68beaSAlexander Stetsenko opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
52511af68beaSAlexander Stetsenko if (argv[0][0] != '@')
52521af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52531af68beaSAlexander Stetsenko gettext("invalid set name: missing '@' prefix\n"));
52541af68beaSAlexander Stetsenko opts->who = argv[0];
52551af68beaSAlexander Stetsenko } else if (opts->create) {
52561af68beaSAlexander Stetsenko if (ldcsuge_sum > 1)
52571af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52581af68beaSAlexander Stetsenko gettext("invalid options combined with -c\n"));
52591af68beaSAlexander Stetsenko opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
52601af68beaSAlexander Stetsenko } else if (opts->everyone) {
52611af68beaSAlexander Stetsenko if (csuge_sum > 1)
52621af68beaSAlexander Stetsenko allow_usage(un, B_FALSE,
52631af68beaSAlexander Stetsenko gettext("invalid options combined with -e\n"));
52641af68beaSAlexander Stetsenko opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
52651af68beaSAlexander Stetsenko } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
52661af68beaSAlexander Stetsenko == 0) {
52671af68beaSAlexander Stetsenko opts->everyone = B_TRUE;
52681af68beaSAlexander Stetsenko argc--;
52691af68beaSAlexander Stetsenko argv++;
52701af68beaSAlexander Stetsenko opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
5271c560ed2bSAlexander Eremin } else if (argc == 1 && !un) {
52721af68beaSAlexander Stetsenko opts->prt_perms = B_TRUE;
52731af68beaSAlexander Stetsenko opts->dataset = argv[argc-1];
52741af68beaSAlexander Stetsenko } else {
52751af68beaSAlexander Stetsenko opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
52761af68beaSAlexander Stetsenko opts->who = argv[0];
52771af68beaSAlexander Stetsenko }
52781af68beaSAlexander Stetsenko
52791af68beaSAlexander Stetsenko if (!opts->local && !opts->descend) {
52801af68beaSAlexander Stetsenko opts->local = B_TRUE;
52811af68beaSAlexander Stetsenko opts->descend = B_TRUE;
52821af68beaSAlexander Stetsenko }
52831af68beaSAlexander Stetsenko }
52841af68beaSAlexander Stetsenko
52851af68beaSAlexander Stetsenko static void
store_allow_perm(zfs_deleg_who_type_t type,boolean_t local,boolean_t descend,const char * who,char * perms,nvlist_t * top_nvl)52861af68beaSAlexander Stetsenko store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
52871af68beaSAlexander Stetsenko const char *who, char *perms, nvlist_t *top_nvl)
52881af68beaSAlexander Stetsenko {
52891af68beaSAlexander Stetsenko int i;
52901af68beaSAlexander Stetsenko char ld[2] = { '\0', '\0' };
52919adfa60dSMatthew Ahrens char who_buf[MAXNAMELEN + 32];
5292c16bcc45SIgor Kozhukhov char base_type = '\0';
5293c16bcc45SIgor Kozhukhov char set_type = '\0';
52941af68beaSAlexander Stetsenko nvlist_t *base_nvl = NULL;
52951af68beaSAlexander Stetsenko nvlist_t *set_nvl = NULL;
52961af68beaSAlexander Stetsenko nvlist_t *nvl;
52971af68beaSAlexander Stetsenko
52981af68beaSAlexander Stetsenko if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
52991af68beaSAlexander Stetsenko nomem();
53001af68beaSAlexander Stetsenko if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)
53011af68beaSAlexander Stetsenko nomem();
53021af68beaSAlexander Stetsenko
53031af68beaSAlexander Stetsenko switch (type) {
53041af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET_SETS:
53051af68beaSAlexander Stetsenko case ZFS_DELEG_NAMED_SET:
53061af68beaSAlexander Stetsenko set_type = ZFS_DELEG_NAMED_SET_SETS;
53071af68beaSAlexander Stetsenko base_type = ZFS_DELEG_NAMED_SET;
53081af68beaSAlexander Stetsenko ld[0] = ZFS_DELEG_NA;
53091af68beaSAlexander Stetsenko break;
53101af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE_SETS:
53111af68beaSAlexander Stetsenko case ZFS_DELEG_CREATE:
53121af68beaSAlexander Stetsenko set_type = ZFS_DELEG_CREATE_SETS;
53131af68beaSAlexander Stetsenko base_type = ZFS_DELEG_CREATE;
53141af68beaSAlexander Stetsenko ld[0] = ZFS_DELEG_NA;
53151af68beaSAlexander Stetsenko break;
53161af68beaSAlexander Stetsenko case ZFS_DELEG_USER_SETS:
53171af68beaSAlexander Stetsenko case ZFS_DELEG_USER:
53181af68beaSAlexander Stetsenko set_type = ZFS_DELEG_USER_SETS;
53191af68beaSAlexander Stetsenko base_type = ZFS_DELEG_USER;
53201af68beaSAlexander Stetsenko if (local)
53211af68beaSAlexander Stetsenko ld[0] = ZFS_DELEG_LOCAL;
53221af68beaSAlexander Stetsenko if (descend)
53231af68beaSAlexander Stetsenko ld[1] = ZFS_DELEG_DESCENDENT;
53241af68beaSAlexander Stetsenko break;
53251af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP_SETS:
53261af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP:
53271af68beaSAlexander Stetsenko set_type = ZFS_DELEG_GROUP_SETS;
53281af68beaSAlexander Stetsenko base_type = ZFS_DELEG_GROUP;
53291af68beaSAlexander Stetsenko if (local)
53301af68beaSAlexander Stetsenko ld[0] = ZFS_DELEG_LOCAL;
53311af68beaSAlexander Stetsenko if (descend)
53321af68beaSAlexander Stetsenko ld[1] = ZFS_DELEG_DESCENDENT;
53331af68beaSAlexander Stetsenko break;
53341af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE_SETS:
53351af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE:
53361af68beaSAlexander Stetsenko set_type = ZFS_DELEG_EVERYONE_SETS;
53371af68beaSAlexander Stetsenko base_type = ZFS_DELEG_EVERYONE;
53381af68beaSAlexander Stetsenko if (local)
53391af68beaSAlexander Stetsenko ld[0] = ZFS_DELEG_LOCAL;
53401af68beaSAlexander Stetsenko if (descend)
53411af68beaSAlexander Stetsenko ld[1] = ZFS_DELEG_DESCENDENT;
5342c16bcc45SIgor Kozhukhov break;
5343c16bcc45SIgor Kozhukhov
5344c16bcc45SIgor Kozhukhov default:
5345c16bcc45SIgor Kozhukhov assert(set_type != '\0' && base_type != '\0');
53461af68beaSAlexander Stetsenko }
53471af68beaSAlexander Stetsenko
53481af68beaSAlexander Stetsenko if (perms != NULL) {
53491af68beaSAlexander Stetsenko char *curr = perms;
53501af68beaSAlexander Stetsenko char *end = curr + strlen(perms);
53511af68beaSAlexander Stetsenko
53521af68beaSAlexander Stetsenko while (curr < end) {
53531af68beaSAlexander Stetsenko char *delim = strchr(curr, ',');
53541af68beaSAlexander Stetsenko if (delim == NULL)
53551af68beaSAlexander Stetsenko delim = end;
53561af68beaSAlexander Stetsenko else
53571af68beaSAlexander Stetsenko *delim = '\0';
53581af68beaSAlexander Stetsenko
53591af68beaSAlexander Stetsenko if (curr[0] == '@')
53601af68beaSAlexander Stetsenko nvl = set_nvl;
53611af68beaSAlexander Stetsenko else
53621af68beaSAlexander Stetsenko nvl = base_nvl;
53631af68beaSAlexander Stetsenko
53641af68beaSAlexander Stetsenko (void) nvlist_add_boolean(nvl, curr);
53651af68beaSAlexander Stetsenko if (delim != end)
53661af68beaSAlexander Stetsenko *delim = ',';
53671af68beaSAlexander Stetsenko curr = delim + 1;
53681af68beaSAlexander Stetsenko }
53691af68beaSAlexander Stetsenko
53701af68beaSAlexander Stetsenko for (i = 0; i < 2; i++) {
53711af68beaSAlexander Stetsenko char locality = ld[i];
53721af68beaSAlexander Stetsenko if (locality == 0)
53731af68beaSAlexander Stetsenko continue;
53741af68beaSAlexander Stetsenko
53751af68beaSAlexander Stetsenko if (!nvlist_empty(base_nvl)) {
53761af68beaSAlexander Stetsenko if (who != NULL)
53771af68beaSAlexander Stetsenko (void) snprintf(who_buf,
53781af68beaSAlexander Stetsenko sizeof (who_buf), "%c%c$%s",
53791af68beaSAlexander Stetsenko base_type, locality, who);
53801af68beaSAlexander Stetsenko else
53811af68beaSAlexander Stetsenko (void) snprintf(who_buf,
53821af68beaSAlexander Stetsenko sizeof (who_buf), "%c%c$",
53831af68beaSAlexander Stetsenko base_type, locality);
53841af68beaSAlexander Stetsenko
53851af68beaSAlexander Stetsenko (void) nvlist_add_nvlist(top_nvl, who_buf,
53861af68beaSAlexander Stetsenko base_nvl);
53871af68beaSAlexander Stetsenko }
53881af68beaSAlexander Stetsenko
53891af68beaSAlexander Stetsenko
53901af68beaSAlexander Stetsenko if (!nvlist_empty(set_nvl)) {
53911af68beaSAlexander Stetsenko if (who != NULL)
53921af68beaSAlexander Stetsenko (void) snprintf(who_buf,
53931af68beaSAlexander Stetsenko sizeof (who_buf), "%c%c$%s",
53941af68beaSAlexander Stetsenko set_type, locality, who);
53951af68beaSAlexander Stetsenko else
53961af68beaSAlexander Stetsenko (void) snprintf(who_buf,
53971af68beaSAlexander Stetsenko sizeof (who_buf), "%c%c$",
53981af68beaSAlexander Stetsenko set_type, locality);
53991af68beaSAlexander Stetsenko
54001af68beaSAlexander Stetsenko (void) nvlist_add_nvlist(top_nvl, who_buf,
54011af68beaSAlexander Stetsenko set_nvl);
54021af68beaSAlexander Stetsenko }
54031af68beaSAlexander Stetsenko }
54041af68beaSAlexander Stetsenko } else {
54051af68beaSAlexander Stetsenko for (i = 0; i < 2; i++) {
54061af68beaSAlexander Stetsenko char locality = ld[i];
54071af68beaSAlexander Stetsenko if (locality == 0)
54081af68beaSAlexander Stetsenko continue;
54091af68beaSAlexander Stetsenko
54101af68beaSAlexander Stetsenko if (who != NULL)
54111af68beaSAlexander Stetsenko (void) snprintf(who_buf, sizeof (who_buf),
54121af68beaSAlexander Stetsenko "%c%c$%s", base_type, locality, who);
54131af68beaSAlexander Stetsenko else
54141af68beaSAlexander Stetsenko (void) snprintf(who_buf, sizeof (who_buf),
54151af68beaSAlexander Stetsenko "%c%c$", base_type, locality);
54161af68beaSAlexander Stetsenko (void) nvlist_add_boolean(top_nvl, who_buf);
54171af68beaSAlexander Stetsenko
54181af68beaSAlexander Stetsenko if (who != NULL)
54191af68beaSAlexander Stetsenko (void) snprintf(who_buf, sizeof (who_buf),
54201af68beaSAlexander Stetsenko "%c%c$%s", set_type, locality, who);
54211af68beaSAlexander Stetsenko else
54221af68beaSAlexander Stetsenko (void) snprintf(who_buf, sizeof (who_buf),
54231af68beaSAlexander Stetsenko "%c%c$", set_type, locality);
54241af68beaSAlexander Stetsenko (void) nvlist_add_boolean(top_nvl, who_buf);
54251af68beaSAlexander Stetsenko }
54261af68beaSAlexander Stetsenko }
54271af68beaSAlexander Stetsenko }
54281af68beaSAlexander Stetsenko
54291af68beaSAlexander Stetsenko static int
construct_fsacl_list(boolean_t un,struct allow_opts * opts,nvlist_t ** nvlp)54301af68beaSAlexander Stetsenko construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
54311af68beaSAlexander Stetsenko {
54321af68beaSAlexander Stetsenko if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
54331af68beaSAlexander Stetsenko nomem();
54341af68beaSAlexander Stetsenko
54351af68beaSAlexander Stetsenko if (opts->set) {
54361af68beaSAlexander Stetsenko store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
54371af68beaSAlexander Stetsenko opts->descend, opts->who, opts->perms, *nvlp);
54381af68beaSAlexander Stetsenko } else if (opts->create) {
54391af68beaSAlexander Stetsenko store_allow_perm(ZFS_DELEG_CREATE, opts->local,
54401af68beaSAlexander Stetsenko opts->descend, NULL, opts->perms, *nvlp);
54411af68beaSAlexander Stetsenko } else if (opts->everyone) {
54421af68beaSAlexander Stetsenko store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
54431af68beaSAlexander Stetsenko opts->descend, NULL, opts->perms, *nvlp);
54441af68beaSAlexander Stetsenko } else {
54451af68beaSAlexander Stetsenko char *curr = opts->who;
54461af68beaSAlexander Stetsenko char *end = curr + strlen(curr);
54471af68beaSAlexander Stetsenko
54481af68beaSAlexander Stetsenko while (curr < end) {
54491af68beaSAlexander Stetsenko const char *who;
5450c16bcc45SIgor Kozhukhov zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
54511af68beaSAlexander Stetsenko char *endch;
54521af68beaSAlexander Stetsenko char *delim = strchr(curr, ',');
54531af68beaSAlexander Stetsenko char errbuf[256];
54541af68beaSAlexander Stetsenko char id[64];
54551af68beaSAlexander Stetsenko struct passwd *p = NULL;
54561af68beaSAlexander Stetsenko struct group *g = NULL;
54571af68beaSAlexander Stetsenko
54581af68beaSAlexander Stetsenko uid_t rid;
54591af68beaSAlexander Stetsenko if (delim == NULL)
54601af68beaSAlexander Stetsenko delim = end;
54611af68beaSAlexander Stetsenko else
54621af68beaSAlexander Stetsenko *delim = '\0';
54631af68beaSAlexander Stetsenko
54641af68beaSAlexander Stetsenko rid = (uid_t)strtol(curr, &endch, 0);
54651af68beaSAlexander Stetsenko if (opts->user) {
54661af68beaSAlexander Stetsenko who_type = ZFS_DELEG_USER;
54671af68beaSAlexander Stetsenko if (*endch != '\0')
54681af68beaSAlexander Stetsenko p = getpwnam(curr);
54691af68beaSAlexander Stetsenko else
54701af68beaSAlexander Stetsenko p = getpwuid(rid);
54711af68beaSAlexander Stetsenko
54721af68beaSAlexander Stetsenko if (p != NULL)
54731af68beaSAlexander Stetsenko rid = p->pw_uid;
54741af68beaSAlexander Stetsenko else {
54751af68beaSAlexander Stetsenko (void) snprintf(errbuf, 256, gettext(
54761af68beaSAlexander Stetsenko "invalid user %s"), curr);
54771af68beaSAlexander Stetsenko allow_usage(un, B_TRUE, errbuf);
54781af68beaSAlexander Stetsenko }
54791af68beaSAlexander Stetsenko } else if (opts->group) {
54801af68beaSAlexander Stetsenko who_type = ZFS_DELEG_GROUP;
54811af68beaSAlexander Stetsenko if (*endch != '\0')
54821af68beaSAlexander Stetsenko g = getgrnam(curr);
54831af68beaSAlexander Stetsenko else
54841af68beaSAlexander Stetsenko g = getgrgid(rid);
54851af68beaSAlexander Stetsenko
54861af68beaSAlexander Stetsenko if (g != NULL)
54871af68beaSAlexander Stetsenko rid = g->gr_gid;
54881af68beaSAlexander Stetsenko else {
54891af68beaSAlexander Stetsenko (void) snprintf(errbuf, 256, gettext(
54901af68beaSAlexander Stetsenko "invalid group %s"), curr);
54911af68beaSAlexander Stetsenko allow_usage(un, B_TRUE, errbuf);
54921af68beaSAlexander Stetsenko }
54931af68beaSAlexander Stetsenko } else {
54941af68beaSAlexander Stetsenko if (*endch != '\0') {
54951af68beaSAlexander Stetsenko p = getpwnam(curr);
54961af68beaSAlexander Stetsenko } else {
54971af68beaSAlexander Stetsenko p = getpwuid(rid);
54981af68beaSAlexander Stetsenko }
54991af68beaSAlexander Stetsenko
5500c16bcc45SIgor Kozhukhov if (p == NULL) {
55011af68beaSAlexander Stetsenko if (*endch != '\0') {
55021af68beaSAlexander Stetsenko g = getgrnam(curr);
55031af68beaSAlexander Stetsenko } else {
55041af68beaSAlexander Stetsenko g = getgrgid(rid);
55051af68beaSAlexander Stetsenko }
5506c16bcc45SIgor Kozhukhov }
55071af68beaSAlexander Stetsenko
55081af68beaSAlexander Stetsenko if (p != NULL) {
55091af68beaSAlexander Stetsenko who_type = ZFS_DELEG_USER;
55101af68beaSAlexander Stetsenko rid = p->pw_uid;
55111af68beaSAlexander Stetsenko } else if (g != NULL) {
55121af68beaSAlexander Stetsenko who_type = ZFS_DELEG_GROUP;
55131af68beaSAlexander Stetsenko rid = g->gr_gid;
55141af68beaSAlexander Stetsenko } else {
55151af68beaSAlexander Stetsenko (void) snprintf(errbuf, 256, gettext(
55161af68beaSAlexander Stetsenko "invalid user/group %s"), curr);
55171af68beaSAlexander Stetsenko allow_usage(un, B_TRUE, errbuf);
55181af68beaSAlexander Stetsenko }
55191af68beaSAlexander Stetsenko }
55201af68beaSAlexander Stetsenko
55211af68beaSAlexander Stetsenko (void) sprintf(id, "%u", rid);
55221af68beaSAlexander Stetsenko who = id;
55231af68beaSAlexander Stetsenko
55241af68beaSAlexander Stetsenko store_allow_perm(who_type, opts->local,
55251af68beaSAlexander Stetsenko opts->descend, who, opts->perms, *nvlp);
55261af68beaSAlexander Stetsenko curr = delim + 1;
55271af68beaSAlexander Stetsenko }
55281af68beaSAlexander Stetsenko }
55291af68beaSAlexander Stetsenko
55301af68beaSAlexander Stetsenko return (0);
55311af68beaSAlexander Stetsenko }
55321af68beaSAlexander Stetsenko
55331af68beaSAlexander Stetsenko static void
print_set_creat_perms(uu_avl_t * who_avl)55341af68beaSAlexander Stetsenko print_set_creat_perms(uu_avl_t *who_avl)
55351af68beaSAlexander Stetsenko {
55361af68beaSAlexander Stetsenko const char *sc_title[] = {
55371af68beaSAlexander Stetsenko gettext("Permission sets:\n"),
55381af68beaSAlexander Stetsenko gettext("Create time permissions:\n"),
55391af68beaSAlexander Stetsenko NULL
55401af68beaSAlexander Stetsenko };
55411af68beaSAlexander Stetsenko who_perm_node_t *who_node = NULL;
55421af68beaSAlexander Stetsenko int prev_weight = -1;
55431af68beaSAlexander Stetsenko
55441af68beaSAlexander Stetsenko for (who_node = uu_avl_first(who_avl); who_node != NULL;
55451af68beaSAlexander Stetsenko who_node = uu_avl_next(who_avl, who_node)) {
55461af68beaSAlexander Stetsenko uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
55471af68beaSAlexander Stetsenko zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
55481af68beaSAlexander Stetsenko const char *who_name = who_node->who_perm.who_name;
55491af68beaSAlexander Stetsenko int weight = who_type2weight(who_type);
55501af68beaSAlexander Stetsenko boolean_t first = B_TRUE;
55511af68beaSAlexander Stetsenko deleg_perm_node_t *deleg_node;
55521af68beaSAlexander Stetsenko
55531af68beaSAlexander Stetsenko if (prev_weight != weight) {
555451463258SBill Sommerfeld VERIFY3S(weight, >=, 0);
555551463258SBill Sommerfeld VERIFY3S(weight, <=, 1);
555651463258SBill Sommerfeld (void) printf(sc_title[weight]);
55571af68beaSAlexander Stetsenko prev_weight = weight;
55581af68beaSAlexander Stetsenko }
55591af68beaSAlexander Stetsenko
55601af68beaSAlexander Stetsenko if (who_name == NULL || strnlen(who_name, 1) == 0)
55611af68beaSAlexander Stetsenko (void) printf("\t");
55621af68beaSAlexander Stetsenko else
55631af68beaSAlexander Stetsenko (void) printf("\t%s ", who_name);
55641af68beaSAlexander Stetsenko
55651af68beaSAlexander Stetsenko for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
55661af68beaSAlexander Stetsenko deleg_node = uu_avl_next(avl, deleg_node)) {
55671af68beaSAlexander Stetsenko if (first) {
55681af68beaSAlexander Stetsenko (void) printf("%s",
55691af68beaSAlexander Stetsenko deleg_node->dpn_perm.dp_name);
55701af68beaSAlexander Stetsenko first = B_FALSE;
55711af68beaSAlexander Stetsenko } else
55721af68beaSAlexander Stetsenko (void) printf(",%s",
55731af68beaSAlexander Stetsenko deleg_node->dpn_perm.dp_name);
55741af68beaSAlexander Stetsenko }
55751af68beaSAlexander Stetsenko
55761af68beaSAlexander Stetsenko (void) printf("\n");
55771af68beaSAlexander Stetsenko }
55781af68beaSAlexander Stetsenko }
55791af68beaSAlexander Stetsenko
5580c16bcc45SIgor Kozhukhov static void
print_uge_deleg_perms(uu_avl_t * who_avl,boolean_t local,boolean_t descend,const char * title)55811af68beaSAlexander Stetsenko print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
55821af68beaSAlexander Stetsenko const char *title)
55831af68beaSAlexander Stetsenko {
55841af68beaSAlexander Stetsenko who_perm_node_t *who_node = NULL;
55851af68beaSAlexander Stetsenko boolean_t prt_title = B_TRUE;
55861af68beaSAlexander Stetsenko uu_avl_walk_t *walk;
55871af68beaSAlexander Stetsenko
55881af68beaSAlexander Stetsenko if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
55891af68beaSAlexander Stetsenko nomem();
55901af68beaSAlexander Stetsenko
55911af68beaSAlexander Stetsenko while ((who_node = uu_avl_walk_next(walk)) != NULL) {
55921af68beaSAlexander Stetsenko const char *who_name = who_node->who_perm.who_name;
55931af68beaSAlexander Stetsenko const char *nice_who_name = who_node->who_perm.who_ug_name;
55941af68beaSAlexander Stetsenko uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
55951af68beaSAlexander Stetsenko zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
55961af68beaSAlexander Stetsenko char delim = ' ';
55971af68beaSAlexander Stetsenko deleg_perm_node_t *deleg_node;
55981af68beaSAlexander Stetsenko boolean_t prt_who = B_TRUE;
55991af68beaSAlexander Stetsenko
56001af68beaSAlexander Stetsenko for (deleg_node = uu_avl_first(avl);
56011af68beaSAlexander Stetsenko deleg_node != NULL;
56021af68beaSAlexander Stetsenko deleg_node = uu_avl_next(avl, deleg_node)) {
56031af68beaSAlexander Stetsenko if (local != deleg_node->dpn_perm.dp_local ||
56041af68beaSAlexander Stetsenko descend != deleg_node->dpn_perm.dp_descend)
56051af68beaSAlexander Stetsenko continue;
56061af68beaSAlexander Stetsenko
56071af68beaSAlexander Stetsenko if (prt_who) {
56081af68beaSAlexander Stetsenko const char *who = NULL;
56091af68beaSAlexander Stetsenko if (prt_title) {
56101af68beaSAlexander Stetsenko prt_title = B_FALSE;
56111af68beaSAlexander Stetsenko (void) printf(title);
56121af68beaSAlexander Stetsenko }
56131af68beaSAlexander Stetsenko
56141af68beaSAlexander Stetsenko switch (who_type) {
56151af68beaSAlexander Stetsenko case ZFS_DELEG_USER_SETS:
56161af68beaSAlexander Stetsenko case ZFS_DELEG_USER:
56171af68beaSAlexander Stetsenko who = gettext("user");
56181af68beaSAlexander Stetsenko if (nice_who_name)
56191af68beaSAlexander Stetsenko who_name = nice_who_name;
56201af68beaSAlexander Stetsenko break;
56211af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP_SETS:
56221af68beaSAlexander Stetsenko case ZFS_DELEG_GROUP:
56231af68beaSAlexander Stetsenko who = gettext("group");
56241af68beaSAlexander Stetsenko if (nice_who_name)
56251af68beaSAlexander Stetsenko who_name = nice_who_name;
56261af68beaSAlexander Stetsenko break;
56271af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE_SETS:
56281af68beaSAlexander Stetsenko case ZFS_DELEG_EVERYONE:
56291af68beaSAlexander Stetsenko who = gettext("everyone");
56301af68beaSAlexander Stetsenko who_name = NULL;
5631c16bcc45SIgor Kozhukhov break;
5632c16bcc45SIgor Kozhukhov
5633c16bcc45SIgor Kozhukhov default:
5634c16bcc45SIgor Kozhukhov assert(who != NULL);
56351af68beaSAlexander Stetsenko }
56361af68beaSAlexander Stetsenko
56371af68beaSAlexander Stetsenko prt_who = B_FALSE;
56381af68beaSAlexander Stetsenko if (who_name == NULL)
56391af68beaSAlexander Stetsenko (void) printf("\t%s", who);
56401af68beaSAlexander Stetsenko else
56411af68beaSAlexander Stetsenko (void) printf("\t%s %s", who, who_name);
56421af68beaSAlexander Stetsenko }
56431af68beaSAlexander Stetsenko
56441af68beaSAlexander Stetsenko (void) printf("%c%s", delim,
56451af68beaSAlexander Stetsenko deleg_node->dpn_perm.dp_name);
56461af68beaSAlexander Stetsenko delim = ',';
56471af68beaSAlexander Stetsenko }
56481af68beaSAlexander Stetsenko
56491af68beaSAlexander Stetsenko if (!prt_who)
56501af68beaSAlexander Stetsenko (void) printf("\n");
56511af68beaSAlexander Stetsenko }
56521af68beaSAlexander Stetsenko
56531af68beaSAlexander Stetsenko uu_avl_walk_end(walk);
56541af68beaSAlexander Stetsenko }
56551af68beaSAlexander Stetsenko
56561af68beaSAlexander Stetsenko static void
print_fs_perms(fs_perm_set_t * fspset)56571af68beaSAlexander Stetsenko print_fs_perms(fs_perm_set_t *fspset)
56581af68beaSAlexander Stetsenko {
56591af68beaSAlexander Stetsenko fs_perm_node_t *node = NULL;
56609adfa60dSMatthew Ahrens char buf[MAXNAMELEN + 32];
56611af68beaSAlexander Stetsenko const char *dsname = buf;
56621af68beaSAlexander Stetsenko
56631af68beaSAlexander Stetsenko for (node = uu_list_first(fspset->fsps_list); node != NULL;
56641af68beaSAlexander Stetsenko node = uu_list_next(fspset->fsps_list, node)) {
56651af68beaSAlexander Stetsenko uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
56661af68beaSAlexander Stetsenko uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
56671af68beaSAlexander Stetsenko int left = 0;
56681af68beaSAlexander Stetsenko
56699adfa60dSMatthew Ahrens (void) snprintf(buf, sizeof (buf),
56701af68beaSAlexander Stetsenko gettext("---- Permissions on %s "),
56711af68beaSAlexander Stetsenko node->fspn_fsperm.fsp_name);
56721af68beaSAlexander Stetsenko (void) printf(dsname);
56731af68beaSAlexander Stetsenko left = 70 - strlen(buf);
56741af68beaSAlexander Stetsenko while (left-- > 0)
56751af68beaSAlexander Stetsenko (void) printf("-");
56761af68beaSAlexander Stetsenko (void) printf("\n");
56771af68beaSAlexander Stetsenko
56781af68beaSAlexander Stetsenko print_set_creat_perms(sc_avl);
56791af68beaSAlexander Stetsenko print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
56801af68beaSAlexander Stetsenko gettext("Local permissions:\n"));
56811af68beaSAlexander Stetsenko print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
56821af68beaSAlexander Stetsenko gettext("Descendent permissions:\n"));
56831af68beaSAlexander Stetsenko print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
56841af68beaSAlexander Stetsenko gettext("Local+Descendent permissions:\n"));
56851af68beaSAlexander Stetsenko }
56861af68beaSAlexander Stetsenko }
56871af68beaSAlexander Stetsenko
56881af68beaSAlexander Stetsenko static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
56891af68beaSAlexander Stetsenko
56901af68beaSAlexander Stetsenko struct deleg_perms {
56911af68beaSAlexander Stetsenko boolean_t un;
56921af68beaSAlexander Stetsenko nvlist_t *nvl;
56931af68beaSAlexander Stetsenko };
56941af68beaSAlexander Stetsenko
56951af68beaSAlexander Stetsenko static int
set_deleg_perms(zfs_handle_t * zhp,void * data)56961af68beaSAlexander Stetsenko set_deleg_perms(zfs_handle_t *zhp, void *data)
56971af68beaSAlexander Stetsenko {
56981af68beaSAlexander Stetsenko struct deleg_perms *perms = (struct deleg_perms *)data;
56991af68beaSAlexander Stetsenko zfs_type_t zfs_type = zfs_get_type(zhp);
57001af68beaSAlexander Stetsenko
57011af68beaSAlexander Stetsenko if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
57021af68beaSAlexander Stetsenko return (0);
57031af68beaSAlexander Stetsenko
57041af68beaSAlexander Stetsenko return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
57051af68beaSAlexander Stetsenko }
57061af68beaSAlexander Stetsenko
57071af68beaSAlexander Stetsenko static int
zfs_do_allow_unallow_impl(int argc,char ** argv,boolean_t un)57081af68beaSAlexander Stetsenko zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
57091af68beaSAlexander Stetsenko {
57101af68beaSAlexander Stetsenko zfs_handle_t *zhp;
57111af68beaSAlexander Stetsenko nvlist_t *perm_nvl = NULL;
57121af68beaSAlexander Stetsenko nvlist_t *update_perm_nvl = NULL;
57131af68beaSAlexander Stetsenko int error = 1;
57141af68beaSAlexander Stetsenko int c;
57151af68beaSAlexander Stetsenko struct allow_opts opts = { 0 };
57161af68beaSAlexander Stetsenko
57171af68beaSAlexander Stetsenko const char *optstr = un ? "ldugecsrh" : "ldugecsh";
57181af68beaSAlexander Stetsenko
57191af68beaSAlexander Stetsenko /* check opts */
57201af68beaSAlexander Stetsenko while ((c = getopt(argc, argv, optstr)) != -1) {
57211af68beaSAlexander Stetsenko switch (c) {
57221af68beaSAlexander Stetsenko case 'l':
57231af68beaSAlexander Stetsenko opts.local = B_TRUE;
57241af68beaSAlexander Stetsenko break;
57251af68beaSAlexander Stetsenko case 'd':
57261af68beaSAlexander Stetsenko opts.descend = B_TRUE;
57271af68beaSAlexander Stetsenko break;
57281af68beaSAlexander Stetsenko case 'u':
57291af68beaSAlexander Stetsenko opts.user = B_TRUE;
57301af68beaSAlexander Stetsenko break;
57311af68beaSAlexander Stetsenko case 'g':
57321af68beaSAlexander Stetsenko opts.group = B_TRUE;
57331af68beaSAlexander Stetsenko break;
57341af68beaSAlexander Stetsenko case 'e':
57351af68beaSAlexander Stetsenko opts.everyone = B_TRUE;
57361af68beaSAlexander Stetsenko break;
57371af68beaSAlexander Stetsenko case 's':
57381af68beaSAlexander Stetsenko opts.set = B_TRUE;
57391af68beaSAlexander Stetsenko break;
57401af68beaSAlexander Stetsenko case 'c':
57411af68beaSAlexander Stetsenko opts.create = B_TRUE;
57421af68beaSAlexander Stetsenko break;
57431af68beaSAlexander Stetsenko case 'r':
57441af68beaSAlexander Stetsenko opts.recursive = B_TRUE;
57451af68beaSAlexander Stetsenko break;
57461af68beaSAlexander Stetsenko case ':':
57471af68beaSAlexander Stetsenko (void) fprintf(stderr, gettext("missing argument for "
57481af68beaSAlexander Stetsenko "'%c' option\n"), optopt);
57491af68beaSAlexander Stetsenko usage(B_FALSE);
57501af68beaSAlexander Stetsenko break;
57511af68beaSAlexander Stetsenko case 'h':
57521af68beaSAlexander Stetsenko opts.prt_usage = B_TRUE;
57531af68beaSAlexander Stetsenko break;
57541af68beaSAlexander Stetsenko case '?':
57551af68beaSAlexander Stetsenko (void) fprintf(stderr, gettext("invalid option '%c'\n"),
57561af68beaSAlexander Stetsenko optopt);
57571af68beaSAlexander Stetsenko usage(B_FALSE);
57581af68beaSAlexander Stetsenko }
57591af68beaSAlexander Stetsenko }
57601af68beaSAlexander Stetsenko
57611af68beaSAlexander Stetsenko argc -= optind;
57621af68beaSAlexander Stetsenko argv += optind;
57631af68beaSAlexander Stetsenko
57641af68beaSAlexander Stetsenko /* check arguments */
57651af68beaSAlexander Stetsenko parse_allow_args(argc, argv, un, &opts);
57661af68beaSAlexander Stetsenko
57671af68beaSAlexander Stetsenko /* try to open the dataset */
5768a640714eSAlexander Eremin if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
5769a640714eSAlexander Eremin ZFS_TYPE_VOLUME)) == NULL) {
5770a640714eSAlexander Eremin (void) fprintf(stderr, "Failed to open dataset: %s\n",
57711af68beaSAlexander Stetsenko opts.dataset);
57721af68beaSAlexander Stetsenko return (-1);
57731af68beaSAlexander Stetsenko }
57741af68beaSAlexander Stetsenko
57751af68beaSAlexander Stetsenko if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
57761af68beaSAlexander Stetsenko goto cleanup2;
57771af68beaSAlexander Stetsenko
57781af68beaSAlexander Stetsenko fs_perm_set_init(&fs_perm_set);
57791af68beaSAlexander Stetsenko if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
5780a640714eSAlexander Eremin (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
57811af68beaSAlexander Stetsenko goto cleanup1;
57821af68beaSAlexander Stetsenko }
57831af68beaSAlexander Stetsenko
57841af68beaSAlexander Stetsenko if (opts.prt_perms)
57851af68beaSAlexander Stetsenko print_fs_perms(&fs_perm_set);
57861af68beaSAlexander Stetsenko else {
57871af68beaSAlexander Stetsenko (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
57881af68beaSAlexander Stetsenko if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
57891af68beaSAlexander Stetsenko goto cleanup0;
57901af68beaSAlexander Stetsenko
57911af68beaSAlexander Stetsenko if (un && opts.recursive) {
57921af68beaSAlexander Stetsenko struct deleg_perms data = { un, update_perm_nvl };
57931af68beaSAlexander Stetsenko if (zfs_iter_filesystems(zhp, set_deleg_perms,
57941af68beaSAlexander Stetsenko &data) != 0)
57951af68beaSAlexander Stetsenko goto cleanup0;
57961af68beaSAlexander Stetsenko }
57971af68beaSAlexander Stetsenko }
57981af68beaSAlexander Stetsenko
57991af68beaSAlexander Stetsenko error = 0;
58001af68beaSAlexander Stetsenko
58011af68beaSAlexander Stetsenko cleanup0:
58021af68beaSAlexander Stetsenko nvlist_free(perm_nvl);
58031af68beaSAlexander Stetsenko nvlist_free(update_perm_nvl);
58041af68beaSAlexander Stetsenko cleanup1:
58051af68beaSAlexander Stetsenko fs_perm_set_fini(&fs_perm_set);
58061af68beaSAlexander Stetsenko cleanup2:
58071af68beaSAlexander Stetsenko zfs_close(zhp);
58081af68beaSAlexander Stetsenko
58091af68beaSAlexander Stetsenko return (error);
58101af68beaSAlexander Stetsenko }
58111af68beaSAlexander Stetsenko
58121af68beaSAlexander Stetsenko static int
zfs_do_allow(int argc,char ** argv)58131af68beaSAlexander Stetsenko zfs_do_allow(int argc, char **argv)
58141af68beaSAlexander Stetsenko {
58151af68beaSAlexander Stetsenko return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
58161af68beaSAlexander Stetsenko }
58171af68beaSAlexander Stetsenko
58181af68beaSAlexander Stetsenko static int
zfs_do_unallow(int argc,char ** argv)58191af68beaSAlexander Stetsenko zfs_do_unallow(int argc, char **argv)
58201af68beaSAlexander Stetsenko {
58211af68beaSAlexander Stetsenko return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
58221af68beaSAlexander Stetsenko }
58231af68beaSAlexander Stetsenko
5824842727c2SChris Kirby static int
zfs_do_hold_rele_impl(int argc,char ** argv,boolean_t holding)5825842727c2SChris Kirby zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
5826842727c2SChris Kirby {
5827842727c2SChris Kirby int errors = 0;
5828842727c2SChris Kirby int i;
5829842727c2SChris Kirby const char *tag;
5830842727c2SChris Kirby boolean_t recursive = B_FALSE;
5831ca45db41SChris Kirby const char *opts = holding ? "rt" : "r";
5832842727c2SChris Kirby int c;
5833842727c2SChris Kirby
5834842727c2SChris Kirby /* check options */
5835ca45db41SChris Kirby while ((c = getopt(argc, argv, opts)) != -1) {
5836842727c2SChris Kirby switch (c) {
5837842727c2SChris Kirby case 'r':
5838842727c2SChris Kirby recursive = B_TRUE;
5839842727c2SChris Kirby break;
5840842727c2SChris Kirby case '?':
5841842727c2SChris Kirby (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5842842727c2SChris Kirby optopt);
5843842727c2SChris Kirby usage(B_FALSE);
5844842727c2SChris Kirby }
5845842727c2SChris Kirby }
5846842727c2SChris Kirby
5847842727c2SChris Kirby argc -= optind;
5848842727c2SChris Kirby argv += optind;
5849842727c2SChris Kirby
5850842727c2SChris Kirby /* check number of arguments */
5851842727c2SChris Kirby if (argc < 2)
5852842727c2SChris Kirby usage(B_FALSE);
5853842727c2SChris Kirby
5854842727c2SChris Kirby tag = argv[0];
5855842727c2SChris Kirby --argc;
5856842727c2SChris Kirby ++argv;
5857842727c2SChris Kirby
5858ca45db41SChris Kirby if (holding && tag[0] == '.') {
5859842727c2SChris Kirby /* tags starting with '.' are reserved for libzfs */
5860ca45db41SChris Kirby (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
5861842727c2SChris Kirby usage(B_FALSE);
5862842727c2SChris Kirby }
5863842727c2SChris Kirby
5864842727c2SChris Kirby for (i = 0; i < argc; ++i) {
5865842727c2SChris Kirby zfs_handle_t *zhp;
58669adfa60dSMatthew Ahrens char parent[ZFS_MAX_DATASET_NAME_LEN];
5867842727c2SChris Kirby const char *delim;
5868842727c2SChris Kirby char *path = argv[i];
5869842727c2SChris Kirby
5870842727c2SChris Kirby delim = strchr(path, '@');
5871842727c2SChris Kirby if (delim == NULL) {
5872842727c2SChris Kirby (void) fprintf(stderr,
5873842727c2SChris Kirby gettext("'%s' is not a snapshot\n"), path);
5874842727c2SChris Kirby ++errors;
5875842727c2SChris Kirby continue;
5876842727c2SChris Kirby }
5877842727c2SChris Kirby (void) strncpy(parent, path, delim - path);
5878842727c2SChris Kirby parent[delim - path] = '\0';
5879842727c2SChris Kirby
5880842727c2SChris Kirby zhp = zfs_open(g_zfs, parent,
5881842727c2SChris Kirby ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
5882842727c2SChris Kirby if (zhp == NULL) {
5883842727c2SChris Kirby ++errors;
5884842727c2SChris Kirby continue;
5885842727c2SChris Kirby }
5886ca45db41SChris Kirby if (holding) {
5887a7a845e4SSteven Hartland if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
5888842727c2SChris Kirby ++errors;
5889ca45db41SChris Kirby } else {
5890ca45db41SChris Kirby if (zfs_release(zhp, delim+1, tag, recursive) != 0)
5891ca45db41SChris Kirby ++errors;
5892ca45db41SChris Kirby }
5893842727c2SChris Kirby zfs_close(zhp);
5894842727c2SChris Kirby }
5895842727c2SChris Kirby
5896842727c2SChris Kirby return (errors != 0);
5897842727c2SChris Kirby }
5898842727c2SChris Kirby
5899842727c2SChris Kirby /*
5900ca45db41SChris Kirby * zfs hold [-r] [-t] <tag> <snap> ...
5901842727c2SChris Kirby *
5902842727c2SChris Kirby * -r Recursively hold
5903842727c2SChris Kirby *
5904842727c2SChris Kirby * Apply a user-hold with the given tag to the list of snapshots.
5905842727c2SChris Kirby */
5906842727c2SChris Kirby static int
zfs_do_hold(int argc,char ** argv)5907842727c2SChris Kirby zfs_do_hold(int argc, char **argv)
5908842727c2SChris Kirby {
5909842727c2SChris Kirby return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5910842727c2SChris Kirby }
5911842727c2SChris Kirby
5912842727c2SChris Kirby /*
5913842727c2SChris Kirby * zfs release [-r] <tag> <snap> ...
5914842727c2SChris Kirby *
5915842727c2SChris Kirby * -r Recursively release
5916842727c2SChris Kirby *
5917842727c2SChris Kirby * Release a user-hold with the given tag from the list of snapshots.
5918842727c2SChris Kirby */
5919842727c2SChris Kirby static int
zfs_do_release(int argc,char ** argv)5920842727c2SChris Kirby zfs_do_release(int argc, char **argv)
5921842727c2SChris Kirby {
5922842727c2SChris Kirby return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5923842727c2SChris Kirby }
5924842727c2SChris Kirby
59251af68beaSAlexander Stetsenko typedef struct holds_cbdata {
59261af68beaSAlexander Stetsenko boolean_t cb_recursive;
59271af68beaSAlexander Stetsenko const char *cb_snapname;
59281af68beaSAlexander Stetsenko nvlist_t **cb_nvlp;
59291af68beaSAlexander Stetsenko size_t cb_max_namelen;
59301af68beaSAlexander Stetsenko size_t cb_max_taglen;
59311af68beaSAlexander Stetsenko } holds_cbdata_t;
59321af68beaSAlexander Stetsenko
59331af68beaSAlexander Stetsenko #define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
59341af68beaSAlexander Stetsenko #define DATETIME_BUF_LEN (32)
59351af68beaSAlexander Stetsenko /*
59361af68beaSAlexander Stetsenko *
59371af68beaSAlexander Stetsenko */
59381af68beaSAlexander Stetsenko static void
print_holds(boolean_t scripted,size_t nwidth,size_t tagwidth,nvlist_t * nvl)59391af68beaSAlexander Stetsenko print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
59401af68beaSAlexander Stetsenko {
59411af68beaSAlexander Stetsenko int i;
59421af68beaSAlexander Stetsenko nvpair_t *nvp = NULL;
59431af68beaSAlexander Stetsenko char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
59441af68beaSAlexander Stetsenko const char *col;
59451af68beaSAlexander Stetsenko
59461af68beaSAlexander Stetsenko if (!scripted) {
59471af68beaSAlexander Stetsenko for (i = 0; i < 3; i++) {
59481af68beaSAlexander Stetsenko col = gettext(hdr_cols[i]);
59491af68beaSAlexander Stetsenko if (i < 2)
59501af68beaSAlexander Stetsenko (void) printf("%-*s ", i ? tagwidth : nwidth,
59511af68beaSAlexander Stetsenko col);
59521af68beaSAlexander Stetsenko else
59531af68beaSAlexander Stetsenko (void) printf("%s\n", col);
59541af68beaSAlexander Stetsenko }
59551af68beaSAlexander Stetsenko }
59561af68beaSAlexander Stetsenko
59571af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
59581af68beaSAlexander Stetsenko char *zname = nvpair_name(nvp);
59591af68beaSAlexander Stetsenko nvlist_t *nvl2;
59601af68beaSAlexander Stetsenko nvpair_t *nvp2 = NULL;
59611af68beaSAlexander Stetsenko (void) nvpair_value_nvlist(nvp, &nvl2);
59621af68beaSAlexander Stetsenko while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
59631af68beaSAlexander Stetsenko char tsbuf[DATETIME_BUF_LEN];
59641af68beaSAlexander Stetsenko char *tagname = nvpair_name(nvp2);
59651af68beaSAlexander Stetsenko uint64_t val = 0;
59661af68beaSAlexander Stetsenko time_t time;
59671af68beaSAlexander Stetsenko struct tm t;
59681af68beaSAlexander Stetsenko
59691af68beaSAlexander Stetsenko (void) nvpair_value_uint64(nvp2, &val);
59701af68beaSAlexander Stetsenko time = (time_t)val;
59711af68beaSAlexander Stetsenko (void) localtime_r(&time, &t);
59721af68beaSAlexander Stetsenko (void) strftime(tsbuf, DATETIME_BUF_LEN,
59731af68beaSAlexander Stetsenko gettext(STRFTIME_FMT_STR), &t);
59741af68beaSAlexander Stetsenko
5975e9b7d6e7SAllan Jude if (scripted) {
5976e9b7d6e7SAllan Jude (void) printf("%s\t%s\t%s\n", zname,
5977e9b7d6e7SAllan Jude tagname, tsbuf);
5978e9b7d6e7SAllan Jude } else {
5979e9b7d6e7SAllan Jude (void) printf("%-*s %-*s %s\n", nwidth,
5980e9b7d6e7SAllan Jude zname, tagwidth, tagname, tsbuf);
5981e9b7d6e7SAllan Jude }
59821af68beaSAlexander Stetsenko }
59831af68beaSAlexander Stetsenko }
59841af68beaSAlexander Stetsenko }
59851af68beaSAlexander Stetsenko
59861af68beaSAlexander Stetsenko /*
59871af68beaSAlexander Stetsenko * Generic callback function to list a dataset or snapshot.
59881af68beaSAlexander Stetsenko */
59891af68beaSAlexander Stetsenko static int
holds_callback(zfs_handle_t * zhp,void * data)59901af68beaSAlexander Stetsenko holds_callback(zfs_handle_t *zhp, void *data)
59911af68beaSAlexander Stetsenko {
59921af68beaSAlexander Stetsenko holds_cbdata_t *cbp = data;
59931af68beaSAlexander Stetsenko nvlist_t *top_nvl = *cbp->cb_nvlp;
59941af68beaSAlexander Stetsenko nvlist_t *nvl = NULL;
59951af68beaSAlexander Stetsenko nvpair_t *nvp = NULL;
59961af68beaSAlexander Stetsenko const char *zname = zfs_get_name(zhp);
59979adfa60dSMatthew Ahrens size_t znamelen = strlen(zname);
59981af68beaSAlexander Stetsenko
59991af68beaSAlexander Stetsenko if (cbp->cb_recursive) {
60001af68beaSAlexander Stetsenko const char *snapname;
60011af68beaSAlexander Stetsenko char *delim = strchr(zname, '@');
60021af68beaSAlexander Stetsenko if (delim == NULL)
60031af68beaSAlexander Stetsenko return (0);
60041af68beaSAlexander Stetsenko
60051af68beaSAlexander Stetsenko snapname = delim + 1;
60061af68beaSAlexander Stetsenko if (strcmp(cbp->cb_snapname, snapname))
60071af68beaSAlexander Stetsenko return (0);
60081af68beaSAlexander Stetsenko }
60091af68beaSAlexander Stetsenko
60101af68beaSAlexander Stetsenko if (zfs_get_holds(zhp, &nvl) != 0)
60111af68beaSAlexander Stetsenko return (-1);
60121af68beaSAlexander Stetsenko
60131af68beaSAlexander Stetsenko if (znamelen > cbp->cb_max_namelen)
60141af68beaSAlexander Stetsenko cbp->cb_max_namelen = znamelen;
60151af68beaSAlexander Stetsenko
60161af68beaSAlexander Stetsenko while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
60171af68beaSAlexander Stetsenko const char *tag = nvpair_name(nvp);
60189adfa60dSMatthew Ahrens size_t taglen = strlen(tag);
60191af68beaSAlexander Stetsenko if (taglen > cbp->cb_max_taglen)
60201af68beaSAlexander Stetsenko cbp->cb_max_taglen = taglen;
60211af68beaSAlexander Stetsenko }
60221af68beaSAlexander Stetsenko
60231af68beaSAlexander Stetsenko return (nvlist_add_nvlist(top_nvl, zname, nvl));
60241af68beaSAlexander Stetsenko }
60251af68beaSAlexander Stetsenko
60261af68beaSAlexander Stetsenko /*
60271af68beaSAlexander Stetsenko * zfs holds [-r] <snap> ...
60281af68beaSAlexander Stetsenko *
60291af68beaSAlexander Stetsenko * -r Recursively hold
60301af68beaSAlexander Stetsenko */
60311af68beaSAlexander Stetsenko static int
zfs_do_holds(int argc,char ** argv)60321af68beaSAlexander Stetsenko zfs_do_holds(int argc, char **argv)
60331af68beaSAlexander Stetsenko {
60341af68beaSAlexander Stetsenko int errors = 0;
60351af68beaSAlexander Stetsenko int c;
60361af68beaSAlexander Stetsenko int i;
60371af68beaSAlexander Stetsenko boolean_t scripted = B_FALSE;
60381af68beaSAlexander Stetsenko boolean_t recursive = B_FALSE;
60391af68beaSAlexander Stetsenko const char *opts = "rH";
60401af68beaSAlexander Stetsenko nvlist_t *nvl;
60411af68beaSAlexander Stetsenko
60421af68beaSAlexander Stetsenko int types = ZFS_TYPE_SNAPSHOT;
60431af68beaSAlexander Stetsenko holds_cbdata_t cb = { 0 };
60441af68beaSAlexander Stetsenko
60451af68beaSAlexander Stetsenko int limit = 0;
604605c998d6SRichard Lowe int ret = 0;
60471af68beaSAlexander Stetsenko int flags = 0;
60481af68beaSAlexander Stetsenko
60491af68beaSAlexander Stetsenko /* check options */
60501af68beaSAlexander Stetsenko while ((c = getopt(argc, argv, opts)) != -1) {
60511af68beaSAlexander Stetsenko switch (c) {
60521af68beaSAlexander Stetsenko case 'r':
60531af68beaSAlexander Stetsenko recursive = B_TRUE;
60541af68beaSAlexander Stetsenko break;
60551af68beaSAlexander Stetsenko case 'H':
60561af68beaSAlexander Stetsenko scripted = B_TRUE;
60571af68beaSAlexander Stetsenko break;
60581af68beaSAlexander Stetsenko case '?':
60591af68beaSAlexander Stetsenko (void) fprintf(stderr, gettext("invalid option '%c'\n"),
60601af68beaSAlexander Stetsenko optopt);
60611af68beaSAlexander Stetsenko usage(B_FALSE);
60621af68beaSAlexander Stetsenko }
60631af68beaSAlexander Stetsenko }
60641af68beaSAlexander Stetsenko
60651af68beaSAlexander Stetsenko if (recursive) {
60661af68beaSAlexander Stetsenko types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
60671af68beaSAlexander Stetsenko flags |= ZFS_ITER_RECURSE;
60681af68beaSAlexander Stetsenko }
60691af68beaSAlexander Stetsenko
60701af68beaSAlexander Stetsenko argc -= optind;
60711af68beaSAlexander Stetsenko argv += optind;
60721af68beaSAlexander Stetsenko
60731af68beaSAlexander Stetsenko /* check number of arguments */
60741af68beaSAlexander Stetsenko if (argc < 1)
60751af68beaSAlexander Stetsenko usage(B_FALSE);
60761af68beaSAlexander Stetsenko
60771af68beaSAlexander Stetsenko if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
60781af68beaSAlexander Stetsenko nomem();
60791af68beaSAlexander Stetsenko
60801af68beaSAlexander Stetsenko for (i = 0; i < argc; ++i) {
60811af68beaSAlexander Stetsenko char *snapshot = argv[i];
60821af68beaSAlexander Stetsenko const char *delim;
60831af68beaSAlexander Stetsenko const char *snapname;
60841af68beaSAlexander Stetsenko
60851af68beaSAlexander Stetsenko delim = strchr(snapshot, '@');
60861af68beaSAlexander Stetsenko if (delim == NULL) {
60871af68beaSAlexander Stetsenko (void) fprintf(stderr,
60881af68beaSAlexander Stetsenko gettext("'%s' is not a snapshot\n"), snapshot);
60891af68beaSAlexander Stetsenko ++errors;
60901af68beaSAlexander Stetsenko continue;
60911af68beaSAlexander Stetsenko }
60921af68beaSAlexander Stetsenko snapname = delim + 1;
60931af68beaSAlexander Stetsenko if (recursive)
60941af68beaSAlexander Stetsenko snapshot[delim - snapshot] = '\0';
60951af68beaSAlexander Stetsenko
60961af68beaSAlexander Stetsenko cb.cb_recursive = recursive;
60971af68beaSAlexander Stetsenko cb.cb_snapname = snapname;
60981af68beaSAlexander Stetsenko cb.cb_nvlp = &nvl;
60991af68beaSAlexander Stetsenko
61001af68beaSAlexander Stetsenko /*
61011af68beaSAlexander Stetsenko * 1. collect holds data, set format options
61021af68beaSAlexander Stetsenko */
61031e9e241fSAndrew Stormont ret = zfs_for_each(1, &argv[i], flags, types, NULL, NULL, limit,
61041af68beaSAlexander Stetsenko holds_callback, &cb);
61051af68beaSAlexander Stetsenko if (ret != 0)
61061af68beaSAlexander Stetsenko ++errors;
61071af68beaSAlexander Stetsenko }
61081af68beaSAlexander Stetsenko
61091af68beaSAlexander Stetsenko /*
61101af68beaSAlexander Stetsenko * 2. print holds data
61111af68beaSAlexander Stetsenko */
61121af68beaSAlexander Stetsenko print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
61131af68beaSAlexander Stetsenko
61141af68beaSAlexander Stetsenko nvlist_free(nvl);
61151af68beaSAlexander Stetsenko
61161af68beaSAlexander Stetsenko return (0 != errors);
61171af68beaSAlexander Stetsenko }
61181af68beaSAlexander Stetsenko
6119dae68f5eSmmusante #define CHECK_SPINNER 30
6120dae68f5eSmmusante #define SPINNER_TIME 3 /* seconds */
6121591e0e13SSebastien Roy #define MOUNT_TIME 1 /* seconds */
6122591e0e13SSebastien Roy
6123591e0e13SSebastien Roy typedef struct get_all_state {
6124591e0e13SSebastien Roy boolean_t ga_verbose;
6125591e0e13SSebastien Roy get_all_cb_t *ga_cbp;
6126591e0e13SSebastien Roy } get_all_state_t;
6127dae68f5eSmmusante
61287f7322feSeschrock static int
get_one_dataset(zfs_handle_t * zhp,void * data)6129f3861e1aSahl get_one_dataset(zfs_handle_t *zhp, void *data)
61307f7322feSeschrock {
6131a8b6ddafSMark J Musante static char *spin[] = { "-", "\\", "|", "/" };
6132dae68f5eSmmusante static int spinval = 0;
6133dae68f5eSmmusante static int spincheck = 0;
6134dae68f5eSmmusante static time_t last_spin_time = (time_t)0;
6135591e0e13SSebastien Roy get_all_state_t *state = data;
6136f3861e1aSahl zfs_type_t type = zfs_get_type(zhp);
61377f7322feSeschrock
6138591e0e13SSebastien Roy if (state->ga_verbose) {
6139dae68f5eSmmusante if (--spincheck < 0) {
6140dae68f5eSmmusante time_t now = time(NULL);
6141dae68f5eSmmusante if (last_spin_time + SPINNER_TIME < now) {
6142a8b6ddafSMark J Musante update_progress(spin[spinval++ % 4]);
6143dae68f5eSmmusante last_spin_time = now;
6144dae68f5eSmmusante }
6145dae68f5eSmmusante spincheck = CHECK_SPINNER;
6146dae68f5eSmmusante }
6147dae68f5eSmmusante }
6148dae68f5eSmmusante
61497f7322feSeschrock /*
6150f3861e1aSahl * Interate over any nested datasets.
61517f7322feSeschrock */
61529d9a58e3SEric Taylor if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
61533ccfa83cSahrens zfs_close(zhp);
6154f3861e1aSahl return (1);
6155f3861e1aSahl }
6156f3861e1aSahl
6157f3861e1aSahl /*
6158f3861e1aSahl * Skip any datasets whose type does not match.
6159f3861e1aSahl */
61609d9a58e3SEric Taylor if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
61617f7322feSeschrock zfs_close(zhp);
61627f7322feSeschrock return (0);
61637f7322feSeschrock }
6164591e0e13SSebastien Roy libzfs_add_handle(state->ga_cbp, zhp);
6165591e0e13SSebastien Roy assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
61667f7322feSeschrock
6167f3861e1aSahl return (0);
61687f7322feSeschrock }
61697f7322feSeschrock
61707f7322feSeschrock static void
get_all_datasets(get_all_cb_t * cbp,boolean_t verbose)6171591e0e13SSebastien Roy get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
61727f7322feSeschrock {
6173591e0e13SSebastien Roy get_all_state_t state = {
6174591e0e13SSebastien Roy .ga_verbose = verbose,
6175591e0e13SSebastien Roy .ga_cbp = cbp
6176591e0e13SSebastien Roy };
6177dae68f5eSmmusante
6178a8b6ddafSMark J Musante if (verbose)
6179a8b6ddafSMark J Musante set_progress_header(gettext("Reading ZFS config"));
6180591e0e13SSebastien Roy (void) zfs_iter_root(g_zfs, get_one_dataset, &state);
6181dae68f5eSmmusante
6182a8b6ddafSMark J Musante if (verbose)
6183a8b6ddafSMark J Musante finish_progress(gettext("done."));
61847f7322feSeschrock }
61857f7322feSeschrock
6186fa9e4066Sahrens /*
6187fa9e4066Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so
6188fa9e4066Sahrens * similar, we have a common function with an extra parameter to determine which
6189fa9e4066Sahrens * mode we are using.
6190fa9e4066Sahrens */
6191591e0e13SSebastien Roy typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
6192591e0e13SSebastien Roy
6193591e0e13SSebastien Roy typedef struct share_mount_state {
6194591e0e13SSebastien Roy share_mount_op_t sm_op;
6195591e0e13SSebastien Roy boolean_t sm_verbose;
6196591e0e13SSebastien Roy int sm_flags;
6197591e0e13SSebastien Roy char *sm_options;
6198591e0e13SSebastien Roy char *sm_proto; /* only valid for OP_SHARE */
6199591e0e13SSebastien Roy mutex_t sm_lock; /* protects the remaining fields */
6200591e0e13SSebastien Roy uint_t sm_total; /* number of filesystems to process */
6201591e0e13SSebastien Roy uint_t sm_done; /* number of filesystems processed */
6202591e0e13SSebastien Roy int sm_status; /* -1 if any of the share/mount operations failed */
6203591e0e13SSebastien Roy } share_mount_state_t;
6204fa9e4066Sahrens
6205fa9e4066Sahrens /*
6206f3861e1aSahl * Share or mount a dataset.
6207fa9e4066Sahrens */
6208fa9e4066Sahrens static int
share_mount_one(zfs_handle_t * zhp,int op,int flags,char * protocol,boolean_t explicit,const char * options)6209da6c28aaSamw share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
6210da6c28aaSamw boolean_t explicit, const char *options)
6211fa9e4066Sahrens {
6212fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN];
6213fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN];
6214da6c28aaSamw char smbshareopts[ZFS_MAXPROPLEN];
6215f3861e1aSahl const char *cmdname = op == OP_SHARE ? "share" : "mount";
6216fa9e4066Sahrens struct mnttab mnt;
6217e9dbad6fSeschrock uint64_t zoned, canmount;
6218da6c28aaSamw boolean_t shared_nfs, shared_smb;
6219fa9e4066Sahrens
62209d9a58e3SEric Taylor assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
6221fa9e4066Sahrens
6222fa9e4066Sahrens /*
6223f3861e1aSahl * Check to make sure we can mount/share this dataset. If we
6224f3861e1aSahl * are in the global zone and the filesystem is exported to a
6225f3861e1aSahl * local zone, or if we are in a local zone and the
6226f3861e1aSahl * filesystem is not exported, then it is an error.
6227fa9e4066Sahrens */
6228fa9e4066Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
6229fa9e4066Sahrens
6230fa9e4066Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) {
6231f3861e1aSahl if (!explicit)
6232fa9e4066Sahrens return (0);
6233fa9e4066Sahrens
6234f3861e1aSahl (void) fprintf(stderr, gettext("cannot %s '%s': "
6235f3861e1aSahl "dataset is exported to a local zone\n"), cmdname,
6236f3861e1aSahl zfs_get_name(zhp));
6237fa9e4066Sahrens return (1);
6238fa9e4066Sahrens
6239fa9e4066Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
6240f3861e1aSahl if (!explicit)
6241fa9e4066Sahrens return (0);
6242fa9e4066Sahrens
6243f3861e1aSahl (void) fprintf(stderr, gettext("cannot %s '%s': "
6244f3861e1aSahl "permission denied\n"), cmdname,
6245f3861e1aSahl zfs_get_name(zhp));
6246fa9e4066Sahrens return (1);
6247fa9e4066Sahrens }
6248fa9e4066Sahrens
6249fa9e4066Sahrens /*
6250f3861e1aSahl * Ignore any filesystems which don't apply to us. This
6251f3861e1aSahl * includes those with a legacy mountpoint, or those with
6252f3861e1aSahl * legacy share options.
6253fa9e4066Sahrens */
6254fa9e4066Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
625599653d4eSeschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
6256fa9e4066Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
625799653d4eSeschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
6258da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
6259da6c28aaSamw sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
6260fa9e4066Sahrens
6261da6c28aaSamw if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
6262da6c28aaSamw strcmp(smbshareopts, "off") == 0) {
6263f3861e1aSahl if (!explicit)
6264fa9e4066Sahrens return (0);
6265fa9e4066Sahrens
6266fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot share '%s': "
6267fa9e4066Sahrens "legacy share\n"), zfs_get_name(zhp));
6268bbf21555SRichard Lowe (void) fprintf(stderr, gettext("use share(8) to "
6269deb8317bSMark J Musante "share this filesystem, or set "
6270deb8317bSMark J Musante "sharenfs property on\n"));
6271fa9e4066Sahrens return (1);
6272fa9e4066Sahrens }
6273fa9e4066Sahrens
6274fa9e4066Sahrens /*
6275f3861e1aSahl * We cannot share or mount legacy filesystems. If the
6276f3861e1aSahl * shareopts is non-legacy but the mountpoint is legacy, we
6277f3861e1aSahl * treat it as a legacy share.
6278fa9e4066Sahrens */
6279fa9e4066Sahrens if (strcmp(mountpoint, "legacy") == 0) {
6280f3861e1aSahl if (!explicit)
6281fa9e4066Sahrens return (0);
6282fa9e4066Sahrens
6283fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': "
6284fa9e4066Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
6285bbf21555SRichard Lowe (void) fprintf(stderr, gettext("use %s(8) to "
62863cb34c60Sahrens "%s this filesystem\n"), cmdname, cmdname);
6287fa9e4066Sahrens return (1);
6288fa9e4066Sahrens }
6289fa9e4066Sahrens
6290fa9e4066Sahrens if (strcmp(mountpoint, "none") == 0) {
6291f3861e1aSahl if (!explicit)
6292fa9e4066Sahrens return (0);
6293fa9e4066Sahrens
6294fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no "
6295fa9e4066Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp));
6296fa9e4066Sahrens return (1);
6297fa9e4066Sahrens }
6298fa9e4066Sahrens
6299a227b7f4Shs24103 /*
6300a227b7f4Shs24103 * canmount explicit outcome
6301a227b7f4Shs24103 * on no pass through
6302a227b7f4Shs24103 * on yes pass through
6303a227b7f4Shs24103 * off no return 0
6304a227b7f4Shs24103 * off yes display error, return 1
6305a227b7f4Shs24103 * noauto no return 0
6306a227b7f4Shs24103 * noauto yes pass through
6307a227b7f4Shs24103 */
6308deb8317bSMark J Musante canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
6309a227b7f4Shs24103 if (canmount == ZFS_CANMOUNT_OFF) {
6310f3861e1aSahl if (!explicit)
6311e9dbad6fSeschrock return (0);
6312e9dbad6fSeschrock
6313f3861e1aSahl (void) fprintf(stderr, gettext("cannot %s '%s': "
6314f3861e1aSahl "'canmount' property is set to 'off'\n"), cmdname,
6315f3861e1aSahl zfs_get_name(zhp));
6316e9dbad6fSeschrock return (1);
6317a227b7f4Shs24103 } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
6318a227b7f4Shs24103 return (0);
6319e9dbad6fSeschrock }
6320e9dbad6fSeschrock
6321fa9e4066Sahrens /*
6322eb633035STom Caputi * If this filesystem is encrypted and does not have
6323eb633035STom Caputi * a loaded key, we can not mount it.
6324eb633035STom Caputi */
6325eb633035STom Caputi if ((flags & MS_CRYPT) == 0 &&
6326eb633035STom Caputi zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
6327eb633035STom Caputi zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
6328eb633035STom Caputi ZFS_KEYSTATUS_UNAVAILABLE) {
6329eb633035STom Caputi if (!explicit)
6330eb633035STom Caputi return (0);
6331eb633035STom Caputi
6332eb633035STom Caputi (void) fprintf(stderr, gettext("cannot %s '%s': "
6333eb633035STom Caputi "encryption key not loaded\n"), cmdname, zfs_get_name(zhp));
6334eb633035STom Caputi return (1);
6335eb633035STom Caputi }
6336eb633035STom Caputi
6337eb633035STom Caputi /*
63389c3fd121SMatthew Ahrens * If this filesystem is inconsistent and has a receive resume
63399c3fd121SMatthew Ahrens * token, we can not mount it.
63409c3fd121SMatthew Ahrens */
63419c3fd121SMatthew Ahrens if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
63429c3fd121SMatthew Ahrens zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
63439c3fd121SMatthew Ahrens NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
63449c3fd121SMatthew Ahrens if (!explicit)
63459c3fd121SMatthew Ahrens return (0);
63469c3fd121SMatthew Ahrens
63479c3fd121SMatthew Ahrens (void) fprintf(stderr, gettext("cannot %s '%s': "
63489c3fd121SMatthew Ahrens "Contains partially-completed state from "
63496ccda740Sloli10K "\"zfs receive -s\", which can be resumed with "
63509c3fd121SMatthew Ahrens "\"zfs send -t\"\n"),
63519c3fd121SMatthew Ahrens cmdname, zfs_get_name(zhp));
63529c3fd121SMatthew Ahrens return (1);
63539c3fd121SMatthew Ahrens }
63549c3fd121SMatthew Ahrens
63559c3fd121SMatthew Ahrens /*
6356f3861e1aSahl * At this point, we have verified that the mountpoint and/or
6357f3861e1aSahl * shareopts are appropriate for auto management. If the
6358f3861e1aSahl * filesystem is already mounted or shared, return (failing
6359f3861e1aSahl * for explicit requests); otherwise mount or share the
6360f3861e1aSahl * filesystem.
6361fa9e4066Sahrens */
6362f3861e1aSahl switch (op) {
6363fa9e4066Sahrens case OP_SHARE:
6364da6c28aaSamw
6365da6c28aaSamw shared_nfs = zfs_is_shared_nfs(zhp, NULL);
6366da6c28aaSamw shared_smb = zfs_is_shared_smb(zhp, NULL);
6367da6c28aaSamw
6368c16bcc45SIgor Kozhukhov if ((shared_nfs && shared_smb) ||
6369da6c28aaSamw (shared_nfs && strcmp(shareopts, "on") == 0 &&
6370da6c28aaSamw strcmp(smbshareopts, "off") == 0) ||
6371da6c28aaSamw (shared_smb && strcmp(smbshareopts, "on") == 0 &&
6372da6c28aaSamw strcmp(shareopts, "off") == 0)) {
6373f3861e1aSahl if (!explicit)
6374f3861e1aSahl return (0);
6375f3861e1aSahl
6376fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot share "
6377fa9e4066Sahrens "'%s': filesystem already shared\n"),
6378fa9e4066Sahrens zfs_get_name(zhp));
6379fa9e4066Sahrens return (1);
6380fa9e4066Sahrens }
6381fa9e4066Sahrens
6382fa9e4066Sahrens if (!zfs_is_mounted(zhp, NULL) &&
6383eb633035STom Caputi zfs_mount(zhp, NULL, flags) != 0)
6384fa9e4066Sahrens return (1);
6385fa9e4066Sahrens
6386da6c28aaSamw if (protocol == NULL) {
6387da6c28aaSamw if (zfs_shareall(zhp) != 0)
6388fa9e4066Sahrens return (1);
6389da6c28aaSamw } else if (strcmp(protocol, "nfs") == 0) {
6390da6c28aaSamw if (zfs_share_nfs(zhp))
6391da6c28aaSamw return (1);
6392da6c28aaSamw } else if (strcmp(protocol, "smb") == 0) {
6393da6c28aaSamw if (zfs_share_smb(zhp))
6394da6c28aaSamw return (1);
6395da6c28aaSamw } else {
6396da6c28aaSamw (void) fprintf(stderr, gettext("cannot share "
6397da6c28aaSamw "'%s': invalid share type '%s' "
6398da6c28aaSamw "specified\n"),
6399da6c28aaSamw zfs_get_name(zhp), protocol);
6400da6c28aaSamw return (1);
6401da6c28aaSamw }
6402da6c28aaSamw
6403fa9e4066Sahrens break;
6404fa9e4066Sahrens
6405fa9e4066Sahrens case OP_MOUNT:
6406f3861e1aSahl if (options == NULL)
6407f3861e1aSahl mnt.mnt_mntopts = "";
6408f3861e1aSahl else
6409f3861e1aSahl mnt.mnt_mntopts = (char *)options;
6410f3861e1aSahl
6411f3861e1aSahl if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
6412f3861e1aSahl zfs_is_mounted(zhp, NULL)) {
6413f3861e1aSahl if (!explicit)
6414f3861e1aSahl return (0);
6415f3861e1aSahl
6416f3861e1aSahl (void) fprintf(stderr, gettext("cannot mount "
6417f3861e1aSahl "'%s': filesystem already mounted\n"),
6418f3861e1aSahl zfs_get_name(zhp));
6419f3861e1aSahl return (1);
6420f3861e1aSahl }
6421f3861e1aSahl
6422f3861e1aSahl if (zfs_mount(zhp, options, flags) != 0)
6423fa9e4066Sahrens return (1);
6424fa9e4066Sahrens break;
6425fa9e4066Sahrens }
6426f3861e1aSahl
6427fa9e4066Sahrens return (0);
6428fa9e4066Sahrens }
6429fa9e4066Sahrens
6430dae68f5eSmmusante /*
6431dae68f5eSmmusante * Reports progress in the form "(current/total)". Not thread-safe.
6432dae68f5eSmmusante */
6433dae68f5eSmmusante static void
report_mount_progress(int current,int total)6434dae68f5eSmmusante report_mount_progress(int current, int total)
6435dae68f5eSmmusante {
6436a8b6ddafSMark J Musante static time_t last_progress_time = 0;
6437dae68f5eSmmusante time_t now = time(NULL);
6438a8b6ddafSMark J Musante char info[32];
6439dae68f5eSmmusante
6440dae68f5eSmmusante /* display header if we're here for the first time */
6441dae68f5eSmmusante if (current == 1) {
6442a8b6ddafSMark J Musante set_progress_header(gettext("Mounting ZFS filesystems"));
64433cb34c60Sahrens } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
64443cb34c60Sahrens /* too soon to report again */
64453cb34c60Sahrens return;
64463cb34c60Sahrens }
6447dae68f5eSmmusante
6448dae68f5eSmmusante last_progress_time = now;
6449dae68f5eSmmusante
6450a8b6ddafSMark J Musante (void) sprintf(info, "(%d/%d)", current, total);
6451dae68f5eSmmusante
6452a8b6ddafSMark J Musante if (current == total)
6453a8b6ddafSMark J Musante finish_progress(info);
6454a8b6ddafSMark J Musante else
6455a8b6ddafSMark J Musante update_progress(info);
6456dae68f5eSmmusante }
6457dae68f5eSmmusante
6458591e0e13SSebastien Roy /*
6459591e0e13SSebastien Roy * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
6460591e0e13SSebastien Roy * updates the progress meter.
6461591e0e13SSebastien Roy */
6462591e0e13SSebastien Roy static int
share_mount_one_cb(zfs_handle_t * zhp,void * arg)6463591e0e13SSebastien Roy share_mount_one_cb(zfs_handle_t *zhp, void *arg)
6464591e0e13SSebastien Roy {
6465591e0e13SSebastien Roy share_mount_state_t *sms = arg;
6466591e0e13SSebastien Roy int ret;
6467591e0e13SSebastien Roy
6468591e0e13SSebastien Roy ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
6469591e0e13SSebastien Roy B_FALSE, sms->sm_options);
6470591e0e13SSebastien Roy
6471591e0e13SSebastien Roy mutex_enter(&sms->sm_lock);
6472591e0e13SSebastien Roy if (ret != 0)
6473591e0e13SSebastien Roy sms->sm_status = ret;
6474591e0e13SSebastien Roy sms->sm_done++;
6475591e0e13SSebastien Roy if (sms->sm_verbose)
6476591e0e13SSebastien Roy report_mount_progress(sms->sm_done, sms->sm_total);
6477591e0e13SSebastien Roy mutex_exit(&sms->sm_lock);
6478591e0e13SSebastien Roy return (ret);
6479591e0e13SSebastien Roy }
6480591e0e13SSebastien Roy
6481c6ef114fSmmusante static void
append_options(char * mntopts,char * newopts)6482c6ef114fSmmusante append_options(char *mntopts, char *newopts)
6483c6ef114fSmmusante {
6484c6ef114fSmmusante int len = strlen(mntopts);
6485c6ef114fSmmusante
6486c6ef114fSmmusante /* original length plus new string to append plus 1 for the comma */
6487c6ef114fSmmusante if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
6488c6ef114fSmmusante (void) fprintf(stderr, gettext("the opts argument for "
6489c6ef114fSmmusante "'%c' option is too long (more than %d chars)\n"),
6490c6ef114fSmmusante "-o", MNT_LINE_MAX);
6491c6ef114fSmmusante usage(B_FALSE);
6492c6ef114fSmmusante }
6493c6ef114fSmmusante
6494c6ef114fSmmusante if (*mntopts)
6495c6ef114fSmmusante mntopts[len++] = ',';
6496c6ef114fSmmusante
6497c6ef114fSmmusante (void) strcpy(&mntopts[len], newopts);
6498c6ef114fSmmusante }
6499c6ef114fSmmusante
6500fa9e4066Sahrens static int
share_mount(int op,int argc,char ** argv)6501f3861e1aSahl share_mount(int op, int argc, char **argv)
6502fa9e4066Sahrens {
6503fa9e4066Sahrens int do_all = 0;
6504dae68f5eSmmusante boolean_t verbose = B_FALSE;
650516299908Slling int c, ret = 0;
6506c6ef114fSmmusante char *options = NULL;
65079d9a58e3SEric Taylor int flags = 0;
6508fa9e4066Sahrens
6509fa9e4066Sahrens /* check options */
6510eb633035STom Caputi while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:O" : "al"))
6511fa9e4066Sahrens != -1) {
6512fa9e4066Sahrens switch (c) {
6513fa9e4066Sahrens case 'a':
6514fa9e4066Sahrens do_all = 1;
6515fa9e4066Sahrens break;
6516dae68f5eSmmusante case 'v':
6517dae68f5eSmmusante verbose = B_TRUE;
6518dae68f5eSmmusante break;
6519eb633035STom Caputi case 'l':
6520eb633035STom Caputi flags |= MS_CRYPT;
6521eb633035STom Caputi break;
6522fa9e4066Sahrens case 'o':
6523c6ef114fSmmusante if (*optarg == '\0') {
6524c6ef114fSmmusante (void) fprintf(stderr, gettext("empty mount "
6525c6ef114fSmmusante "options (-o) specified\n"));
6526ce5e3b86Srm160521 usage(B_FALSE);
6527c6ef114fSmmusante }
6528c6ef114fSmmusante
6529c6ef114fSmmusante if (options == NULL)
6530c6ef114fSmmusante options = safe_malloc(MNT_LINE_MAX + 1);
6531c6ef114fSmmusante
6532c6ef114fSmmusante /* option validation is done later */
6533c6ef114fSmmusante append_options(options, optarg);
6534ce5e3b86Srm160521 break;
6535ce5e3b86Srm160521
6536fa9e4066Sahrens case 'O':
6537f3861e1aSahl flags |= MS_OVERLAY;
6538fa9e4066Sahrens break;
6539fa9e4066Sahrens case ':':
6540fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for "
6541fa9e4066Sahrens "'%c' option\n"), optopt);
654299653d4eSeschrock usage(B_FALSE);
6543fa9e4066Sahrens break;
6544fa9e4066Sahrens case '?':
6545fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6546fa9e4066Sahrens optopt);
654799653d4eSeschrock usage(B_FALSE);
6548fa9e4066Sahrens }
6549fa9e4066Sahrens }
6550fa9e4066Sahrens
6551fa9e4066Sahrens argc -= optind;
6552fa9e4066Sahrens argv += optind;
6553fa9e4066Sahrens
6554fa9e4066Sahrens /* check number of arguments */
6555fa9e4066Sahrens if (do_all) {
6556da6c28aaSamw char *protocol = NULL;
65577f7322feSeschrock
65589d9a58e3SEric Taylor if (op == OP_SHARE && argc > 0) {
65599d9a58e3SEric Taylor if (strcmp(argv[0], "nfs") != 0 &&
65609d9a58e3SEric Taylor strcmp(argv[0], "smb") != 0) {
6561f3861e1aSahl (void) fprintf(stderr, gettext("share type "
6562ab003da8SJim Dunham "must be 'nfs' or 'smb'\n"));
6563f3861e1aSahl usage(B_FALSE);
6564f3861e1aSahl }
6565da6c28aaSamw protocol = argv[0];
6566f3861e1aSahl argc--;
6567f3861e1aSahl argv++;
6568f3861e1aSahl }
6569f3861e1aSahl
6570fa9e4066Sahrens if (argc != 0) {
6571fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
657299653d4eSeschrock usage(B_FALSE);
6573fa9e4066Sahrens }
6574fa9e4066Sahrens
6575a8b6ddafSMark J Musante start_progress_timer();
6576591e0e13SSebastien Roy get_all_cb_t cb = { 0 };
6577591e0e13SSebastien Roy get_all_datasets(&cb, verbose);
65787f7322feSeschrock
6579591e0e13SSebastien Roy if (cb.cb_used == 0)
65807f7322feSeschrock return (0);
65817f7322feSeschrock
6582591e0e13SSebastien Roy if (op == OP_SHARE) {
65838a981c33SDaniel Hoffman sa_init_selective_arg_t sharearg;
6584591e0e13SSebastien Roy sharearg.zhandle_arr = cb.cb_handles;
6585591e0e13SSebastien Roy sharearg.zhandle_len = cb.cb_used;
6586591e0e13SSebastien Roy if ((ret = zfs_init_libshare_arg(g_zfs,
65878a981c33SDaniel Hoffman SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
6588591e0e13SSebastien Roy (void) fprintf(stderr, gettext(
6589591e0e13SSebastien Roy "Could not initialize libshare, %d"), ret);
65908a981c33SDaniel Hoffman return (ret);
65918a981c33SDaniel Hoffman }
65927f7322feSeschrock }
65937f7322feSeschrock
6594591e0e13SSebastien Roy share_mount_state_t share_mount_state = { 0 };
6595591e0e13SSebastien Roy share_mount_state.sm_op = op;
6596591e0e13SSebastien Roy share_mount_state.sm_verbose = verbose;
6597591e0e13SSebastien Roy share_mount_state.sm_flags = flags;
6598591e0e13SSebastien Roy share_mount_state.sm_options = options;
6599591e0e13SSebastien Roy share_mount_state.sm_proto = protocol;
6600591e0e13SSebastien Roy share_mount_state.sm_total = cb.cb_used;
6601591e0e13SSebastien Roy (void) mutex_init(&share_mount_state.sm_lock,
6602591e0e13SSebastien Roy LOCK_NORMAL | LOCK_ERRORCHECK, NULL);
6603591e0e13SSebastien Roy /*
6604591e0e13SSebastien Roy * libshare isn't mt-safe, so only do the operation in parallel
6605591e0e13SSebastien Roy * if we're mounting.
6606591e0e13SSebastien Roy */
6607591e0e13SSebastien Roy zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
6608591e0e13SSebastien Roy share_mount_one_cb, &share_mount_state, op == OP_MOUNT);
6609591e0e13SSebastien Roy ret = share_mount_state.sm_status;
6610591e0e13SSebastien Roy
6611591e0e13SSebastien Roy for (int i = 0; i < cb.cb_used; i++)
6612591e0e13SSebastien Roy zfs_close(cb.cb_handles[i]);
6613591e0e13SSebastien Roy free(cb.cb_handles);
6614fa9e4066Sahrens } else if (argc == 0) {
6615fa9e4066Sahrens struct mnttab entry;
6616fa9e4066Sahrens
6617ce5e3b86Srm160521 if ((op == OP_SHARE) || (options != NULL)) {
6618fa9e4066Sahrens (void) fprintf(stderr, gettext("missing filesystem "
6619ce5e3b86Srm160521 "argument (specify -a for all)\n"));
662099653d4eSeschrock usage(B_FALSE);
6621fa9e4066Sahrens }
6622fa9e4066Sahrens
6623fa9e4066Sahrens /*
6624fa9e4066Sahrens * When mount is given no arguments, go through /etc/mnttab and
6625fa9e4066Sahrens * display any active ZFS mounts. We hide any snapshots, since
6626fa9e4066Sahrens * they are controlled automatically.
6627fa9e4066Sahrens */
6628fa9e4066Sahrens rewind(mnttab_file);
6629fa9e4066Sahrens while (getmntent(mnttab_file, &entry) == 0) {
6630fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
6631fa9e4066Sahrens strchr(entry.mnt_special, '@') != NULL)
6632fa9e4066Sahrens continue;
6633fa9e4066Sahrens
6634fa9e4066Sahrens (void) printf("%-30s %s\n", entry.mnt_special,
6635fa9e4066Sahrens entry.mnt_mountp);
6636fa9e4066Sahrens }
6637fa9e4066Sahrens
6638fa9e4066Sahrens } else {
6639fa9e4066Sahrens zfs_handle_t *zhp;
6640fa9e4066Sahrens
6641fa9e4066Sahrens if (argc > 1) {
6642fa9e4066Sahrens (void) fprintf(stderr,
6643fa9e4066Sahrens gettext("too many arguments\n"));
664499653d4eSeschrock usage(B_FALSE);
6645fa9e4066Sahrens }
6646fa9e4066Sahrens
66479d9a58e3SEric Taylor if ((zhp = zfs_open(g_zfs, argv[0],
66489d9a58e3SEric Taylor ZFS_TYPE_FILESYSTEM)) == NULL) {
6649fa9e4066Sahrens ret = 1;
6650f3861e1aSahl } else {
6651da6c28aaSamw ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
6652f3861e1aSahl options);
6653fa9e4066Sahrens zfs_close(zhp);
6654fa9e4066Sahrens }
6655fa9e4066Sahrens }
6656fa9e4066Sahrens
6657fa9e4066Sahrens return (ret);
6658fa9e4066Sahrens }
6659fa9e4066Sahrens
6660fa9e4066Sahrens /*
6661ab003da8SJim Dunham * zfs mount -a [nfs]
6662fa9e4066Sahrens * zfs mount filesystem
6663fa9e4066Sahrens *
6664fa9e4066Sahrens * Mount all filesystems, or mount the given filesystem.
6665fa9e4066Sahrens */
6666fa9e4066Sahrens static int
zfs_do_mount(int argc,char ** argv)6667fa9e4066Sahrens zfs_do_mount(int argc, char **argv)
6668fa9e4066Sahrens {
6669f3861e1aSahl return (share_mount(OP_MOUNT, argc, argv));
6670fa9e4066Sahrens }
6671fa9e4066Sahrens
6672fa9e4066Sahrens /*
6673ab003da8SJim Dunham * zfs share -a [nfs | smb]
6674fa9e4066Sahrens * zfs share filesystem
6675fa9e4066Sahrens *
6676fa9e4066Sahrens * Share all filesystems, or share the given filesystem.
6677fa9e4066Sahrens */
6678fa9e4066Sahrens static int
zfs_do_share(int argc,char ** argv)6679fa9e4066Sahrens zfs_do_share(int argc, char **argv)
6680fa9e4066Sahrens {
6681f3861e1aSahl return (share_mount(OP_SHARE, argc, argv));
6682fa9e4066Sahrens }
6683fa9e4066Sahrens
6684fa9e4066Sahrens typedef struct unshare_unmount_node {
6685fa9e4066Sahrens zfs_handle_t *un_zhp;
6686fa9e4066Sahrens char *un_mountp;
6687fa9e4066Sahrens uu_avl_node_t un_avlnode;
6688fa9e4066Sahrens } unshare_unmount_node_t;
6689fa9e4066Sahrens
6690fa9e4066Sahrens /* ARGSUSED */
6691fa9e4066Sahrens static int
unshare_unmount_compare(const void * larg,const void * rarg,void * unused)6692fa9e4066Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
6693fa9e4066Sahrens {
6694fa9e4066Sahrens const unshare_unmount_node_t *l = larg;
6695fa9e4066Sahrens const unshare_unmount_node_t *r = rarg;
6696fa9e4066Sahrens
6697fa9e4066Sahrens return (strcmp(l->un_mountp, r->un_mountp));
6698fa9e4066Sahrens }
6699fa9e4066Sahrens
6700fa9e4066Sahrens /*
6701fa9e4066Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
6702fa9e4066Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
6703fa9e4066Sahrens * and unmount it appropriately.
6704fa9e4066Sahrens */
6705fa9e4066Sahrens static int
unshare_unmount_path(int op,char * path,int flags,boolean_t is_manual)6706f3861e1aSahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
6707fa9e4066Sahrens {
6708fa9e4066Sahrens zfs_handle_t *zhp;
670905c998d6SRichard Lowe int ret = 0;
6710fa9e4066Sahrens struct stat64 statbuf;
6711fa9e4066Sahrens struct extmnttab entry;
6712f3861e1aSahl const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
6713c6ef114fSmmusante ino_t path_inode;
6714fa9e4066Sahrens
6715fa9e4066Sahrens /*
6716fa9e4066Sahrens * Search for the path in /etc/mnttab. Rather than looking for the
6717fa9e4066Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".."
6718fa9e4066Sahrens * or "//"), we stat() the path and search for the corresponding
6719fa9e4066Sahrens * (major,minor) device pair.
6720fa9e4066Sahrens */
6721fa9e4066Sahrens if (stat64(path, &statbuf) != 0) {
6722fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6723fa9e4066Sahrens cmdname, path, strerror(errno));
6724fa9e4066Sahrens return (1);
6725fa9e4066Sahrens }
6726c6ef114fSmmusante path_inode = statbuf.st_ino;
6727fa9e4066Sahrens
6728fa9e4066Sahrens /*
6729fa9e4066Sahrens * Search for the given (major,minor) pair in the mount table.
6730fa9e4066Sahrens */
6731fa9e4066Sahrens rewind(mnttab_file);
6732fa9e4066Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
6733fa9e4066Sahrens if (entry.mnt_major == major(statbuf.st_dev) &&
6734fa9e4066Sahrens entry.mnt_minor == minor(statbuf.st_dev))
6735fa9e4066Sahrens break;
6736fa9e4066Sahrens }
6737fa9e4066Sahrens if (ret != 0) {
673811022c7cStimh if (op == OP_SHARE) {
6739fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not "
6740fa9e4066Sahrens "currently mounted\n"), cmdname, path);
6741fa9e4066Sahrens return (1);
6742fa9e4066Sahrens }
674311022c7cStimh (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
674411022c7cStimh path);
674511022c7cStimh if ((ret = umount2(path, flags)) != 0)
674611022c7cStimh (void) fprintf(stderr, gettext("%s: %s\n"), path,
674711022c7cStimh strerror(errno));
674811022c7cStimh return (ret != 0);
674911022c7cStimh }
6750fa9e4066Sahrens
6751fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
6752fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
6753fa9e4066Sahrens "filesystem\n"), cmdname, path);
6754fa9e4066Sahrens return (1);
6755fa9e4066Sahrens }
6756fa9e4066Sahrens
675799653d4eSeschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special,
675899653d4eSeschrock ZFS_TYPE_FILESYSTEM)) == NULL)
6759fa9e4066Sahrens return (1);
6760fa9e4066Sahrens
6761c6ef114fSmmusante ret = 1;
6762d72c03d9SSachin Gaikwad if (stat64(entry.mnt_mountp, &statbuf) != 0) {
6763d72c03d9SSachin Gaikwad (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6764d72c03d9SSachin Gaikwad cmdname, path, strerror(errno));
6765d72c03d9SSachin Gaikwad goto out;
6766d72c03d9SSachin Gaikwad } else if (statbuf.st_ino != path_inode) {
6767d72c03d9SSachin Gaikwad (void) fprintf(stderr, gettext("cannot "
6768d72c03d9SSachin Gaikwad "%s '%s': not a mountpoint\n"), cmdname, path);
6769d72c03d9SSachin Gaikwad goto out;
6770d72c03d9SSachin Gaikwad }
6771d72c03d9SSachin Gaikwad
6772c6ef114fSmmusante if (op == OP_SHARE) {
6773c6ef114fSmmusante char nfs_mnt_prop[ZFS_MAXPROPLEN];
6774c6ef114fSmmusante char smbshare_prop[ZFS_MAXPROPLEN];
6775c6ef114fSmmusante
6776c6ef114fSmmusante verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
6777da6c28aaSamw sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
6778c6ef114fSmmusante verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
6779da6c28aaSamw sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
6780fa9e4066Sahrens
6781da6c28aaSamw if (strcmp(nfs_mnt_prop, "off") == 0 &&
6782da6c28aaSamw strcmp(smbshare_prop, "off") == 0) {
6783fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare "
6784fa9e4066Sahrens "'%s': legacy share\n"), path);
6785fa9e4066Sahrens (void) fprintf(stderr, gettext("use "
6786bbf21555SRichard Lowe "unshare(8) to unshare this filesystem\n"));
6787da6c28aaSamw } else if (!zfs_is_shared(zhp)) {
6788fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': "
6789fa9e4066Sahrens "not currently shared\n"), path);
6790fa9e4066Sahrens } else {
6791da6c28aaSamw ret = zfs_unshareall_bypath(zhp, path);
6792fa9e4066Sahrens }
6793fa9e4066Sahrens } else {
6794c6ef114fSmmusante char mtpt_prop[ZFS_MAXPROPLEN];
6795c6ef114fSmmusante
6796c6ef114fSmmusante verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
6797c6ef114fSmmusante sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
6798c6ef114fSmmusante
6799d72c03d9SSachin Gaikwad if (is_manual) {
68006b90ca48Slling ret = zfs_unmount(zhp, NULL, flags);
6801c6ef114fSmmusante } else if (strcmp(mtpt_prop, "legacy") == 0) {
6802fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unmount "
6803fa9e4066Sahrens "'%s': legacy mountpoint\n"),
6804fa9e4066Sahrens zfs_get_name(zhp));
6805bbf21555SRichard Lowe (void) fprintf(stderr, gettext("use umount(8) "
6806fa9e4066Sahrens "to unmount this filesystem\n"));
6807fa9e4066Sahrens } else {
6808fa9e4066Sahrens ret = zfs_unmountall(zhp, flags);
6809fa9e4066Sahrens }
6810fa9e4066Sahrens }
6811fa9e4066Sahrens
6812d72c03d9SSachin Gaikwad out:
6813fa9e4066Sahrens zfs_close(zhp);
6814fa9e4066Sahrens
6815fa9e4066Sahrens return (ret != 0);
6816fa9e4066Sahrens }
6817fa9e4066Sahrens
6818fa9e4066Sahrens /*
6819fa9e4066Sahrens * Generic callback for unsharing or unmounting a filesystem.
6820fa9e4066Sahrens */
6821fa9e4066Sahrens static int
unshare_unmount(int op,int argc,char ** argv)6822f3861e1aSahl unshare_unmount(int op, int argc, char **argv)
6823fa9e4066Sahrens {
6824fa9e4066Sahrens int do_all = 0;
6825fa9e4066Sahrens int flags = 0;
6826fa9e4066Sahrens int ret = 0;
68279d9a58e3SEric Taylor int c;
6828fa9e4066Sahrens zfs_handle_t *zhp;
6829ab003da8SJim Dunham char nfs_mnt_prop[ZFS_MAXPROPLEN];
6830da6c28aaSamw char sharesmb[ZFS_MAXPROPLEN];
6831fa9e4066Sahrens
6832fa9e4066Sahrens /* check options */
6833f3861e1aSahl while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
6834fa9e4066Sahrens switch (c) {
6835fa9e4066Sahrens case 'a':
6836fa9e4066Sahrens do_all = 1;
6837fa9e4066Sahrens break;
6838fa9e4066Sahrens case 'f':
6839fa9e4066Sahrens flags = MS_FORCE;
6840fa9e4066Sahrens break;
6841fa9e4066Sahrens case '?':
6842fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6843fa9e4066Sahrens optopt);
684499653d4eSeschrock usage(B_FALSE);
6845fa9e4066Sahrens }
6846fa9e4066Sahrens }
6847fa9e4066Sahrens
6848fa9e4066Sahrens argc -= optind;
6849fa9e4066Sahrens argv += optind;
6850fa9e4066Sahrens
6851fa9e4066Sahrens if (do_all) {
6852fa9e4066Sahrens /*
6853fa9e4066Sahrens * We could make use of zfs_for_each() to walk all datasets in
6854fa9e4066Sahrens * the system, but this would be very inefficient, especially
6855fa9e4066Sahrens * since we would have to linearly search /etc/mnttab for each
6856fa9e4066Sahrens * one. Instead, do one pass through /etc/mnttab looking for
6857fa9e4066Sahrens * zfs entries and call zfs_unmount() for each one.
6858fa9e4066Sahrens *
6859fa9e4066Sahrens * Things get a little tricky if the administrator has created
6860fa9e4066Sahrens * mountpoints beneath other ZFS filesystems. In this case, we
6861fa9e4066Sahrens * have to unmount the deepest filesystems first. To accomplish
6862fa9e4066Sahrens * this, we place all the mountpoints in an AVL tree sorted by
6863fa9e4066Sahrens * the special type (dataset name), and walk the result in
6864fa9e4066Sahrens * reverse to make sure to get any snapshots first.
6865fa9e4066Sahrens */
6866fa9e4066Sahrens struct mnttab entry;
6867fa9e4066Sahrens uu_avl_pool_t *pool;
6868c16bcc45SIgor Kozhukhov uu_avl_t *tree = NULL;
6869fa9e4066Sahrens unshare_unmount_node_t *node;
6870fa9e4066Sahrens uu_avl_index_t idx;
6871fa9e4066Sahrens uu_avl_walk_t *walk;
6872fa9e4066Sahrens
6873f3861e1aSahl if (argc != 0) {
6874f3861e1aSahl (void) fprintf(stderr, gettext("too many arguments\n"));
6875f3861e1aSahl usage(B_FALSE);
6876f3861e1aSahl }
6877f3861e1aSahl
6878a8b6ddafSMark J Musante if (((pool = uu_avl_pool_create("unmount_pool",
6879fa9e4066Sahrens sizeof (unshare_unmount_node_t),
6880fa9e4066Sahrens offsetof(unshare_unmount_node_t, un_avlnode),
6881a8b6ddafSMark J Musante unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
6882a8b6ddafSMark J Musante ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
6883a8b6ddafSMark J Musante nomem();
6884fa9e4066Sahrens
6885fa9e4066Sahrens rewind(mnttab_file);
6886fa9e4066Sahrens while (getmntent(mnttab_file, &entry) == 0) {
6887fa9e4066Sahrens
6888fa9e4066Sahrens /* ignore non-ZFS entries */
6889fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6890fa9e4066Sahrens continue;
6891fa9e4066Sahrens
6892fa9e4066Sahrens /* ignore snapshots */
6893fa9e4066Sahrens if (strchr(entry.mnt_special, '@') != NULL)
6894fa9e4066Sahrens continue;
6895fa9e4066Sahrens
689699653d4eSeschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6897fa9e4066Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) {
6898fa9e4066Sahrens ret = 1;
6899fa9e4066Sahrens continue;
6900fa9e4066Sahrens }
6901fa9e4066Sahrens
69028808ac5dSYuri Pankov /*
69038808ac5dSYuri Pankov * Ignore datasets that are excluded/restricted by
69048808ac5dSYuri Pankov * parent pool name.
69058808ac5dSYuri Pankov */
69068808ac5dSYuri Pankov if (zpool_skip_pool(zfs_get_pool_name(zhp))) {
69078808ac5dSYuri Pankov zfs_close(zhp);
69088808ac5dSYuri Pankov continue;
69098808ac5dSYuri Pankov }
69108808ac5dSYuri Pankov
6911da6c28aaSamw switch (op) {
6912da6c28aaSamw case OP_SHARE:
6913da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6914ab003da8SJim Dunham nfs_mnt_prop,
6915ab003da8SJim Dunham sizeof (nfs_mnt_prop),
6916da6c28aaSamw NULL, NULL, 0, B_FALSE) == 0);
6917ab003da8SJim Dunham if (strcmp(nfs_mnt_prop, "off") != 0)
6918da6c28aaSamw break;
6919da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6920ab003da8SJim Dunham nfs_mnt_prop,
6921ab003da8SJim Dunham sizeof (nfs_mnt_prop),
6922da6c28aaSamw NULL, NULL, 0, B_FALSE) == 0);
6923ab003da8SJim Dunham if (strcmp(nfs_mnt_prop, "off") == 0)
6924fa9e4066Sahrens continue;
6925da6c28aaSamw break;
6926da6c28aaSamw case OP_MOUNT:
6927da6c28aaSamw /* Ignore legacy mounts */
6928da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
6929ab003da8SJim Dunham nfs_mnt_prop,
6930ab003da8SJim Dunham sizeof (nfs_mnt_prop),
6931da6c28aaSamw NULL, NULL, 0, B_FALSE) == 0);
6932ab003da8SJim Dunham if (strcmp(nfs_mnt_prop, "legacy") == 0)
6933da6c28aaSamw continue;
6934a227b7f4Shs24103 /* Ignore canmount=noauto mounts */
6935a227b7f4Shs24103 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
6936a227b7f4Shs24103 ZFS_CANMOUNT_NOAUTO)
6937a227b7f4Shs24103 continue;
6938da6c28aaSamw default:
6939da6c28aaSamw break;
6940fa9e4066Sahrens }
6941fa9e4066Sahrens
6942fa9e4066Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t));
6943fa9e4066Sahrens node->un_zhp = zhp;
6944a8b6ddafSMark J Musante node->un_mountp = safe_strdup(entry.mnt_mountp);
6945fa9e4066Sahrens
6946fa9e4066Sahrens uu_avl_node_init(node, &node->un_avlnode, pool);
6947fa9e4066Sahrens
6948fa9e4066Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
6949fa9e4066Sahrens uu_avl_insert(tree, node, idx);
6950fa9e4066Sahrens } else {
6951fa9e4066Sahrens zfs_close(node->un_zhp);
6952fa9e4066Sahrens free(node->un_mountp);
6953fa9e4066Sahrens free(node);
6954fa9e4066Sahrens }
6955fa9e4066Sahrens }
6956fa9e4066Sahrens
6957fa9e4066Sahrens /*
6958b8dc1b43SJoyce McIntosh * Initialize libshare SA_INIT_SHARE_API_SELECTIVE here
6959b8dc1b43SJoyce McIntosh * to avoid unnecessary load/unload of the libshare API
6960b8dc1b43SJoyce McIntosh * per shared dataset downstream.
6961b8dc1b43SJoyce McIntosh */
6962b8dc1b43SJoyce McIntosh if (op == OP_SHARE) {
6963b8dc1b43SJoyce McIntosh get_all_cb_t dslist = { 0 };
6964b8dc1b43SJoyce McIntosh get_all_datasets(&dslist, B_FALSE);
6965b8dc1b43SJoyce McIntosh
6966b8dc1b43SJoyce McIntosh if (dslist.cb_used != 0) {
6967b8dc1b43SJoyce McIntosh sa_init_selective_arg_t sharearg;
6968b8dc1b43SJoyce McIntosh sharearg.zhandle_arr = dslist.cb_handles;
6969b8dc1b43SJoyce McIntosh sharearg.zhandle_len = dslist.cb_used;
6970b8dc1b43SJoyce McIntosh if ((ret = zfs_init_libshare_arg(g_zfs,
6971b8dc1b43SJoyce McIntosh SA_INIT_SHARE_API_SELECTIVE, &sharearg)) !=
6972b8dc1b43SJoyce McIntosh SA_OK) {
6973b8dc1b43SJoyce McIntosh (void) fprintf(stderr, gettext(
6974b8dc1b43SJoyce McIntosh "Could not initialize libshare, "
6975b8dc1b43SJoyce McIntosh "%d"), ret);
6976b8dc1b43SJoyce McIntosh return (1);
6977b8dc1b43SJoyce McIntosh }
6978b8dc1b43SJoyce McIntosh }
6979b8dc1b43SJoyce McIntosh }
6980b8dc1b43SJoyce McIntosh
6981b8dc1b43SJoyce McIntosh /*
6982fa9e4066Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and
6983fa9e4066Sahrens * removing it from the AVL tree in the process.
6984fa9e4066Sahrens */
6985fa9e4066Sahrens if ((walk = uu_avl_walk_start(tree,
6986a8b6ddafSMark J Musante UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
6987a8b6ddafSMark J Musante nomem();
6988fa9e4066Sahrens
6989fa9e4066Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) {
6990fa9e4066Sahrens uu_avl_remove(tree, node);
6991fa9e4066Sahrens
6992f3861e1aSahl switch (op) {
6993fa9e4066Sahrens case OP_SHARE:
6994da6c28aaSamw if (zfs_unshareall_bypath(node->un_zhp,
6995fa9e4066Sahrens node->un_mountp) != 0)
6996fa9e4066Sahrens ret = 1;
6997fa9e4066Sahrens break;
6998fa9e4066Sahrens
6999fa9e4066Sahrens case OP_MOUNT:
7000fa9e4066Sahrens if (zfs_unmount(node->un_zhp,
7001fa9e4066Sahrens node->un_mountp, flags) != 0)
7002fa9e4066Sahrens ret = 1;
7003fa9e4066Sahrens break;
7004fa9e4066Sahrens }
7005fa9e4066Sahrens
7006fa9e4066Sahrens zfs_close(node->un_zhp);
7007fa9e4066Sahrens free(node->un_mountp);
7008fa9e4066Sahrens free(node);
7009fa9e4066Sahrens }
7010fa9e4066Sahrens
7011fa9e4066Sahrens uu_avl_walk_end(walk);
7012fa9e4066Sahrens uu_avl_destroy(tree);
7013fa9e4066Sahrens uu_avl_pool_destroy(pool);
7014f3861e1aSahl
7015fa9e4066Sahrens } else {
7016f3861e1aSahl if (argc != 1) {
7017f3861e1aSahl if (argc == 0)
7018f3861e1aSahl (void) fprintf(stderr,
7019f3861e1aSahl gettext("missing filesystem argument\n"));
7020f3861e1aSahl else
7021f3861e1aSahl (void) fprintf(stderr,
7022f3861e1aSahl gettext("too many arguments\n"));
7023f3861e1aSahl usage(B_FALSE);
7024f3861e1aSahl }
7025f3861e1aSahl
7026fa9e4066Sahrens /*
7027fa9e4066Sahrens * We have an argument, but it may be a full path or a ZFS
7028fa9e4066Sahrens * filesystem. Pass full paths off to unmount_path() (shared by
7029fa9e4066Sahrens * manual_unmount), otherwise open the filesystem and pass to
7030fa9e4066Sahrens * zfs_unmount().
7031fa9e4066Sahrens */
7032fa9e4066Sahrens if (argv[0][0] == '/')
7033f3861e1aSahl return (unshare_unmount_path(op, argv[0],
703499653d4eSeschrock flags, B_FALSE));
7035fa9e4066Sahrens
70369d9a58e3SEric Taylor if ((zhp = zfs_open(g_zfs, argv[0],
70379d9a58e3SEric Taylor ZFS_TYPE_FILESYSTEM)) == NULL)
7038fa9e4066Sahrens return (1);
7039fa9e4066Sahrens
7040f3861e1aSahl verify(zfs_prop_get(zhp, op == OP_SHARE ?
7041da6c28aaSamw ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
7042ab003da8SJim Dunham nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
7043da6c28aaSamw NULL, 0, B_FALSE) == 0);
7044fa9e4066Sahrens
7045f3861e1aSahl switch (op) {
7046fa9e4066Sahrens case OP_SHARE:
7047da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
7048ab003da8SJim Dunham nfs_mnt_prop,
7049ab003da8SJim Dunham sizeof (nfs_mnt_prop),
7050da6c28aaSamw NULL, NULL, 0, B_FALSE) == 0);
7051da6c28aaSamw verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
7052da6c28aaSamw sharesmb, sizeof (sharesmb), NULL, NULL,
7053da6c28aaSamw 0, B_FALSE) == 0);
7054da6c28aaSamw
7055ab003da8SJim Dunham if (strcmp(nfs_mnt_prop, "off") == 0 &&
7056da6c28aaSamw strcmp(sharesmb, "off") == 0) {
7057f3861e1aSahl (void) fprintf(stderr, gettext("cannot "
7058f3861e1aSahl "unshare '%s': legacy share\n"),
7059fa9e4066Sahrens zfs_get_name(zhp));
7060f3861e1aSahl (void) fprintf(stderr, gettext("use "
7061bbf21555SRichard Lowe "unshare(8) to unshare this "
7062f3861e1aSahl "filesystem\n"));
7063fa9e4066Sahrens ret = 1;
7064da6c28aaSamw } else if (!zfs_is_shared(zhp)) {
7065f3861e1aSahl (void) fprintf(stderr, gettext("cannot "
7066f3861e1aSahl "unshare '%s': not currently "
7067f3861e1aSahl "shared\n"), zfs_get_name(zhp));
7068f3861e1aSahl ret = 1;
7069da6c28aaSamw } else if (zfs_unshareall(zhp) != 0) {
7070fa9e4066Sahrens ret = 1;
7071fa9e4066Sahrens }
7072fa9e4066Sahrens break;
7073fa9e4066Sahrens
7074fa9e4066Sahrens case OP_MOUNT:
7075ab003da8SJim Dunham if (strcmp(nfs_mnt_prop, "legacy") == 0) {
7076f3861e1aSahl (void) fprintf(stderr, gettext("cannot "
7077f3861e1aSahl "unmount '%s': legacy "
7078f3861e1aSahl "mountpoint\n"), zfs_get_name(zhp));
7079f3861e1aSahl (void) fprintf(stderr, gettext("use "
7080bbf21555SRichard Lowe "umount(8) to unmount this "
7081f3861e1aSahl "filesystem\n"));
7082fa9e4066Sahrens ret = 1;
7083fa9e4066Sahrens } else if (!zfs_is_mounted(zhp, NULL)) {
7084f3861e1aSahl (void) fprintf(stderr, gettext("cannot "
7085f3861e1aSahl "unmount '%s': not currently "
7086f3861e1aSahl "mounted\n"),
7087fa9e4066Sahrens zfs_get_name(zhp));
7088fa9e4066Sahrens ret = 1;
7089fa9e4066Sahrens } else if (zfs_unmountall(zhp, flags) != 0) {
7090fa9e4066Sahrens ret = 1;
7091fa9e4066Sahrens }
7092f3861e1aSahl break;
7093f3861e1aSahl }
7094fa9e4066Sahrens
7095fa9e4066Sahrens zfs_close(zhp);
7096fa9e4066Sahrens }
7097fa9e4066Sahrens
7098fa9e4066Sahrens return (ret);
7099fa9e4066Sahrens }
7100fa9e4066Sahrens
7101fa9e4066Sahrens /*
7102fa9e4066Sahrens * zfs unmount -a
7103fa9e4066Sahrens * zfs unmount filesystem
7104fa9e4066Sahrens *
7105fa9e4066Sahrens * Unmount all filesystems, or a specific ZFS filesystem.
7106fa9e4066Sahrens */
7107fa9e4066Sahrens static int
zfs_do_unmount(int argc,char ** argv)7108fa9e4066Sahrens zfs_do_unmount(int argc, char **argv)
7109fa9e4066Sahrens {
7110fa9e4066Sahrens return (unshare_unmount(OP_MOUNT, argc, argv));
7111fa9e4066Sahrens }
7112fa9e4066Sahrens
7113fa9e4066Sahrens /*
7114fa9e4066Sahrens * zfs unshare -a
7115fa9e4066Sahrens * zfs unshare filesystem
7116fa9e4066Sahrens *
7117fa9e4066Sahrens * Unshare all filesystems, or a specific ZFS filesystem.
7118fa9e4066Sahrens */
7119fa9e4066Sahrens static int
zfs_do_unshare(int argc,char ** argv)7120fa9e4066Sahrens zfs_do_unshare(int argc, char **argv)
7121fa9e4066Sahrens {
7122fa9e4066Sahrens return (unshare_unmount(OP_SHARE, argc, argv));
7123fa9e4066Sahrens }
7124fa9e4066Sahrens
7125fa9e4066Sahrens /*
7126fa9e4066Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
7127fa9e4066Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
7128fa9e4066Sahrens */
7129fa9e4066Sahrens static int
manual_mount(int argc,char ** argv)7130fa9e4066Sahrens manual_mount(int argc, char **argv)
7131fa9e4066Sahrens {
7132fa9e4066Sahrens zfs_handle_t *zhp;
7133fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN];
7134fa9e4066Sahrens char mntopts[MNT_LINE_MAX] = { '\0' };
713505c998d6SRichard Lowe int ret = 0;
7136fa9e4066Sahrens int c;
7137fa9e4066Sahrens int flags = 0;
7138fa9e4066Sahrens char *dataset, *path;
7139fa9e4066Sahrens
7140fa9e4066Sahrens /* check options */
7141ea8dc4b6Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) {
7142fa9e4066Sahrens switch (c) {
7143fa9e4066Sahrens case 'o':
7144fa9e4066Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts));
7145fa9e4066Sahrens break;
7146fa9e4066Sahrens case 'O':
7147fa9e4066Sahrens flags |= MS_OVERLAY;
7148fa9e4066Sahrens break;
7149ea8dc4b6Seschrock case 'm':
7150ea8dc4b6Seschrock flags |= MS_NOMNTTAB;
7151ea8dc4b6Seschrock break;
7152fa9e4066Sahrens case ':':
7153fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for "
7154fa9e4066Sahrens "'%c' option\n"), optopt);
715599653d4eSeschrock usage(B_FALSE);
7156fa9e4066Sahrens break;
7157fa9e4066Sahrens case '?':
7158fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
7159fa9e4066Sahrens optopt);
7160fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] "
7161fa9e4066Sahrens "<path>\n"));
7162fa9e4066Sahrens return (2);
7163fa9e4066Sahrens }
7164fa9e4066Sahrens }
7165fa9e4066Sahrens
7166fa9e4066Sahrens argc -= optind;
7167fa9e4066Sahrens argv += optind;
7168fa9e4066Sahrens
7169fa9e4066Sahrens /* check that we only have two arguments */
7170fa9e4066Sahrens if (argc != 2) {
7171fa9e4066Sahrens if (argc == 0)
7172fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset "
7173fa9e4066Sahrens "argument\n"));
7174fa9e4066Sahrens else if (argc == 1)
7175fa9e4066Sahrens (void) fprintf(stderr,
7176fa9e4066Sahrens gettext("missing mountpoint argument\n"));
7177fa9e4066Sahrens else
7178fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
7179fa9e4066Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
7180fa9e4066Sahrens return (2);
7181fa9e4066Sahrens }
7182fa9e4066Sahrens
7183fa9e4066Sahrens dataset = argv[0];
7184fa9e4066Sahrens path = argv[1];
7185fa9e4066Sahrens
7186fa9e4066Sahrens /* try to open the dataset */
718799653d4eSeschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
7188fa9e4066Sahrens return (1);
7189fa9e4066Sahrens
7190fa9e4066Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
719199653d4eSeschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
7192fa9e4066Sahrens
7193fa9e4066Sahrens /* check for legacy mountpoint and complain appropriately */
7194fa9e4066Sahrens ret = 0;
7195fa9e4066Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
7196fa9e4066Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
7197fa9e4066Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) {
7198fa9e4066Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"),
7199fa9e4066Sahrens strerror(errno));
7200fa9e4066Sahrens ret = 1;
7201fa9e4066Sahrens }
7202fa9e4066Sahrens } else {
7203fa9e4066Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
7204fa9e4066Sahrens "mounted using 'mount -F zfs'\n"), dataset);
7205fa9e4066Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
7206fa9e4066Sahrens "instead.\n"), path);
7207fa9e4066Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
7208fa9e4066Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
7209bbf21555SRichard Lowe (void) fprintf(stderr, gettext("See zfs(8) for more "
7210fa9e4066Sahrens "information.\n"));
7211fa9e4066Sahrens ret = 1;
7212fa9e4066Sahrens }
7213fa9e4066Sahrens
7214fa9e4066Sahrens return (ret);
7215fa9e4066Sahrens }
7216fa9e4066Sahrens
7217fa9e4066Sahrens /*
7218fa9e4066Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow
7219fa9e4066Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative
7220fa9e4066Sahrens * interface.
7221fa9e4066Sahrens */
7222fa9e4066Sahrens static int
manual_unmount(int argc,char ** argv)7223fa9e4066Sahrens manual_unmount(int argc, char **argv)
7224fa9e4066Sahrens {
7225fa9e4066Sahrens int flags = 0;
7226fa9e4066Sahrens int c;
7227fa9e4066Sahrens
7228fa9e4066Sahrens /* check options */
7229fa9e4066Sahrens while ((c = getopt(argc, argv, "f")) != -1) {
7230fa9e4066Sahrens switch (c) {
7231fa9e4066Sahrens case 'f':
7232fa9e4066Sahrens flags = MS_FORCE;
7233fa9e4066Sahrens break;
7234fa9e4066Sahrens case '?':
7235fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"),
7236fa9e4066Sahrens optopt);
7237fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] "
7238fa9e4066Sahrens "<path>\n"));
7239fa9e4066Sahrens return (2);
7240fa9e4066Sahrens }
7241fa9e4066Sahrens }
7242fa9e4066Sahrens
7243fa9e4066Sahrens argc -= optind;
7244fa9e4066Sahrens argv += optind;
7245fa9e4066Sahrens
7246fa9e4066Sahrens /* check arguments */
7247fa9e4066Sahrens if (argc != 1) {
7248fa9e4066Sahrens if (argc == 0)
7249fa9e4066Sahrens (void) fprintf(stderr, gettext("missing path "
7250fa9e4066Sahrens "argument\n"));
7251fa9e4066Sahrens else
7252fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n"));
7253fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
7254fa9e4066Sahrens return (2);
7255fa9e4066Sahrens }
7256fa9e4066Sahrens
725799653d4eSeschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
7258fa9e4066Sahrens }
7259fa9e4066Sahrens
7260fa9e4066Sahrens static int
find_command_idx(char * command,int * idx)7261ecd6cf80Smarks find_command_idx(char *command, int *idx)
7262ecd6cf80Smarks {
7263ecd6cf80Smarks int i;
7264ecd6cf80Smarks
7265ecd6cf80Smarks for (i = 0; i < NCOMMAND; i++) {
7266ecd6cf80Smarks if (command_table[i].name == NULL)
7267ecd6cf80Smarks continue;
7268ecd6cf80Smarks
7269ecd6cf80Smarks if (strcmp(command, command_table[i].name) == 0) {
7270ecd6cf80Smarks *idx = i;
7271ecd6cf80Smarks return (0);
7272ecd6cf80Smarks }
7273ecd6cf80Smarks }
7274ecd6cf80Smarks return (1);
7275ecd6cf80Smarks }
7276ecd6cf80Smarks
727799d5e173STim Haley static int
zfs_do_diff(int argc,char ** argv)727899d5e173STim Haley zfs_do_diff(int argc, char **argv)
727999d5e173STim Haley {
728099d5e173STim Haley zfs_handle_t *zhp;
728199d5e173STim Haley int flags = 0;
728299d5e173STim Haley char *tosnap = NULL;
728399d5e173STim Haley char *fromsnap = NULL;
728499d5e173STim Haley char *atp, *copy;
728505c998d6SRichard Lowe int err = 0;
728699d5e173STim Haley int c;
728799d5e173STim Haley
728899d5e173STim Haley while ((c = getopt(argc, argv, "FHt")) != -1) {
728999d5e173STim Haley switch (c) {
729099d5e173STim Haley case 'F':
729199d5e173STim Haley flags |= ZFS_DIFF_CLASSIFY;
729299d5e173STim Haley break;
729399d5e173STim Haley case 'H':
729499d5e173STim Haley flags |= ZFS_DIFF_PARSEABLE;
729599d5e173STim Haley break;
729699d5e173STim Haley case 't':
729799d5e173STim Haley flags |= ZFS_DIFF_TIMESTAMP;
729899d5e173STim Haley break;
729999d5e173STim Haley default:
730099d5e173STim Haley (void) fprintf(stderr,
730199d5e173STim Haley gettext("invalid option '%c'\n"), optopt);
730299d5e173STim Haley usage(B_FALSE);
730399d5e173STim Haley }
730499d5e173STim Haley }
730599d5e173STim Haley
730699d5e173STim Haley argc -= optind;
730799d5e173STim Haley argv += optind;
730899d5e173STim Haley
730999d5e173STim Haley if (argc < 1) {
731099d5e173STim Haley (void) fprintf(stderr,
731199d5e173STim Haley gettext("must provide at least one snapshot name\n"));
731299d5e173STim Haley usage(B_FALSE);
731399d5e173STim Haley }
731499d5e173STim Haley
731599d5e173STim Haley if (argc > 2) {
731699d5e173STim Haley (void) fprintf(stderr, gettext("too many arguments\n"));
731799d5e173STim Haley usage(B_FALSE);
731899d5e173STim Haley }
731999d5e173STim Haley
732099d5e173STim Haley fromsnap = argv[0];
732199d5e173STim Haley tosnap = (argc == 2) ? argv[1] : NULL;
732299d5e173STim Haley
732399d5e173STim Haley copy = NULL;
732499d5e173STim Haley if (*fromsnap != '@')
732599d5e173STim Haley copy = strdup(fromsnap);
732699d5e173STim Haley else if (tosnap)
732799d5e173STim Haley copy = strdup(tosnap);
732899d5e173STim Haley if (copy == NULL)
732999d5e173STim Haley usage(B_FALSE);
733099d5e173STim Haley
7331c16bcc45SIgor Kozhukhov if ((atp = strchr(copy, '@')) != NULL)
733299d5e173STim Haley *atp = '\0';
733399d5e173STim Haley
7334*606d76deScao if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {
7335*606d76deScao free(copy);
733699d5e173STim Haley return (1);
7337*606d76deScao }
733899d5e173STim Haley free(copy);
733999d5e173STim Haley
734099d5e173STim Haley /*
734199d5e173STim Haley * Ignore SIGPIPE so that the library can give us
734299d5e173STim Haley * information on any failure
734399d5e173STim Haley */
734499d5e173STim Haley (void) sigignore(SIGPIPE);
734599d5e173STim Haley
734699d5e173STim Haley err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
734799d5e173STim Haley
734899d5e173STim Haley zfs_close(zhp);
734999d5e173STim Haley
735099d5e173STim Haley return (err != 0);
735199d5e173STim Haley }
735299d5e173STim Haley
73530b2e8253Sloli10K /*
73540b2e8253Sloli10K * zfs remap <filesystem | volume>
73550b2e8253Sloli10K *
73560b2e8253Sloli10K * Remap the indirect blocks in the given fileystem or volume.
73570b2e8253Sloli10K */
73585cabbc6bSPrashanth Sreenivasa static int
zfs_do_remap(int argc,char ** argv)73595cabbc6bSPrashanth Sreenivasa zfs_do_remap(int argc, char **argv)
73605cabbc6bSPrashanth Sreenivasa {
73615cabbc6bSPrashanth Sreenivasa const char *fsname;
73625cabbc6bSPrashanth Sreenivasa int err = 0;
73630b2e8253Sloli10K int c;
73640b2e8253Sloli10K
73650b2e8253Sloli10K /* check options */
73660b2e8253Sloli10K while ((c = getopt(argc, argv, "")) != -1) {
73670b2e8253Sloli10K switch (c) {
73680b2e8253Sloli10K case '?':
73690b2e8253Sloli10K (void) fprintf(stderr,
73700b2e8253Sloli10K gettext("invalid option '%c'\n"), optopt);
73710b2e8253Sloli10K usage(B_FALSE);
73720b2e8253Sloli10K }
73730b2e8253Sloli10K }
73740b2e8253Sloli10K
73755cabbc6bSPrashanth Sreenivasa if (argc != 2) {
73765cabbc6bSPrashanth Sreenivasa (void) fprintf(stderr, gettext("wrong number of arguments\n"));
73775cabbc6bSPrashanth Sreenivasa usage(B_FALSE);
73785cabbc6bSPrashanth Sreenivasa }
73795cabbc6bSPrashanth Sreenivasa
73805cabbc6bSPrashanth Sreenivasa fsname = argv[1];
73815cabbc6bSPrashanth Sreenivasa err = zfs_remap_indirects(g_zfs, fsname);
73825cabbc6bSPrashanth Sreenivasa
73835cabbc6bSPrashanth Sreenivasa return (err);
73845cabbc6bSPrashanth Sreenivasa }
73855cabbc6bSPrashanth Sreenivasa
738678f17100SMatthew Ahrens /*
738778f17100SMatthew Ahrens * zfs bookmark <fs@snap> <fs#bmark>
738878f17100SMatthew Ahrens *
738978f17100SMatthew Ahrens * Creates a bookmark with the given name from the given snapshot.
739078f17100SMatthew Ahrens */
739178f17100SMatthew Ahrens static int
zfs_do_bookmark(int argc,char ** argv)739278f17100SMatthew Ahrens zfs_do_bookmark(int argc, char **argv)
739378f17100SMatthew Ahrens {
73949adfa60dSMatthew Ahrens char snapname[ZFS_MAX_DATASET_NAME_LEN];
739578f17100SMatthew Ahrens zfs_handle_t *zhp;
739678f17100SMatthew Ahrens nvlist_t *nvl;
739778f17100SMatthew Ahrens int ret = 0;
739878f17100SMatthew Ahrens int c;
739978f17100SMatthew Ahrens
740078f17100SMatthew Ahrens /* check options */
740178f17100SMatthew Ahrens while ((c = getopt(argc, argv, "")) != -1) {
740278f17100SMatthew Ahrens switch (c) {
740378f17100SMatthew Ahrens case '?':
740478f17100SMatthew Ahrens (void) fprintf(stderr,
740578f17100SMatthew Ahrens gettext("invalid option '%c'\n"), optopt);
740678f17100SMatthew Ahrens goto usage;
740778f17100SMatthew Ahrens }
740878f17100SMatthew Ahrens }
740978f17100SMatthew Ahrens
741078f17100SMatthew Ahrens argc -= optind;
741178f17100SMatthew Ahrens argv += optind;
741278f17100SMatthew Ahrens
741378f17100SMatthew Ahrens /* check number of arguments */
741478f17100SMatthew Ahrens if (argc < 1) {
741578f17100SMatthew Ahrens (void) fprintf(stderr, gettext("missing snapshot argument\n"));
741678f17100SMatthew Ahrens goto usage;
741778f17100SMatthew Ahrens }
741878f17100SMatthew Ahrens if (argc < 2) {
741978f17100SMatthew Ahrens (void) fprintf(stderr, gettext("missing bookmark argument\n"));
742078f17100SMatthew Ahrens goto usage;
742178f17100SMatthew Ahrens }
742278f17100SMatthew Ahrens
742378f17100SMatthew Ahrens if (strchr(argv[1], '#') == NULL) {
742478f17100SMatthew Ahrens (void) fprintf(stderr,
742578f17100SMatthew Ahrens gettext("invalid bookmark name '%s' -- "
742678f17100SMatthew Ahrens "must contain a '#'\n"), argv[1]);
742778f17100SMatthew Ahrens goto usage;
742878f17100SMatthew Ahrens }
742978f17100SMatthew Ahrens
743078f17100SMatthew Ahrens if (argv[0][0] == '@') {
743178f17100SMatthew Ahrens /*
743278f17100SMatthew Ahrens * Snapshot name begins with @.
743378f17100SMatthew Ahrens * Default to same fs as bookmark.
743478f17100SMatthew Ahrens */
743578f17100SMatthew Ahrens (void) strncpy(snapname, argv[1], sizeof (snapname));
743678f17100SMatthew Ahrens *strchr(snapname, '#') = '\0';
743778f17100SMatthew Ahrens (void) strlcat(snapname, argv[0], sizeof (snapname));
743878f17100SMatthew Ahrens } else {
74393382f241Sluozhengzheng (void) strlcpy(snapname, argv[0], sizeof (snapname));
744078f17100SMatthew Ahrens }
744178f17100SMatthew Ahrens zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
744278f17100SMatthew Ahrens if (zhp == NULL)
744378f17100SMatthew Ahrens goto usage;
744478f17100SMatthew Ahrens zfs_close(zhp);
744578f17100SMatthew Ahrens
744678f17100SMatthew Ahrens
744778f17100SMatthew Ahrens nvl = fnvlist_alloc();
744878f17100SMatthew Ahrens fnvlist_add_string(nvl, argv[1], snapname);
744978f17100SMatthew Ahrens ret = lzc_bookmark(nvl, NULL);
745078f17100SMatthew Ahrens fnvlist_free(nvl);
745178f17100SMatthew Ahrens
745278f17100SMatthew Ahrens if (ret != 0) {
74537ac89354SDon Brady const char *err_msg = NULL;
745478f17100SMatthew Ahrens char errbuf[1024];
745578f17100SMatthew Ahrens
745678f17100SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf),
745778f17100SMatthew Ahrens dgettext(TEXT_DOMAIN,
745878f17100SMatthew Ahrens "cannot create bookmark '%s'"), argv[1]);
745978f17100SMatthew Ahrens
746078f17100SMatthew Ahrens switch (ret) {
746178f17100SMatthew Ahrens case EXDEV:
746278f17100SMatthew Ahrens err_msg = "bookmark is in a different pool";
746378f17100SMatthew Ahrens break;
746478f17100SMatthew Ahrens case EEXIST:
746578f17100SMatthew Ahrens err_msg = "bookmark exists";
746678f17100SMatthew Ahrens break;
746778f17100SMatthew Ahrens case EINVAL:
746878f17100SMatthew Ahrens err_msg = "invalid argument";
746978f17100SMatthew Ahrens break;
747078f17100SMatthew Ahrens case ENOTSUP:
747178f17100SMatthew Ahrens err_msg = "bookmark feature not enabled";
747278f17100SMatthew Ahrens break;
74737d46dc6cSMatthew Ahrens case ENOSPC:
74747d46dc6cSMatthew Ahrens err_msg = "out of space";
74757d46dc6cSMatthew Ahrens break;
747678f17100SMatthew Ahrens default:
74777ac89354SDon Brady (void) zfs_standard_error(g_zfs, ret, errbuf);
747878f17100SMatthew Ahrens break;
747978f17100SMatthew Ahrens }
74807ac89354SDon Brady if (err_msg != NULL) {
748178f17100SMatthew Ahrens (void) fprintf(stderr, "%s: %s\n", errbuf,
748278f17100SMatthew Ahrens dgettext(TEXT_DOMAIN, err_msg));
748378f17100SMatthew Ahrens }
74847ac89354SDon Brady }
748578f17100SMatthew Ahrens
74867d46dc6cSMatthew Ahrens return (ret != 0);
748778f17100SMatthew Ahrens
748878f17100SMatthew Ahrens usage:
748978f17100SMatthew Ahrens usage(B_FALSE);
749078f17100SMatthew Ahrens return (-1);
749178f17100SMatthew Ahrens }
749278f17100SMatthew Ahrens
7493dfc11533SChris Williamson static int
zfs_do_channel_program(int argc,char ** argv)7494dfc11533SChris Williamson zfs_do_channel_program(int argc, char **argv)
7495dfc11533SChris Williamson {
7496dfc11533SChris Williamson int ret, fd;
7497ef150c2bSRichard Lowe int c;
7498dfc11533SChris Williamson char *progbuf, *filename, *poolname;
7499dfc11533SChris Williamson size_t progsize, progread;
75007ac89354SDon Brady nvlist_t *outnvl = NULL;
7501dfc11533SChris Williamson uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
7502dfc11533SChris Williamson uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
750352675910SAlek Pinchuk boolean_t sync_flag = B_TRUE, json_output = B_FALSE;
7504dfc11533SChris Williamson zpool_handle_t *zhp;
7505dfc11533SChris Williamson
7506dfc11533SChris Williamson /* check options */
7507dfc11533SChris Williamson while (-1 !=
750852675910SAlek Pinchuk (c = getopt(argc, argv, "jnt:(instr-limit)m:(memory-limit)"))) {
7509dfc11533SChris Williamson switch (c) {
7510dfc11533SChris Williamson case 't':
7511dfc11533SChris Williamson case 'm': {
7512dfc11533SChris Williamson uint64_t arg;
7513dfc11533SChris Williamson char *endp;
7514dfc11533SChris Williamson
7515dfc11533SChris Williamson errno = 0;
7516dfc11533SChris Williamson arg = strtoull(optarg, &endp, 0);
7517dfc11533SChris Williamson if (errno != 0 || *endp != '\0') {
7518dfc11533SChris Williamson (void) fprintf(stderr, gettext(
7519dfc11533SChris Williamson "invalid argument "
7520dfc11533SChris Williamson "'%s': expected integer\n"), optarg);
7521dfc11533SChris Williamson goto usage;
7522dfc11533SChris Williamson }
7523dfc11533SChris Williamson
7524dfc11533SChris Williamson if (c == 't') {
7525dfc11533SChris Williamson if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) {
7526dfc11533SChris Williamson (void) fprintf(stderr, gettext(
7527dfc11533SChris Williamson "Invalid instruction limit: "
7528dfc11533SChris Williamson "%s\n"), optarg);
7529dfc11533SChris Williamson return (1);
7530dfc11533SChris Williamson } else {
7531dfc11533SChris Williamson instrlimit = arg;
7532dfc11533SChris Williamson }
7533dfc11533SChris Williamson } else {
7534dfc11533SChris Williamson ASSERT3U(c, ==, 'm');
7535dfc11533SChris Williamson if (arg > ZCP_MAX_MEMLIMIT || arg == 0) {
7536dfc11533SChris Williamson (void) fprintf(stderr, gettext(
7537dfc11533SChris Williamson "Invalid memory limit: "
7538dfc11533SChris Williamson "%s\n"), optarg);
7539dfc11533SChris Williamson return (1);
7540dfc11533SChris Williamson } else {
7541dfc11533SChris Williamson memlimit = arg;
7542dfc11533SChris Williamson }
7543dfc11533SChris Williamson }
7544dfc11533SChris Williamson break;
7545dfc11533SChris Williamson }
7546a3b28680SSerapheim Dimitropoulos case 'n': {
7547a3b28680SSerapheim Dimitropoulos sync_flag = B_FALSE;
7548a3b28680SSerapheim Dimitropoulos break;
7549a3b28680SSerapheim Dimitropoulos }
755052675910SAlek Pinchuk case 'j': {
755152675910SAlek Pinchuk json_output = B_TRUE;
755252675910SAlek Pinchuk break;
755352675910SAlek Pinchuk }
7554dfc11533SChris Williamson case '?':
7555dfc11533SChris Williamson (void) fprintf(stderr, gettext("invalid option '%c'\n"),
7556dfc11533SChris Williamson optopt);
7557dfc11533SChris Williamson goto usage;
7558dfc11533SChris Williamson }
7559dfc11533SChris Williamson }
7560dfc11533SChris Williamson
7561dfc11533SChris Williamson argc -= optind;
7562dfc11533SChris Williamson argv += optind;
7563dfc11533SChris Williamson
7564dfc11533SChris Williamson if (argc < 2) {
7565dfc11533SChris Williamson (void) fprintf(stderr,
7566dfc11533SChris Williamson gettext("invalid number of arguments\n"));
7567dfc11533SChris Williamson goto usage;
7568dfc11533SChris Williamson }
7569dfc11533SChris Williamson
7570dfc11533SChris Williamson poolname = argv[0];
7571dfc11533SChris Williamson filename = argv[1];
7572dfc11533SChris Williamson if (strcmp(filename, "-") == 0) {
7573dfc11533SChris Williamson fd = 0;
7574dfc11533SChris Williamson filename = "standard input";
7575dfc11533SChris Williamson } else if ((fd = open(filename, O_RDONLY)) < 0) {
7576dfc11533SChris Williamson (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
7577dfc11533SChris Williamson filename, strerror(errno));
7578dfc11533SChris Williamson return (1);
7579dfc11533SChris Williamson }
7580dfc11533SChris Williamson
7581dfc11533SChris Williamson if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
7582dfc11533SChris Williamson (void) fprintf(stderr, gettext("cannot open pool '%s'"),
7583dfc11533SChris Williamson poolname);
7584dfc11533SChris Williamson return (1);
7585dfc11533SChris Williamson }
7586dfc11533SChris Williamson zpool_close(zhp);
7587dfc11533SChris Williamson
7588dfc11533SChris Williamson /*
7589dfc11533SChris Williamson * Read in the channel program, expanding the program buffer as
7590dfc11533SChris Williamson * necessary.
7591dfc11533SChris Williamson */
7592dfc11533SChris Williamson progread = 0;
7593dfc11533SChris Williamson progsize = 1024;
7594dfc11533SChris Williamson progbuf = safe_malloc(progsize);
7595dfc11533SChris Williamson do {
7596dfc11533SChris Williamson ret = read(fd, progbuf + progread, progsize - progread);
7597dfc11533SChris Williamson progread += ret;
7598dfc11533SChris Williamson if (progread == progsize && ret > 0) {
7599dfc11533SChris Williamson progsize *= 2;
7600dfc11533SChris Williamson progbuf = safe_realloc(progbuf, progsize);
7601dfc11533SChris Williamson }
7602dfc11533SChris Williamson } while (ret > 0);
7603dfc11533SChris Williamson
7604dfc11533SChris Williamson if (fd != 0)
7605dfc11533SChris Williamson (void) close(fd);
7606dfc11533SChris Williamson if (ret < 0) {
7607dfc11533SChris Williamson free(progbuf);
7608dfc11533SChris Williamson (void) fprintf(stderr,
7609dfc11533SChris Williamson gettext("cannot read '%s': %s\n"),
7610dfc11533SChris Williamson filename, strerror(errno));
7611dfc11533SChris Williamson return (1);
7612dfc11533SChris Williamson }
7613dfc11533SChris Williamson progbuf[progread] = '\0';
7614dfc11533SChris Williamson
7615dfc11533SChris Williamson /*
7616dfc11533SChris Williamson * Any remaining arguments are passed as arguments to the lua script as
7617dfc11533SChris Williamson * a string array:
7618dfc11533SChris Williamson * {
7619dfc11533SChris Williamson * "argv" -> [ "arg 1", ... "arg n" ],
7620dfc11533SChris Williamson * }
7621dfc11533SChris Williamson */
7622dfc11533SChris Williamson nvlist_t *argnvl = fnvlist_alloc();
7623dfc11533SChris Williamson fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
7624dfc11533SChris Williamson
7625a3b28680SSerapheim Dimitropoulos if (sync_flag) {
7626a3b28680SSerapheim Dimitropoulos ret = lzc_channel_program(poolname, progbuf,
7627a3b28680SSerapheim Dimitropoulos instrlimit, memlimit, argnvl, &outnvl);
7628a3b28680SSerapheim Dimitropoulos } else {
7629a3b28680SSerapheim Dimitropoulos ret = lzc_channel_program_nosync(poolname, progbuf,
7630a3b28680SSerapheim Dimitropoulos instrlimit, memlimit, argnvl, &outnvl);
7631a3b28680SSerapheim Dimitropoulos }
7632dfc11533SChris Williamson
7633dfc11533SChris Williamson if (ret != 0) {
7634dfc11533SChris Williamson /*
7635dfc11533SChris Williamson * On error, report the error message handed back by lua if one
7636dfc11533SChris Williamson * exists. Otherwise, generate an appropriate error message,
7637dfc11533SChris Williamson * falling back on strerror() for an unexpected return code.
7638dfc11533SChris Williamson */
7639dfc11533SChris Williamson char *errstring = NULL;
76407ac89354SDon Brady const char *msg = gettext("Channel program execution failed");
76417ac89354SDon Brady if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
7642dfc11533SChris Williamson (void) nvlist_lookup_string(outnvl,
7643dfc11533SChris Williamson ZCP_RET_ERROR, &errstring);
7644dfc11533SChris Williamson if (errstring == NULL)
7645dfc11533SChris Williamson errstring = strerror(ret);
7646dfc11533SChris Williamson } else {
7647dfc11533SChris Williamson switch (ret) {
7648dfc11533SChris Williamson case EINVAL:
7649dfc11533SChris Williamson errstring =
7650dfc11533SChris Williamson "Invalid instruction or memory limit.";
7651dfc11533SChris Williamson break;
7652dfc11533SChris Williamson case ENOMEM:
7653dfc11533SChris Williamson errstring = "Return value too large.";
7654dfc11533SChris Williamson break;
7655dfc11533SChris Williamson case ENOSPC:
7656dfc11533SChris Williamson errstring = "Memory limit exhausted.";
7657dfc11533SChris Williamson break;
7658dfc11533SChris Williamson case ETIME:
7659dfc11533SChris Williamson errstring = "Timed out.";
7660dfc11533SChris Williamson break;
7661dfc11533SChris Williamson case EPERM:
7662dfc11533SChris Williamson errstring = "Permission denied. Channel "
7663dfc11533SChris Williamson "programs must be run as root.";
7664dfc11533SChris Williamson break;
7665dfc11533SChris Williamson default:
76667ac89354SDon Brady (void) zfs_standard_error(g_zfs, ret, msg);
7667dfc11533SChris Williamson }
7668dfc11533SChris Williamson }
76697ac89354SDon Brady if (errstring != NULL)
76707ac89354SDon Brady (void) fprintf(stderr, "%s:\n%s\n", msg, errstring);
7671dfc11533SChris Williamson } else {
767252675910SAlek Pinchuk if (json_output) {
767352675910SAlek Pinchuk (void) nvlist_print_json(stdout, outnvl);
767452675910SAlek Pinchuk } else if (nvlist_empty(outnvl)) {
767552675910SAlek Pinchuk (void) fprintf(stdout, gettext("Channel program fully "
767652675910SAlek Pinchuk "executed and did not produce output.\n"));
7677dfc11533SChris Williamson } else {
767852675910SAlek Pinchuk (void) fprintf(stdout, gettext("Channel program fully "
767952675910SAlek Pinchuk "executed and produced output:\n"));
7680dfc11533SChris Williamson dump_nvlist(outnvl, 4);
7681dfc11533SChris Williamson }
7682dfc11533SChris Williamson }
7683dfc11533SChris Williamson
7684dfc11533SChris Williamson free(progbuf);
7685dfc11533SChris Williamson fnvlist_free(outnvl);
7686dfc11533SChris Williamson fnvlist_free(argnvl);
7687dfc11533SChris Williamson return (ret != 0);
7688dfc11533SChris Williamson
7689dfc11533SChris Williamson usage:
7690dfc11533SChris Williamson usage(B_FALSE);
7691dfc11533SChris Williamson return (-1);
7692dfc11533SChris Williamson }
7693dfc11533SChris Williamson
7694eb633035STom Caputi typedef struct loadkey_cbdata {
7695eb633035STom Caputi boolean_t cb_loadkey;
7696eb633035STom Caputi boolean_t cb_recursive;
7697eb633035STom Caputi boolean_t cb_noop;
7698eb633035STom Caputi char *cb_keylocation;
7699eb633035STom Caputi uint64_t cb_numfailed;
7700eb633035STom Caputi uint64_t cb_numattempted;
7701eb633035STom Caputi } loadkey_cbdata_t;
7702eb633035STom Caputi
7703eb633035STom Caputi static int
load_key_callback(zfs_handle_t * zhp,void * data)7704eb633035STom Caputi load_key_callback(zfs_handle_t *zhp, void *data)
7705eb633035STom Caputi {
7706eb633035STom Caputi int ret;
7707eb633035STom Caputi boolean_t is_encroot;
7708eb633035STom Caputi loadkey_cbdata_t *cb = data;
7709eb633035STom Caputi uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
7710eb633035STom Caputi
7711eb633035STom Caputi /*
7712eb633035STom Caputi * If we are working recursively, we want to skip loading / unloading
7713eb633035STom Caputi * keys for non-encryption roots and datasets whose keys are already
7714eb633035STom Caputi * in the desired end-state.
7715eb633035STom Caputi */
7716eb633035STom Caputi if (cb->cb_recursive) {
7717eb633035STom Caputi ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
7718eb633035STom Caputi if (ret != 0)
7719eb633035STom Caputi return (ret);
7720eb633035STom Caputi if (!is_encroot)
7721eb633035STom Caputi return (0);
7722eb633035STom Caputi
7723eb633035STom Caputi if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||
7724eb633035STom Caputi (!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))
7725eb633035STom Caputi return (0);
7726eb633035STom Caputi }
7727eb633035STom Caputi
7728eb633035STom Caputi cb->cb_numattempted++;
7729eb633035STom Caputi
7730eb633035STom Caputi if (cb->cb_loadkey)
7731eb633035STom Caputi ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);
7732eb633035STom Caputi else
7733eb633035STom Caputi ret = zfs_crypto_unload_key(zhp);
7734eb633035STom Caputi
7735eb633035STom Caputi if (ret != 0) {
7736eb633035STom Caputi cb->cb_numfailed++;
7737eb633035STom Caputi return (ret);
7738eb633035STom Caputi }
7739eb633035STom Caputi
7740eb633035STom Caputi return (0);
7741eb633035STom Caputi }
7742eb633035STom Caputi
7743eb633035STom Caputi static int
load_unload_keys(int argc,char ** argv,boolean_t loadkey)7744eb633035STom Caputi load_unload_keys(int argc, char **argv, boolean_t loadkey)
7745eb633035STom Caputi {
7746eb633035STom Caputi int c, ret = 0, flags = 0;
7747eb633035STom Caputi boolean_t do_all = B_FALSE;
7748eb633035STom Caputi loadkey_cbdata_t cb = { 0 };
7749eb633035STom Caputi
7750eb633035STom Caputi cb.cb_loadkey = loadkey;
7751eb633035STom Caputi
7752eb633035STom Caputi while ((c = getopt(argc, argv, "anrL:")) != -1) {
7753eb633035STom Caputi /* noop and alternate keylocations only apply to zfs load-key */
7754eb633035STom Caputi if (loadkey) {
7755eb633035STom Caputi switch (c) {
7756eb633035STom Caputi case 'n':
7757eb633035STom Caputi cb.cb_noop = B_TRUE;
7758eb633035STom Caputi continue;
7759eb633035STom Caputi case 'L':
7760eb633035STom Caputi cb.cb_keylocation = optarg;
7761eb633035STom Caputi continue;
7762eb633035STom Caputi default:
7763eb633035STom Caputi break;
7764eb633035STom Caputi }
7765eb633035STom Caputi }
7766eb633035STom Caputi
7767eb633035STom Caputi switch (c) {
7768eb633035STom Caputi case 'a':
7769eb633035STom Caputi do_all = B_TRUE;
7770eb633035STom Caputi cb.cb_recursive = B_TRUE;
7771eb633035STom Caputi break;
7772eb633035STom Caputi case 'r':
7773eb633035STom Caputi flags |= ZFS_ITER_RECURSE;
7774eb633035STom Caputi cb.cb_recursive = B_TRUE;
7775eb633035STom Caputi break;
7776eb633035STom Caputi default:
7777eb633035STom Caputi (void) fprintf(stderr,
7778eb633035STom Caputi gettext("invalid option '%c'\n"), optopt);
7779eb633035STom Caputi usage(B_FALSE);
7780eb633035STom Caputi }
7781eb633035STom Caputi }
7782eb633035STom Caputi
7783eb633035STom Caputi argc -= optind;
7784eb633035STom Caputi argv += optind;
7785eb633035STom Caputi
7786eb633035STom Caputi if (!do_all && argc == 0) {
7787eb633035STom Caputi (void) fprintf(stderr,
7788eb633035STom Caputi gettext("Missing dataset argument or -a option\n"));
7789eb633035STom Caputi usage(B_FALSE);
7790eb633035STom Caputi }
7791eb633035STom Caputi
7792eb633035STom Caputi if (do_all && argc != 0) {
7793eb633035STom Caputi (void) fprintf(stderr,
7794eb633035STom Caputi gettext("Cannot specify dataset with -a option\n"));
7795eb633035STom Caputi usage(B_FALSE);
7796eb633035STom Caputi }
7797eb633035STom Caputi
7798eb633035STom Caputi if (cb.cb_recursive && cb.cb_keylocation != NULL &&
7799eb633035STom Caputi strcmp(cb.cb_keylocation, "prompt") != 0) {
7800eb633035STom Caputi (void) fprintf(stderr, gettext("alternate keylocation may only "
7801eb633035STom Caputi "be 'prompt' with -r or -a\n"));
7802eb633035STom Caputi usage(B_FALSE);
7803eb633035STom Caputi }
7804eb633035STom Caputi
7805eb633035STom Caputi ret = zfs_for_each(argc, argv, flags,
7806eb633035STom Caputi ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,
7807eb633035STom Caputi load_key_callback, &cb);
7808eb633035STom Caputi
7809eb633035STom Caputi if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {
7810eb633035STom Caputi (void) printf(gettext("%llu / %llu key(s) successfully %s\n"),
7811eb633035STom Caputi (u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),
7812eb633035STom Caputi (u_longlong_t)cb.cb_numattempted,
7813eb633035STom Caputi loadkey ? (cb.cb_noop ? "verified" : "loaded") :
7814eb633035STom Caputi "unloaded");
7815eb633035STom Caputi }
7816eb633035STom Caputi
7817eb633035STom Caputi if (cb.cb_numfailed != 0)
7818eb633035STom Caputi ret = -1;
7819eb633035STom Caputi
7820eb633035STom Caputi return (ret);
7821eb633035STom Caputi }
7822eb633035STom Caputi
7823eb633035STom Caputi static int
zfs_do_load_key(int argc,char ** argv)7824eb633035STom Caputi zfs_do_load_key(int argc, char **argv)
7825eb633035STom Caputi {
7826eb633035STom Caputi return (load_unload_keys(argc, argv, B_TRUE));
7827eb633035STom Caputi }
7828eb633035STom Caputi
7829eb633035STom Caputi
7830eb633035STom Caputi static int
zfs_do_unload_key(int argc,char ** argv)7831eb633035STom Caputi zfs_do_unload_key(int argc, char **argv)
7832eb633035STom Caputi {
7833eb633035STom Caputi return (load_unload_keys(argc, argv, B_FALSE));
7834eb633035STom Caputi }
7835eb633035STom Caputi
7836eb633035STom Caputi static int
zfs_do_change_key(int argc,char ** argv)7837eb633035STom Caputi zfs_do_change_key(int argc, char **argv)
7838eb633035STom Caputi {
7839eb633035STom Caputi int c, ret;
7840eb633035STom Caputi uint64_t keystatus;
7841eb633035STom Caputi boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;
7842eb633035STom Caputi zfs_handle_t *zhp = NULL;
7843eb633035STom Caputi nvlist_t *props = fnvlist_alloc();
7844eb633035STom Caputi
7845eb633035STom Caputi while ((c = getopt(argc, argv, "lio:")) != -1) {
7846eb633035STom Caputi switch (c) {
7847eb633035STom Caputi case 'l':
7848eb633035STom Caputi loadkey = B_TRUE;
7849eb633035STom Caputi break;
7850eb633035STom Caputi case 'i':
7851eb633035STom Caputi inheritkey = B_TRUE;
7852eb633035STom Caputi break;
7853eb633035STom Caputi case 'o':
78546ccda740Sloli10K if (!parseprop(props, optarg)) {
7855eb633035STom Caputi nvlist_free(props);
7856eb633035STom Caputi return (1);
7857eb633035STom Caputi }
7858eb633035STom Caputi break;
7859eb633035STom Caputi default:
7860eb633035STom Caputi (void) fprintf(stderr,
7861eb633035STom Caputi gettext("invalid option '%c'\n"), optopt);
7862eb633035STom Caputi usage(B_FALSE);
7863eb633035STom Caputi }
7864eb633035STom Caputi }
7865eb633035STom Caputi
7866eb633035STom Caputi if (inheritkey && !nvlist_empty(props)) {
7867eb633035STom Caputi (void) fprintf(stderr,
7868eb633035STom Caputi gettext("Properties not allowed for inheriting\n"));
7869eb633035STom Caputi usage(B_FALSE);
7870eb633035STom Caputi }
7871eb633035STom Caputi
7872eb633035STom Caputi argc -= optind;
7873eb633035STom Caputi argv += optind;
7874eb633035STom Caputi
7875eb633035STom Caputi if (argc < 1) {
7876eb633035STom Caputi (void) fprintf(stderr, gettext("Missing dataset argument\n"));
7877eb633035STom Caputi usage(B_FALSE);
7878eb633035STom Caputi }
7879eb633035STom Caputi
7880eb633035STom Caputi if (argc > 1) {
7881eb633035STom Caputi (void) fprintf(stderr, gettext("Too many arguments\n"));
7882eb633035STom Caputi usage(B_FALSE);
7883eb633035STom Caputi }
7884eb633035STom Caputi
7885eb633035STom Caputi zhp = zfs_open(g_zfs, argv[argc - 1],
7886eb633035STom Caputi ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
7887eb633035STom Caputi if (zhp == NULL)
7888eb633035STom Caputi usage(B_FALSE);
7889eb633035STom Caputi
7890eb633035STom Caputi if (loadkey) {
7891eb633035STom Caputi keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
7892eb633035STom Caputi if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {
7893eb633035STom Caputi ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);
7894eb633035STom Caputi if (ret != 0) {
7895eb633035STom Caputi nvlist_free(props);
7896eb633035STom Caputi zfs_close(zhp);
7897eb633035STom Caputi return (-1);
7898eb633035STom Caputi }
7899eb633035STom Caputi }
7900eb633035STom Caputi
7901eb633035STom Caputi /* refresh the properties so the new keystatus is visible */
7902eb633035STom Caputi zfs_refresh_properties(zhp);
7903eb633035STom Caputi }
7904eb633035STom Caputi
7905eb633035STom Caputi ret = zfs_crypto_rewrap(zhp, props, inheritkey);
7906eb633035STom Caputi if (ret != 0) {
7907eb633035STom Caputi nvlist_free(props);
7908eb633035STom Caputi zfs_close(zhp);
7909eb633035STom Caputi return (-1);
7910eb633035STom Caputi }
7911eb633035STom Caputi
7912eb633035STom Caputi nvlist_free(props);
7913eb633035STom Caputi zfs_close(zhp);
7914eb633035STom Caputi return (0);
7915eb633035STom Caputi }
7916eb633035STom Caputi
7917f67950b2SNasf-Fan /*
7918f67950b2SNasf-Fan * 1) zfs project [-d|-r] <file|directory ...>
7919f67950b2SNasf-Fan * List project ID and inherit flag of file(s) or directories.
7920f67950b2SNasf-Fan * -d: List the directory itself, not its children.
7921f67950b2SNasf-Fan * -r: List subdirectories recursively.
7922f67950b2SNasf-Fan *
7923f67950b2SNasf-Fan * 2) zfs project -C [-k] [-r] <file|directory ...>
7924f67950b2SNasf-Fan * Clear project inherit flag and/or ID on the file(s) or directories.
7925f67950b2SNasf-Fan * -k: Keep the project ID unchanged. If not specified, the project ID
7926f67950b2SNasf-Fan * will be reset as zero.
7927f67950b2SNasf-Fan * -r: Clear on subdirectories recursively.
7928f67950b2SNasf-Fan *
7929f67950b2SNasf-Fan * 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>
7930f67950b2SNasf-Fan * Check project ID and inherit flag on the file(s) or directories,
7931f67950b2SNasf-Fan * report the outliers.
7932f67950b2SNasf-Fan * -0: Print file name followed by a NUL instead of newline.
7933f67950b2SNasf-Fan * -d: Check the directory itself, not its children.
7934f67950b2SNasf-Fan * -p: Specify the referenced ID for comparing with the target file(s)
7935f67950b2SNasf-Fan * or directories' project IDs. If not specified, the target (top)
7936f67950b2SNasf-Fan * directory's project ID will be used as the referenced one.
7937f67950b2SNasf-Fan * -r: Check subdirectories recursively.
7938f67950b2SNasf-Fan *
7939f67950b2SNasf-Fan * 4) zfs project [-p id] [-r] [-s] <file|directory ...>
7940f67950b2SNasf-Fan * Set project ID and/or inherit flag on the file(s) or directories.
7941f67950b2SNasf-Fan * -p: Set the project ID as the given id.
7942f67950b2SNasf-Fan * -r: Set on subdirectory recursively. If not specify "-p" option,
7943f67950b2SNasf-Fan * it will use top-level directory's project ID as the given id,
7944f67950b2SNasf-Fan * then set both project ID and inherit flag on all descendants
7945f67950b2SNasf-Fan * of the top-level directory.
7946f67950b2SNasf-Fan * -s: Set project inherit flag.
7947f67950b2SNasf-Fan */
7948f67950b2SNasf-Fan static int
zfs_do_project(int argc,char ** argv)7949f67950b2SNasf-Fan zfs_do_project(int argc, char **argv)
7950f67950b2SNasf-Fan {
7951f67950b2SNasf-Fan zfs_project_control_t zpc = {
7952f67950b2SNasf-Fan .zpc_expected_projid = ZFS_INVALID_PROJID,
7953f67950b2SNasf-Fan .zpc_op = ZFS_PROJECT_OP_DEFAULT,
7954f67950b2SNasf-Fan .zpc_dironly = B_FALSE,
7955f67950b2SNasf-Fan .zpc_keep_projid = B_FALSE,
7956f67950b2SNasf-Fan .zpc_newline = B_TRUE,
7957f67950b2SNasf-Fan .zpc_recursive = B_FALSE,
7958f67950b2SNasf-Fan .zpc_set_flag = B_FALSE,
7959f67950b2SNasf-Fan };
7960f67950b2SNasf-Fan int ret = 0, c;
7961f67950b2SNasf-Fan
7962f67950b2SNasf-Fan if (argc < 2)
7963f67950b2SNasf-Fan usage(B_FALSE);
7964f67950b2SNasf-Fan
7965f67950b2SNasf-Fan while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {
7966f67950b2SNasf-Fan switch (c) {
7967f67950b2SNasf-Fan case '0':
7968f67950b2SNasf-Fan zpc.zpc_newline = B_FALSE;
7969f67950b2SNasf-Fan break;
7970f67950b2SNasf-Fan case 'C':
7971f67950b2SNasf-Fan if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
7972f67950b2SNasf-Fan (void) fprintf(stderr, gettext("cannot "
7973f67950b2SNasf-Fan "specify '-C' '-c' '-s' together\n"));
7974f67950b2SNasf-Fan usage(B_FALSE);
7975f67950b2SNasf-Fan }
7976f67950b2SNasf-Fan
7977f67950b2SNasf-Fan zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;
7978f67950b2SNasf-Fan break;
7979f67950b2SNasf-Fan case 'c':
7980f67950b2SNasf-Fan if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
7981f67950b2SNasf-Fan (void) fprintf(stderr, gettext("cannot "
7982f67950b2SNasf-Fan "specify '-C' '-c' '-s' together\n"));
7983f67950b2SNasf-Fan usage(B_FALSE);
7984f67950b2SNasf-Fan }
7985f67950b2SNasf-Fan
7986f67950b2SNasf-Fan zpc.zpc_op = ZFS_PROJECT_OP_CHECK;
7987f67950b2SNasf-Fan break;
7988f67950b2SNasf-Fan case 'd':
7989f67950b2SNasf-Fan zpc.zpc_dironly = B_TRUE;
7990f67950b2SNasf-Fan /* overwrite "-r" option */
7991f67950b2SNasf-Fan zpc.zpc_recursive = B_FALSE;
7992f67950b2SNasf-Fan break;
7993f67950b2SNasf-Fan case 'k':
7994f67950b2SNasf-Fan zpc.zpc_keep_projid = B_TRUE;
7995f67950b2SNasf-Fan break;
7996f67950b2SNasf-Fan case 'p': {
7997f67950b2SNasf-Fan char *endptr;
7998f67950b2SNasf-Fan
7999f67950b2SNasf-Fan errno = 0;
8000f67950b2SNasf-Fan zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);
8001f67950b2SNasf-Fan if (errno != 0 || *endptr != '\0') {
8002f67950b2SNasf-Fan (void) fprintf(stderr,
8003f67950b2SNasf-Fan gettext("project ID must be less than "
8004f67950b2SNasf-Fan "%u\n"), UINT32_MAX);
8005f67950b2SNasf-Fan usage(B_FALSE);
8006f67950b2SNasf-Fan }
8007f67950b2SNasf-Fan if (zpc.zpc_expected_projid >= UINT32_MAX) {
8008f67950b2SNasf-Fan (void) fprintf(stderr,
8009f67950b2SNasf-Fan gettext("invalid project ID\n"));
8010f67950b2SNasf-Fan usage(B_FALSE);
8011f67950b2SNasf-Fan }
8012f67950b2SNasf-Fan break;
8013f67950b2SNasf-Fan }
8014f67950b2SNasf-Fan case 'r':
8015f67950b2SNasf-Fan zpc.zpc_recursive = B_TRUE;
8016f67950b2SNasf-Fan /* overwrite "-d" option */
8017f67950b2SNasf-Fan zpc.zpc_dironly = B_FALSE;
8018f67950b2SNasf-Fan break;
8019f67950b2SNasf-Fan case 's':
8020f67950b2SNasf-Fan if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
8021f67950b2SNasf-Fan (void) fprintf(stderr, gettext("cannot "
8022f67950b2SNasf-Fan "specify '-C' '-c' '-s' together\n"));
8023f67950b2SNasf-Fan usage(B_FALSE);
8024f67950b2SNasf-Fan }
8025f67950b2SNasf-Fan
8026f67950b2SNasf-Fan zpc.zpc_set_flag = B_TRUE;
8027f67950b2SNasf-Fan zpc.zpc_op = ZFS_PROJECT_OP_SET;
8028f67950b2SNasf-Fan break;
8029f67950b2SNasf-Fan default:
8030f67950b2SNasf-Fan (void) fprintf(stderr, gettext("invalid option '%c'\n"),
8031f67950b2SNasf-Fan optopt);
8032f67950b2SNasf-Fan usage(B_FALSE);
8033f67950b2SNasf-Fan }
8034f67950b2SNasf-Fan }
8035f67950b2SNasf-Fan
8036f67950b2SNasf-Fan if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {
8037f67950b2SNasf-Fan if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)
8038f67950b2SNasf-Fan zpc.zpc_op = ZFS_PROJECT_OP_SET;
8039f67950b2SNasf-Fan else
8040f67950b2SNasf-Fan zpc.zpc_op = ZFS_PROJECT_OP_LIST;
8041f67950b2SNasf-Fan }
8042f67950b2SNasf-Fan
8043f67950b2SNasf-Fan switch (zpc.zpc_op) {
8044f67950b2SNasf-Fan case ZFS_PROJECT_OP_LIST:
8045f67950b2SNasf-Fan if (zpc.zpc_keep_projid) {
8046f67950b2SNasf-Fan (void) fprintf(stderr,
8047f67950b2SNasf-Fan gettext("'-k' is only valid together with '-C'\n"));
8048f67950b2SNasf-Fan usage(B_FALSE);
8049f67950b2SNasf-Fan }
8050f67950b2SNasf-Fan if (!zpc.zpc_newline) {
8051f67950b2SNasf-Fan (void) fprintf(stderr,
8052f67950b2SNasf-Fan gettext("'-0' is only valid together with '-c'\n"));
8053f67950b2SNasf-Fan usage(B_FALSE);
8054f67950b2SNasf-Fan }
8055f67950b2SNasf-Fan break;
8056f67950b2SNasf-Fan case ZFS_PROJECT_OP_CHECK:
8057f67950b2SNasf-Fan if (zpc.zpc_keep_projid) {
8058f67950b2SNasf-Fan (void) fprintf(stderr,
8059f67950b2SNasf-Fan gettext("'-k' is only valid together with '-C'\n"));
8060f67950b2SNasf-Fan usage(B_FALSE);
8061f67950b2SNasf-Fan }
8062f67950b2SNasf-Fan break;
8063f67950b2SNasf-Fan case ZFS_PROJECT_OP_CLEAR:
8064f67950b2SNasf-Fan if (zpc.zpc_dironly) {
8065f67950b2SNasf-Fan (void) fprintf(stderr,
8066f67950b2SNasf-Fan gettext("'-d' is useless together with '-C'\n"));
8067f67950b2SNasf-Fan usage(B_FALSE);
8068f67950b2SNasf-Fan }
8069f67950b2SNasf-Fan if (!zpc.zpc_newline) {
8070f67950b2SNasf-Fan (void) fprintf(stderr,
8071f67950b2SNasf-Fan gettext("'-0' is only valid together with '-c'\n"));
8072f67950b2SNasf-Fan usage(B_FALSE);
8073f67950b2SNasf-Fan }
8074f67950b2SNasf-Fan if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {
8075f67950b2SNasf-Fan (void) fprintf(stderr,
8076f67950b2SNasf-Fan gettext("'-p' is useless together with '-C'\n"));
8077f67950b2SNasf-Fan usage(B_FALSE);
8078f67950b2SNasf-Fan }
8079f67950b2SNasf-Fan break;
8080f67950b2SNasf-Fan case ZFS_PROJECT_OP_SET:
8081f67950b2SNasf-Fan if (zpc.zpc_dironly) {
8082f67950b2SNasf-Fan (void) fprintf(stderr,
8083f67950b2SNasf-Fan gettext("'-d' is useless for set project ID and/or "
8084f67950b2SNasf-Fan "inherit flag\n"));
8085f67950b2SNasf-Fan usage(B_FALSE);
8086f67950b2SNasf-Fan }
8087f67950b2SNasf-Fan if (zpc.zpc_keep_projid) {
8088f67950b2SNasf-Fan (void) fprintf(stderr,
8089f67950b2SNasf-Fan gettext("'-k' is only valid together with '-C'\n"));
8090f67950b2SNasf-Fan usage(B_FALSE);
8091f67950b2SNasf-Fan }
8092f67950b2SNasf-Fan if (!zpc.zpc_newline) {
8093f67950b2SNasf-Fan (void) fprintf(stderr,
8094f67950b2SNasf-Fan gettext("'-0' is only valid together with '-c'\n"));
8095f67950b2SNasf-Fan usage(B_FALSE);
8096f67950b2SNasf-Fan }
8097f67950b2SNasf-Fan break;
8098f67950b2SNasf-Fan default:
8099f67950b2SNasf-Fan ASSERT(0);
8100f67950b2SNasf-Fan break;
8101f67950b2SNasf-Fan }
8102f67950b2SNasf-Fan
8103f67950b2SNasf-Fan argv += optind;
8104f67950b2SNasf-Fan argc -= optind;
8105f67950b2SNasf-Fan if (argc == 0) {
8106f67950b2SNasf-Fan (void) fprintf(stderr,
8107f67950b2SNasf-Fan gettext("missing file or directory target(s)\n"));
8108f67950b2SNasf-Fan usage(B_FALSE);
8109f67950b2SNasf-Fan }
8110f67950b2SNasf-Fan
8111f67950b2SNasf-Fan for (int i = 0; i < argc; i++) {
8112f67950b2SNasf-Fan int err;
8113f67950b2SNasf-Fan
8114f67950b2SNasf-Fan err = zfs_project_handle(argv[i], &zpc);
8115f67950b2SNasf-Fan if (err && !ret)
8116f67950b2SNasf-Fan ret = err;
8117f67950b2SNasf-Fan }
8118f67950b2SNasf-Fan
8119f67950b2SNasf-Fan return (ret);
8120f67950b2SNasf-Fan }
8121f67950b2SNasf-Fan
8122fa9e4066Sahrens int
main(int argc,char ** argv)8123fa9e4066Sahrens main(int argc, char **argv)
8124fa9e4066Sahrens {
812505c998d6SRichard Lowe int ret = 0;
8126fa9e4066Sahrens int i;
8127fa9e4066Sahrens char *progname;
8128fa9e4066Sahrens char *cmdname;
8129fa9e4066Sahrens
8130fa9e4066Sahrens (void) setlocale(LC_ALL, "");
8131fa9e4066Sahrens (void) textdomain(TEXT_DOMAIN);
8132fa9e4066Sahrens
8133fa9e4066Sahrens opterr = 0;
8134fa9e4066Sahrens
813599653d4eSeschrock if ((g_zfs = libzfs_init()) == NULL) {
813699653d4eSeschrock (void) fprintf(stderr, gettext("internal error: failed to "
813799653d4eSeschrock "initialize ZFS library\n"));
813899653d4eSeschrock return (1);
813999653d4eSeschrock }
814099653d4eSeschrock
81414445fffbSMatthew Ahrens zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
8142ecd6cf80Smarks
814399653d4eSeschrock libzfs_print_on_error(g_zfs, B_TRUE);
814499653d4eSeschrock
8145fa9e4066Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
8146fa9e4066Sahrens (void) fprintf(stderr, gettext("internal error: unable to "
8147fa9e4066Sahrens "open %s\n"), MNTTAB);
8148fa9e4066Sahrens return (1);
8149fa9e4066Sahrens }
8150fa9e4066Sahrens
8151fa9e4066Sahrens /*
8152fa9e4066Sahrens * This command also doubles as the /etc/fs mount and unmount program.
8153fa9e4066Sahrens * Determine if we should take this behavior based on argv[0].
8154fa9e4066Sahrens */
8155fa9e4066Sahrens progname = basename(argv[0]);
8156fa9e4066Sahrens if (strcmp(progname, "mount") == 0) {
8157fa9e4066Sahrens ret = manual_mount(argc, argv);
8158fa9e4066Sahrens } else if (strcmp(progname, "umount") == 0) {
8159fa9e4066Sahrens ret = manual_unmount(argc, argv);
8160fa9e4066Sahrens } else {
8161fa9e4066Sahrens /*
8162fa9e4066Sahrens * Make sure the user has specified some command.
8163fa9e4066Sahrens */
8164fa9e4066Sahrens if (argc < 2) {
8165fa9e4066Sahrens (void) fprintf(stderr, gettext("missing command\n"));
816699653d4eSeschrock usage(B_FALSE);
8167fa9e4066Sahrens }
8168fa9e4066Sahrens
8169fa9e4066Sahrens cmdname = argv[1];
8170fa9e4066Sahrens
8171fa9e4066Sahrens /*
8172fa9e4066Sahrens * The 'umount' command is an alias for 'unmount'
8173fa9e4066Sahrens */
8174fa9e4066Sahrens if (strcmp(cmdname, "umount") == 0)
8175fa9e4066Sahrens cmdname = "unmount";
8176fa9e4066Sahrens
8177fa9e4066Sahrens /*
8178f2a3c691Sahrens * The 'recv' command is an alias for 'receive'
8179f2a3c691Sahrens */
8180f2a3c691Sahrens if (strcmp(cmdname, "recv") == 0)
8181f2a3c691Sahrens cmdname = "receive";
8182f2a3c691Sahrens
8183f2a3c691Sahrens /*
81847dbbcd83SAdam Stevko * The 'snap' command is an alias for 'snapshot'
81857dbbcd83SAdam Stevko */
81867dbbcd83SAdam Stevko if (strcmp(cmdname, "snap") == 0)
81877dbbcd83SAdam Stevko cmdname = "snapshot";
81887dbbcd83SAdam Stevko
81897dbbcd83SAdam Stevko /*
8190fa9e4066Sahrens * Special case '-?'
8191fa9e4066Sahrens */
8192fa9e4066Sahrens if (strcmp(cmdname, "-?") == 0)
819399653d4eSeschrock usage(B_TRUE);
8194fa9e4066Sahrens
8195fa9e4066Sahrens /*
8196fa9e4066Sahrens * Run the appropriate command.
8197fa9e4066Sahrens */
8198b2634b9cSEric Taylor libzfs_mnttab_cache(g_zfs, B_TRUE);
8199ecd6cf80Smarks if (find_command_idx(cmdname, &i) == 0) {
8200fa9e4066Sahrens current_command = &command_table[i];
8201fa9e4066Sahrens ret = command_table[i].func(argc - 1, argv + 1);
820291ebeef5Sahrens } else if (strchr(cmdname, '=') != NULL) {
820391ebeef5Sahrens verify(find_command_idx("set", &i) == 0);
8204ecd6cf80Smarks current_command = &command_table[i];
8205ecd6cf80Smarks ret = command_table[i].func(argc, argv);
8206ecd6cf80Smarks } else {
8207fa9e4066Sahrens (void) fprintf(stderr, gettext("unrecognized "
8208fa9e4066Sahrens "command '%s'\n"), cmdname);
820999653d4eSeschrock usage(B_FALSE);
8210fa9e4066Sahrens }
8211b2634b9cSEric Taylor libzfs_mnttab_cache(g_zfs, B_FALSE);
8212fa9e4066Sahrens }
8213fa9e4066Sahrens
8214fa9e4066Sahrens (void) fclose(mnttab_file);
8215fa9e4066Sahrens
82164445fffbSMatthew Ahrens if (ret == 0 && log_history)
82174445fffbSMatthew Ahrens (void) zpool_log_history(g_zfs, history_str);
82184445fffbSMatthew Ahrens
821999653d4eSeschrock libzfs_fini(g_zfs);
822099653d4eSeschrock
8221fa9e4066Sahrens /*
8222fa9e4066Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit
8223fa9e4066Sahrens * for the purposes of running ::findleaks.
8224fa9e4066Sahrens */
8225fa9e4066Sahrens if (getenv("ZFS_ABORT") != NULL) {
8226fa9e4066Sahrens (void) printf("dumping core by request\n");
8227fa9e4066Sahrens abort();
8228fa9e4066Sahrens }
8229fa9e4066Sahrens
8230fa9e4066Sahrens return (ret);
8231fa9e4066Sahrens }
8232