Print this page
FITS: generating send-streams in portable format
This commit adds the command 'zfs fits-send', analogous to zfs send. The
generated send stream is compatible with the stream generated with that
from 'btrfs send' and can in principle easily be received to any filesystem.
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/zfs/zfs_main.c
+++ new/usr/src/cmd/zfs/zfs_main.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
25 25 * Copyright (c) 2012 by Delphix. All rights reserved.
26 26 * Copyright 2012 Milan Jurik. All rights reserved.
27 27 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
28 28 */
29 29
30 30 #include <assert.h>
31 31 #include <ctype.h>
32 32 #include <errno.h>
33 33 #include <libgen.h>
34 34 #include <libintl.h>
35 35 #include <libuutil.h>
36 36 #include <libnvpair.h>
37 37 #include <locale.h>
38 38 #include <stddef.h>
39 39 #include <stdio.h>
40 40 #include <stdlib.h>
41 41 #include <strings.h>
42 42 #include <unistd.h>
43 43 #include <fcntl.h>
44 44 #include <zone.h>
45 45 #include <grp.h>
46 46 #include <pwd.h>
47 47 #include <signal.h>
48 48 #include <sys/list.h>
49 49 #include <sys/mkdev.h>
50 50 #include <sys/mntent.h>
51 51 #include <sys/mnttab.h>
52 52 #include <sys/mount.h>
53 53 #include <sys/stat.h>
54 54 #include <sys/fs/zfs.h>
55 55 #include <sys/types.h>
56 56 #include <time.h>
57 57
58 58 #include <libzfs.h>
59 59 #include <libzfs_core.h>
60 60 #include <zfs_prop.h>
61 61 #include <zfs_deleg.h>
62 62 #include <libuutil.h>
63 63 #include <aclutils.h>
64 64 #include <directory.h>
65 65
66 66 #include "zfs_iter.h"
67 67 #include "zfs_util.h"
68 68 #include "zfs_comutil.h"
69 69
70 70 libzfs_handle_t *g_zfs;
71 71
72 72 static FILE *mnttab_file;
73 73 static char history_str[HIS_MAX_RECORD_LEN];
74 74 static boolean_t log_history = B_TRUE;
75 75
76 76 static int zfs_do_clone(int argc, char **argv);
77 77 static int zfs_do_create(int argc, char **argv);
78 78 static int zfs_do_destroy(int argc, char **argv);
79 79 static int zfs_do_get(int argc, char **argv);
80 80 static int zfs_do_inherit(int argc, char **argv);
81 81 static int zfs_do_list(int argc, char **argv);
↓ open down ↓ |
81 lines elided |
↑ open up ↑ |
82 82 static int zfs_do_mount(int argc, char **argv);
83 83 static int zfs_do_rename(int argc, char **argv);
84 84 static int zfs_do_rollback(int argc, char **argv);
85 85 static int zfs_do_set(int argc, char **argv);
86 86 static int zfs_do_upgrade(int argc, char **argv);
87 87 static int zfs_do_snapshot(int argc, char **argv);
88 88 static int zfs_do_unmount(int argc, char **argv);
89 89 static int zfs_do_share(int argc, char **argv);
90 90 static int zfs_do_unshare(int argc, char **argv);
91 91 static int zfs_do_send(int argc, char **argv);
92 +static int zfs_do_fits_send(int argc, char **argv);
92 93 static int zfs_do_receive(int argc, char **argv);
93 94 static int zfs_do_promote(int argc, char **argv);
94 95 static int zfs_do_userspace(int argc, char **argv);
95 96 static int zfs_do_allow(int argc, char **argv);
96 97 static int zfs_do_unallow(int argc, char **argv);
97 98 static int zfs_do_hold(int argc, char **argv);
98 99 static int zfs_do_holds(int argc, char **argv);
99 100 static int zfs_do_release(int argc, char **argv);
100 101 static int zfs_do_diff(int argc, char **argv);
101 102
102 103 /*
103 104 * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
104 105 */
105 106
106 107 #ifdef DEBUG
107 108 const char *
108 109 _umem_debug_init(void)
109 110 {
110 111 return ("default,verbose"); /* $UMEM_DEBUG setting */
111 112 }
112 113
113 114 const char *
114 115 _umem_logging_init(void)
115 116 {
116 117 return ("fail,contents"); /* $UMEM_LOGGING setting */
117 118 }
118 119 #endif
119 120
120 121 typedef enum {
121 122 HELP_CLONE,
122 123 HELP_CREATE,
123 124 HELP_DESTROY,
↓ open down ↓ |
22 lines elided |
↑ open up ↑ |
124 125 HELP_GET,
125 126 HELP_INHERIT,
126 127 HELP_UPGRADE,
127 128 HELP_LIST,
128 129 HELP_MOUNT,
129 130 HELP_PROMOTE,
130 131 HELP_RECEIVE,
131 132 HELP_RENAME,
132 133 HELP_ROLLBACK,
133 134 HELP_SEND,
135 + HELP_FITS_SEND,
134 136 HELP_SET,
135 137 HELP_SHARE,
136 138 HELP_SNAPSHOT,
137 139 HELP_UNMOUNT,
138 140 HELP_UNSHARE,
139 141 HELP_ALLOW,
140 142 HELP_UNALLOW,
141 143 HELP_USERSPACE,
142 144 HELP_GROUPSPACE,
143 145 HELP_HOLD,
144 146 HELP_HOLDS,
145 147 HELP_RELEASE,
146 148 HELP_DIFF,
147 149 } zfs_help_t;
148 150
149 151 typedef struct zfs_command {
150 152 const char *name;
151 153 int (*func)(int argc, char **argv);
152 154 zfs_help_t usage;
153 155 } zfs_command_t;
154 156
155 157 /*
156 158 * Master command table. Each ZFS command has a name, associated function, and
157 159 * usage message. The usage messages need to be internationalized, so we have
158 160 * to have a function to return the usage message based on a command index.
159 161 *
160 162 * These commands are organized according to how they are displayed in the usage
161 163 * message. An empty command (one with a NULL name) indicates an empty line in
162 164 * the generic usage message.
163 165 */
164 166 static zfs_command_t command_table[] = {
165 167 { "create", zfs_do_create, HELP_CREATE },
166 168 { "destroy", zfs_do_destroy, HELP_DESTROY },
167 169 { NULL },
168 170 { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },
169 171 { "rollback", zfs_do_rollback, HELP_ROLLBACK },
170 172 { "clone", zfs_do_clone, HELP_CLONE },
171 173 { "promote", zfs_do_promote, HELP_PROMOTE },
172 174 { "rename", zfs_do_rename, HELP_RENAME },
173 175 { NULL },
174 176 { "list", zfs_do_list, HELP_LIST },
175 177 { NULL },
176 178 { "set", zfs_do_set, HELP_SET },
177 179 { "get", zfs_do_get, HELP_GET },
178 180 { "inherit", zfs_do_inherit, HELP_INHERIT },
179 181 { "upgrade", zfs_do_upgrade, HELP_UPGRADE },
180 182 { "userspace", zfs_do_userspace, HELP_USERSPACE },
↓ open down ↓ |
37 lines elided |
↑ open up ↑ |
181 183 { "groupspace", zfs_do_userspace, HELP_GROUPSPACE },
182 184 { NULL },
183 185 { "mount", zfs_do_mount, HELP_MOUNT },
184 186 { "unmount", zfs_do_unmount, HELP_UNMOUNT },
185 187 { "share", zfs_do_share, HELP_SHARE },
186 188 { "unshare", zfs_do_unshare, HELP_UNSHARE },
187 189 { NULL },
188 190 { "send", zfs_do_send, HELP_SEND },
189 191 { "receive", zfs_do_receive, HELP_RECEIVE },
190 192 { NULL },
193 + { "fits-send", zfs_do_fits_send, HELP_FITS_SEND },
194 + { NULL },
191 195 { "allow", zfs_do_allow, HELP_ALLOW },
192 196 { NULL },
193 197 { "unallow", zfs_do_unallow, HELP_UNALLOW },
194 198 { NULL },
195 199 { "hold", zfs_do_hold, HELP_HOLD },
196 200 { "holds", zfs_do_holds, HELP_HOLDS },
197 201 { "release", zfs_do_release, HELP_RELEASE },
198 202 { "diff", zfs_do_diff, HELP_DIFF },
199 203 };
200 204
201 205 #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
202 206
203 207 zfs_command_t *current_command;
204 208
205 209 static const char *
206 210 get_usage(zfs_help_t idx)
207 211 {
208 212 switch (idx) {
209 213 case HELP_CLONE:
210 214 return (gettext("\tclone [-p] [-o property=value] ... "
211 215 "<snapshot> <filesystem|volume>\n"));
212 216 case HELP_CREATE:
213 217 return (gettext("\tcreate [-p] [-o property=value] ... "
214 218 "<filesystem>\n"
215 219 "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
216 220 "-V <size> <volume>\n"));
217 221 case HELP_DESTROY:
218 222 return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
219 223 "\tdestroy [-dnpRrv] "
220 224 "<filesystem|volume>@<snap>[%<snap>][,...]\n"));
221 225 case HELP_GET:
222 226 return (gettext("\tget [-rHp] [-d max] "
223 227 "[-o \"all\" | field[,...]] [-t type[,...]] "
224 228 "[-s source[,...]]\n"
225 229 "\t <\"all\" | property[,...]> "
226 230 "[filesystem|volume|snapshot] ...\n"));
227 231 case HELP_INHERIT:
228 232 return (gettext("\tinherit [-rS] <property> "
229 233 "<filesystem|volume|snapshot> ...\n"));
230 234 case HELP_UPGRADE:
231 235 return (gettext("\tupgrade [-v]\n"
232 236 "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
233 237 case HELP_LIST:
234 238 return (gettext("\tlist [-rH][-d max] "
235 239 "[-o property[,...]] [-t type[,...]] [-s property] ...\n"
236 240 "\t [-S property] ... "
237 241 "[filesystem|volume|snapshot] ...\n"));
238 242 case HELP_MOUNT:
239 243 return (gettext("\tmount\n"
240 244 "\tmount [-vO] [-o opts] <-a | filesystem>\n"));
241 245 case HELP_PROMOTE:
242 246 return (gettext("\tpromote <clone-filesystem>\n"));
243 247 case HELP_RECEIVE:
244 248 return (gettext("\treceive [-vnFu] <filesystem|volume|"
245 249 "snapshot>\n"
246 250 "\treceive [-vnFu] [-d | -e] <filesystem>\n"));
↓ open down ↓ |
46 lines elided |
↑ open up ↑ |
247 251 case HELP_RENAME:
248 252 return (gettext("\trename [-f] <filesystem|volume|snapshot> "
249 253 "<filesystem|volume|snapshot>\n"
250 254 "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
251 255 "\trename -r <snapshot> <snapshot>"));
252 256 case HELP_ROLLBACK:
253 257 return (gettext("\trollback [-rRf] <snapshot>\n"));
254 258 case HELP_SEND:
255 259 return (gettext("\tsend [-DnPpRv] [-[iI] snapshot] "
256 260 "<snapshot>\n"));
261 + case HELP_FITS_SEND:
262 + return (gettext("\tfits-send [-v] [-i snapshot] "
263 + "<snapshot>\n"));
257 264 case HELP_SET:
258 265 return (gettext("\tset <property=value> "
259 266 "<filesystem|volume|snapshot> ...\n"));
260 267 case HELP_SHARE:
261 268 return (gettext("\tshare <-a | filesystem>\n"));
262 269 case HELP_SNAPSHOT:
263 270 return (gettext("\tsnapshot [-r] [-o property=value] ... "
264 271 "<filesystem@snapname|volume@snapname> ...\n"));
265 272 case HELP_UNMOUNT:
266 273 return (gettext("\tunmount [-f] "
267 274 "<-a | filesystem|mountpoint>\n"));
268 275 case HELP_UNSHARE:
269 276 return (gettext("\tunshare "
270 277 "<-a | filesystem|mountpoint>\n"));
271 278 case HELP_ALLOW:
272 279 return (gettext("\tallow <filesystem|volume>\n"
273 280 "\tallow [-ldug] "
274 281 "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
275 282 "\t <filesystem|volume>\n"
276 283 "\tallow [-ld] -e <perm|@setname>[,...] "
277 284 "<filesystem|volume>\n"
278 285 "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
279 286 "\tallow -s @setname <perm|@setname>[,...] "
280 287 "<filesystem|volume>\n"));
281 288 case HELP_UNALLOW:
282 289 return (gettext("\tunallow [-rldug] "
283 290 "<\"everyone\"|user|group>[,...]\n"
284 291 "\t [<perm|@setname>[,...]] <filesystem|volume>\n"
285 292 "\tunallow [-rld] -e [<perm|@setname>[,...]] "
286 293 "<filesystem|volume>\n"
287 294 "\tunallow [-r] -c [<perm|@setname>[,...]] "
288 295 "<filesystem|volume>\n"
289 296 "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
290 297 "<filesystem|volume>\n"));
291 298 case HELP_USERSPACE:
292 299 return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
293 300 "[-s field] ...\n\t[-S field] ... "
294 301 "[-t type[,...]] <filesystem|snapshot>\n"));
295 302 case HELP_GROUPSPACE:
296 303 return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
297 304 "[-s field] ...\n\t[-S field] ... "
298 305 "[-t type[,...]] <filesystem|snapshot>\n"));
299 306 case HELP_HOLD:
300 307 return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
301 308 case HELP_HOLDS:
302 309 return (gettext("\tholds [-r] <snapshot> ...\n"));
303 310 case HELP_RELEASE:
304 311 return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
305 312 case HELP_DIFF:
306 313 return (gettext("\tdiff [-FHt] <snapshot> "
307 314 "[snapshot|filesystem]\n"));
308 315 }
309 316
310 317 abort();
311 318 /* NOTREACHED */
312 319 }
313 320
314 321 void
315 322 nomem(void)
316 323 {
317 324 (void) fprintf(stderr, gettext("internal error: out of memory\n"));
318 325 exit(1);
319 326 }
320 327
321 328 /*
322 329 * Utility function to guarantee malloc() success.
323 330 */
324 331
325 332 void *
326 333 safe_malloc(size_t size)
327 334 {
328 335 void *data;
329 336
330 337 if ((data = calloc(1, size)) == NULL)
331 338 nomem();
332 339
333 340 return (data);
334 341 }
335 342
336 343 static char *
337 344 safe_strdup(char *str)
338 345 {
339 346 char *dupstr = strdup(str);
340 347
341 348 if (dupstr == NULL)
342 349 nomem();
343 350
344 351 return (dupstr);
345 352 }
346 353
347 354 /*
348 355 * Callback routine that will print out information for each of
349 356 * the properties.
350 357 */
351 358 static int
352 359 usage_prop_cb(int prop, void *cb)
353 360 {
354 361 FILE *fp = cb;
355 362
356 363 (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
357 364
358 365 if (zfs_prop_readonly(prop))
359 366 (void) fprintf(fp, " NO ");
360 367 else
361 368 (void) fprintf(fp, "YES ");
362 369
363 370 if (zfs_prop_inheritable(prop))
364 371 (void) fprintf(fp, " YES ");
365 372 else
366 373 (void) fprintf(fp, " NO ");
367 374
368 375 if (zfs_prop_values(prop) == NULL)
369 376 (void) fprintf(fp, "-\n");
370 377 else
371 378 (void) fprintf(fp, "%s\n", zfs_prop_values(prop));
372 379
373 380 return (ZPROP_CONT);
374 381 }
375 382
376 383 /*
377 384 * Display usage message. If we're inside a command, display only the usage for
378 385 * that command. Otherwise, iterate over the entire command table and display
379 386 * a complete usage message.
380 387 */
381 388 static void
382 389 usage(boolean_t requested)
383 390 {
384 391 int i;
385 392 boolean_t show_properties = B_FALSE;
386 393 FILE *fp = requested ? stdout : stderr;
387 394
388 395 if (current_command == NULL) {
389 396
390 397 (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
391 398 (void) fprintf(fp,
392 399 gettext("where 'command' is one of the following:\n\n"));
393 400
394 401 for (i = 0; i < NCOMMAND; i++) {
395 402 if (command_table[i].name == NULL)
396 403 (void) fprintf(fp, "\n");
397 404 else
398 405 (void) fprintf(fp, "%s",
399 406 get_usage(command_table[i].usage));
400 407 }
401 408
402 409 (void) fprintf(fp, gettext("\nEach dataset is of the form: "
403 410 "pool/[dataset/]*dataset[@name]\n"));
404 411 } else {
405 412 (void) fprintf(fp, gettext("usage:\n"));
406 413 (void) fprintf(fp, "%s", get_usage(current_command->usage));
407 414 }
408 415
409 416 if (current_command != NULL &&
410 417 (strcmp(current_command->name, "set") == 0 ||
411 418 strcmp(current_command->name, "get") == 0 ||
412 419 strcmp(current_command->name, "inherit") == 0 ||
413 420 strcmp(current_command->name, "list") == 0))
414 421 show_properties = B_TRUE;
415 422
416 423 if (show_properties) {
417 424 (void) fprintf(fp,
418 425 gettext("\nThe following properties are supported:\n"));
419 426
420 427 (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n",
421 428 "PROPERTY", "EDIT", "INHERIT", "VALUES");
422 429
423 430 /* Iterate over all properties */
424 431 (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
425 432 ZFS_TYPE_DATASET);
426 433
427 434 (void) fprintf(fp, "\t%-15s ", "userused@...");
428 435 (void) fprintf(fp, " NO NO <size>\n");
429 436 (void) fprintf(fp, "\t%-15s ", "groupused@...");
430 437 (void) fprintf(fp, " NO NO <size>\n");
431 438 (void) fprintf(fp, "\t%-15s ", "userquota@...");
432 439 (void) fprintf(fp, "YES NO <size> | none\n");
433 440 (void) fprintf(fp, "\t%-15s ", "groupquota@...");
434 441 (void) fprintf(fp, "YES NO <size> | none\n");
435 442 (void) fprintf(fp, "\t%-15s ", "written@<snap>");
436 443 (void) fprintf(fp, " NO NO <size>\n");
437 444
438 445 (void) fprintf(fp, gettext("\nSizes are specified in bytes "
439 446 "with standard units such as K, M, G, etc.\n"));
440 447 (void) fprintf(fp, gettext("\nUser-defined properties can "
441 448 "be specified by using a name containing a colon (:).\n"));
442 449 (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ "
443 450 "properties must be appended with\n"
444 451 "a user or group specifier of one of these forms:\n"
445 452 " POSIX name (eg: \"matt\")\n"
446 453 " POSIX id (eg: \"126829\")\n"
447 454 " SMB name@domain (eg: \"matt@sun\")\n"
448 455 " SMB SID (eg: \"S-1-234-567-89\")\n"));
449 456 } else {
450 457 (void) fprintf(fp,
451 458 gettext("\nFor the property list, run: %s\n"),
452 459 "zfs set|get");
453 460 (void) fprintf(fp,
454 461 gettext("\nFor the delegated permission list, run: %s\n"),
455 462 "zfs allow|unallow");
456 463 }
457 464
458 465 /*
459 466 * See comments at end of main().
460 467 */
461 468 if (getenv("ZFS_ABORT") != NULL) {
462 469 (void) printf("dumping core by request\n");
463 470 abort();
464 471 }
465 472
466 473 exit(requested ? 0 : 2);
467 474 }
468 475
469 476 static int
470 477 parseprop(nvlist_t *props)
471 478 {
472 479 char *propname = optarg;
473 480 char *propval, *strval;
474 481
475 482 if ((propval = strchr(propname, '=')) == NULL) {
476 483 (void) fprintf(stderr, gettext("missing "
477 484 "'=' for -o option\n"));
478 485 return (-1);
479 486 }
480 487 *propval = '\0';
481 488 propval++;
482 489 if (nvlist_lookup_string(props, propname, &strval) == 0) {
483 490 (void) fprintf(stderr, gettext("property '%s' "
484 491 "specified multiple times\n"), propname);
485 492 return (-1);
486 493 }
487 494 if (nvlist_add_string(props, propname, propval) != 0)
488 495 nomem();
489 496 return (0);
490 497 }
491 498
492 499 static int
493 500 parse_depth(char *opt, int *flags)
494 501 {
495 502 char *tmp;
496 503 int depth;
497 504
498 505 depth = (int)strtol(opt, &tmp, 0);
499 506 if (*tmp) {
500 507 (void) fprintf(stderr,
501 508 gettext("%s is not an integer\n"), optarg);
502 509 usage(B_FALSE);
503 510 }
504 511 if (depth < 0) {
505 512 (void) fprintf(stderr,
506 513 gettext("Depth can not be negative.\n"));
507 514 usage(B_FALSE);
508 515 }
509 516 *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
510 517 return (depth);
511 518 }
512 519
513 520 #define PROGRESS_DELAY 2 /* seconds */
514 521
515 522 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";
516 523 static time_t pt_begin;
517 524 static char *pt_header = NULL;
518 525 static boolean_t pt_shown;
519 526
520 527 static void
521 528 start_progress_timer(void)
522 529 {
523 530 pt_begin = time(NULL) + PROGRESS_DELAY;
524 531 pt_shown = B_FALSE;
525 532 }
526 533
527 534 static void
528 535 set_progress_header(char *header)
529 536 {
530 537 assert(pt_header == NULL);
531 538 pt_header = safe_strdup(header);
532 539 if (pt_shown) {
533 540 (void) printf("%s: ", header);
534 541 (void) fflush(stdout);
535 542 }
536 543 }
537 544
538 545 static void
539 546 update_progress(char *update)
540 547 {
541 548 if (!pt_shown && time(NULL) > pt_begin) {
542 549 int len = strlen(update);
543 550
544 551 (void) printf("%s: %s%*.*s", pt_header, update, len, len,
545 552 pt_reverse);
546 553 (void) fflush(stdout);
547 554 pt_shown = B_TRUE;
548 555 } else if (pt_shown) {
549 556 int len = strlen(update);
550 557
551 558 (void) printf("%s%*.*s", update, len, len, pt_reverse);
552 559 (void) fflush(stdout);
553 560 }
554 561 }
555 562
556 563 static void
557 564 finish_progress(char *done)
558 565 {
559 566 if (pt_shown) {
560 567 (void) printf("%s\n", done);
561 568 (void) fflush(stdout);
562 569 }
563 570 free(pt_header);
564 571 pt_header = NULL;
565 572 }
566 573 /*
567 574 * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
568 575 *
569 576 * Given an existing dataset, create a writable copy whose initial contents
570 577 * are the same as the source. The newly created dataset maintains a
571 578 * dependency on the original; the original cannot be destroyed so long as
572 579 * the clone exists.
573 580 *
574 581 * The '-p' flag creates all the non-existing ancestors of the target first.
575 582 */
576 583 static int
577 584 zfs_do_clone(int argc, char **argv)
578 585 {
579 586 zfs_handle_t *zhp = NULL;
580 587 boolean_t parents = B_FALSE;
581 588 nvlist_t *props;
582 589 int ret = 0;
583 590 int c;
584 591
585 592 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
586 593 nomem();
587 594
588 595 /* check options */
589 596 while ((c = getopt(argc, argv, "o:p")) != -1) {
590 597 switch (c) {
591 598 case 'o':
592 599 if (parseprop(props))
593 600 return (1);
594 601 break;
595 602 case 'p':
596 603 parents = B_TRUE;
597 604 break;
598 605 case '?':
599 606 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
600 607 optopt);
601 608 goto usage;
602 609 }
603 610 }
604 611
605 612 argc -= optind;
606 613 argv += optind;
607 614
608 615 /* check number of arguments */
609 616 if (argc < 1) {
610 617 (void) fprintf(stderr, gettext("missing source dataset "
611 618 "argument\n"));
612 619 goto usage;
613 620 }
614 621 if (argc < 2) {
615 622 (void) fprintf(stderr, gettext("missing target dataset "
616 623 "argument\n"));
617 624 goto usage;
618 625 }
619 626 if (argc > 2) {
620 627 (void) fprintf(stderr, gettext("too many arguments\n"));
621 628 goto usage;
622 629 }
623 630
624 631 /* open the source dataset */
625 632 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
626 633 return (1);
627 634
628 635 if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
629 636 ZFS_TYPE_VOLUME)) {
630 637 /*
631 638 * Now create the ancestors of the target dataset. If the
632 639 * target already exists and '-p' option was used we should not
633 640 * complain.
634 641 */
635 642 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
636 643 ZFS_TYPE_VOLUME))
637 644 return (0);
638 645 if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
639 646 return (1);
640 647 }
641 648
642 649 /* pass to libzfs */
643 650 ret = zfs_clone(zhp, argv[1], props);
644 651
645 652 /* create the mountpoint if necessary */
646 653 if (ret == 0) {
647 654 zfs_handle_t *clone;
648 655
649 656 clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
650 657 if (clone != NULL) {
651 658 if (zfs_get_type(clone) != ZFS_TYPE_VOLUME)
652 659 if ((ret = zfs_mount(clone, NULL, 0)) == 0)
653 660 ret = zfs_share(clone);
654 661 zfs_close(clone);
655 662 }
656 663 }
657 664
658 665 zfs_close(zhp);
659 666 nvlist_free(props);
660 667
661 668 return (!!ret);
662 669
663 670 usage:
664 671 if (zhp)
665 672 zfs_close(zhp);
666 673 nvlist_free(props);
667 674 usage(B_FALSE);
668 675 return (-1);
669 676 }
670 677
671 678 /*
672 679 * zfs create [-p] [-o prop=value] ... fs
673 680 * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
674 681 *
675 682 * Create a new dataset. This command can be used to create filesystems
676 683 * and volumes. Snapshot creation is handled by 'zfs snapshot'.
677 684 * For volumes, the user must specify a size to be used.
678 685 *
679 686 * The '-s' flag applies only to volumes, and indicates that we should not try
680 687 * to set the reservation for this volume. By default we set a reservation
681 688 * equal to the size for any volume. For pools with SPA_VERSION >=
682 689 * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
683 690 *
684 691 * The '-p' flag creates all the non-existing ancestors of the target first.
685 692 */
686 693 static int
687 694 zfs_do_create(int argc, char **argv)
688 695 {
689 696 zfs_type_t type = ZFS_TYPE_FILESYSTEM;
690 697 zfs_handle_t *zhp = NULL;
691 698 uint64_t volsize;
692 699 int c;
693 700 boolean_t noreserve = B_FALSE;
694 701 boolean_t bflag = B_FALSE;
695 702 boolean_t parents = B_FALSE;
696 703 int ret = 1;
697 704 nvlist_t *props;
698 705 uint64_t intval;
699 706 int canmount = ZFS_CANMOUNT_OFF;
700 707
701 708 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
702 709 nomem();
703 710
704 711 /* check options */
705 712 while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
706 713 switch (c) {
707 714 case 'V':
708 715 type = ZFS_TYPE_VOLUME;
709 716 if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
710 717 (void) fprintf(stderr, gettext("bad volume "
711 718 "size '%s': %s\n"), optarg,
712 719 libzfs_error_description(g_zfs));
713 720 goto error;
714 721 }
715 722
716 723 if (nvlist_add_uint64(props,
717 724 zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
718 725 nomem();
719 726 volsize = intval;
720 727 break;
721 728 case 'p':
722 729 parents = B_TRUE;
723 730 break;
724 731 case 'b':
725 732 bflag = B_TRUE;
726 733 if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
727 734 (void) fprintf(stderr, gettext("bad volume "
728 735 "block size '%s': %s\n"), optarg,
729 736 libzfs_error_description(g_zfs));
730 737 goto error;
731 738 }
732 739
733 740 if (nvlist_add_uint64(props,
734 741 zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
735 742 intval) != 0)
736 743 nomem();
737 744 break;
738 745 case 'o':
739 746 if (parseprop(props))
740 747 goto error;
741 748 break;
742 749 case 's':
743 750 noreserve = B_TRUE;
744 751 break;
745 752 case ':':
746 753 (void) fprintf(stderr, gettext("missing size "
747 754 "argument\n"));
748 755 goto badusage;
749 756 case '?':
750 757 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
751 758 optopt);
752 759 goto badusage;
753 760 }
754 761 }
755 762
756 763 if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
757 764 (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
758 765 "used when creating a volume\n"));
759 766 goto badusage;
760 767 }
761 768
762 769 argc -= optind;
763 770 argv += optind;
764 771
765 772 /* check number of arguments */
766 773 if (argc == 0) {
767 774 (void) fprintf(stderr, gettext("missing %s argument\n"),
768 775 zfs_type_to_name(type));
769 776 goto badusage;
770 777 }
771 778 if (argc > 1) {
772 779 (void) fprintf(stderr, gettext("too many arguments\n"));
773 780 goto badusage;
774 781 }
775 782
776 783 if (type == ZFS_TYPE_VOLUME && !noreserve) {
777 784 zpool_handle_t *zpool_handle;
778 785 uint64_t spa_version;
779 786 char *p;
780 787 zfs_prop_t resv_prop;
781 788 char *strval;
782 789
783 790 if (p = strchr(argv[0], '/'))
784 791 *p = '\0';
785 792 zpool_handle = zpool_open(g_zfs, argv[0]);
786 793 if (p != NULL)
787 794 *p = '/';
788 795 if (zpool_handle == NULL)
789 796 goto error;
790 797 spa_version = zpool_get_prop_int(zpool_handle,
791 798 ZPOOL_PROP_VERSION, NULL);
792 799 zpool_close(zpool_handle);
793 800 if (spa_version >= SPA_VERSION_REFRESERVATION)
794 801 resv_prop = ZFS_PROP_REFRESERVATION;
795 802 else
796 803 resv_prop = ZFS_PROP_RESERVATION;
797 804 volsize = zvol_volsize_to_reservation(volsize, props);
798 805
799 806 if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
800 807 &strval) != 0) {
801 808 if (nvlist_add_uint64(props,
802 809 zfs_prop_to_name(resv_prop), volsize) != 0) {
803 810 nvlist_free(props);
804 811 nomem();
805 812 }
806 813 }
807 814 }
808 815
809 816 if (parents && zfs_name_valid(argv[0], type)) {
810 817 /*
811 818 * Now create the ancestors of target dataset. If the target
812 819 * already exists and '-p' option was used we should not
813 820 * complain.
814 821 */
815 822 if (zfs_dataset_exists(g_zfs, argv[0], type)) {
816 823 ret = 0;
817 824 goto error;
818 825 }
819 826 if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
820 827 goto error;
821 828 }
822 829
823 830 /* pass to libzfs */
824 831 if (zfs_create(g_zfs, argv[0], type, props) != 0)
825 832 goto error;
826 833
827 834 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
828 835 goto error;
829 836
830 837 ret = 0;
831 838 /*
832 839 * if the user doesn't want the dataset automatically mounted,
833 840 * then skip the mount/share step
834 841 */
835 842 if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type))
836 843 canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
837 844
838 845 /*
839 846 * Mount and/or share the new filesystem as appropriate. We provide a
840 847 * verbose error message to let the user know that their filesystem was
841 848 * in fact created, even if we failed to mount or share it.
842 849 */
843 850 if (canmount == ZFS_CANMOUNT_ON) {
844 851 if (zfs_mount(zhp, NULL, 0) != 0) {
845 852 (void) fprintf(stderr, gettext("filesystem "
846 853 "successfully created, but not mounted\n"));
847 854 ret = 1;
848 855 } else if (zfs_share(zhp) != 0) {
849 856 (void) fprintf(stderr, gettext("filesystem "
850 857 "successfully created, but not shared\n"));
851 858 ret = 1;
852 859 }
853 860 }
854 861
855 862 error:
856 863 if (zhp)
857 864 zfs_close(zhp);
858 865 nvlist_free(props);
859 866 return (ret);
860 867 badusage:
861 868 nvlist_free(props);
862 869 usage(B_FALSE);
863 870 return (2);
864 871 }
865 872
866 873 /*
867 874 * zfs destroy [-rRf] <fs, vol>
868 875 * zfs destroy [-rRd] <snap>
869 876 *
870 877 * -r Recursively destroy all children
871 878 * -R Recursively destroy all dependents, including clones
872 879 * -f Force unmounting of any dependents
873 880 * -d If we can't destroy now, mark for deferred destruction
874 881 *
875 882 * Destroys the given dataset. By default, it will unmount any filesystems,
876 883 * and refuse to destroy a dataset that has any dependents. A dependent can
877 884 * either be a child, or a clone of a child.
878 885 */
879 886 typedef struct destroy_cbdata {
880 887 boolean_t cb_first;
881 888 boolean_t cb_force;
882 889 boolean_t cb_recurse;
883 890 boolean_t cb_error;
884 891 boolean_t cb_doclones;
885 892 zfs_handle_t *cb_target;
886 893 boolean_t cb_defer_destroy;
887 894 boolean_t cb_verbose;
888 895 boolean_t cb_parsable;
889 896 boolean_t cb_dryrun;
890 897 nvlist_t *cb_nvl;
891 898
892 899 /* first snap in contiguous run */
893 900 char *cb_firstsnap;
894 901 /* previous snap in contiguous run */
895 902 char *cb_prevsnap;
896 903 int64_t cb_snapused;
897 904 char *cb_snapspec;
898 905 } destroy_cbdata_t;
899 906
900 907 /*
901 908 * Check for any dependents based on the '-r' or '-R' flags.
902 909 */
903 910 static int
904 911 destroy_check_dependent(zfs_handle_t *zhp, void *data)
905 912 {
906 913 destroy_cbdata_t *cbp = data;
907 914 const char *tname = zfs_get_name(cbp->cb_target);
908 915 const char *name = zfs_get_name(zhp);
909 916
910 917 if (strncmp(tname, name, strlen(tname)) == 0 &&
911 918 (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
912 919 /*
913 920 * This is a direct descendant, not a clone somewhere else in
914 921 * the hierarchy.
915 922 */
916 923 if (cbp->cb_recurse)
917 924 goto out;
918 925
919 926 if (cbp->cb_first) {
920 927 (void) fprintf(stderr, gettext("cannot destroy '%s': "
921 928 "%s has children\n"),
922 929 zfs_get_name(cbp->cb_target),
923 930 zfs_type_to_name(zfs_get_type(cbp->cb_target)));
924 931 (void) fprintf(stderr, gettext("use '-r' to destroy "
925 932 "the following datasets:\n"));
926 933 cbp->cb_first = B_FALSE;
927 934 cbp->cb_error = B_TRUE;
928 935 }
929 936
930 937 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
931 938 } else {
932 939 /*
933 940 * This is a clone. We only want to report this if the '-r'
934 941 * wasn't specified, or the target is a snapshot.
935 942 */
936 943 if (!cbp->cb_recurse &&
937 944 zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
938 945 goto out;
939 946
940 947 if (cbp->cb_first) {
941 948 (void) fprintf(stderr, gettext("cannot destroy '%s': "
942 949 "%s has dependent clones\n"),
943 950 zfs_get_name(cbp->cb_target),
944 951 zfs_type_to_name(zfs_get_type(cbp->cb_target)));
945 952 (void) fprintf(stderr, gettext("use '-R' to destroy "
946 953 "the following datasets:\n"));
947 954 cbp->cb_first = B_FALSE;
948 955 cbp->cb_error = B_TRUE;
949 956 cbp->cb_dryrun = B_TRUE;
950 957 }
951 958
952 959 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
953 960 }
954 961
955 962 out:
956 963 zfs_close(zhp);
957 964 return (0);
958 965 }
959 966
960 967 static int
961 968 destroy_callback(zfs_handle_t *zhp, void *data)
962 969 {
963 970 destroy_cbdata_t *cb = data;
964 971 const char *name = zfs_get_name(zhp);
965 972
966 973 if (cb->cb_verbose) {
967 974 if (cb->cb_parsable) {
968 975 (void) printf("destroy\t%s\n", name);
969 976 } else if (cb->cb_dryrun) {
970 977 (void) printf(gettext("would destroy %s\n"),
971 978 name);
972 979 } else {
973 980 (void) printf(gettext("will destroy %s\n"),
974 981 name);
975 982 }
976 983 }
977 984
978 985 /*
979 986 * Ignore pools (which we've already flagged as an error before getting
980 987 * here).
981 988 */
982 989 if (strchr(zfs_get_name(zhp), '/') == NULL &&
983 990 zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
984 991 zfs_close(zhp);
985 992 return (0);
986 993 }
987 994
988 995 if (!cb->cb_dryrun) {
989 996 if (zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
990 997 zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
991 998 zfs_close(zhp);
992 999 return (-1);
993 1000 }
994 1001 }
995 1002
996 1003 zfs_close(zhp);
997 1004 return (0);
998 1005 }
999 1006
1000 1007 static int
1001 1008 destroy_print_cb(zfs_handle_t *zhp, void *arg)
1002 1009 {
1003 1010 destroy_cbdata_t *cb = arg;
1004 1011 const char *name = zfs_get_name(zhp);
1005 1012 int err = 0;
1006 1013
1007 1014 if (nvlist_exists(cb->cb_nvl, name)) {
1008 1015 if (cb->cb_firstsnap == NULL)
1009 1016 cb->cb_firstsnap = strdup(name);
1010 1017 if (cb->cb_prevsnap != NULL)
1011 1018 free(cb->cb_prevsnap);
1012 1019 /* this snap continues the current range */
1013 1020 cb->cb_prevsnap = strdup(name);
1014 1021 if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
1015 1022 nomem();
1016 1023 if (cb->cb_verbose) {
1017 1024 if (cb->cb_parsable) {
1018 1025 (void) printf("destroy\t%s\n", name);
1019 1026 } else if (cb->cb_dryrun) {
1020 1027 (void) printf(gettext("would destroy %s\n"),
1021 1028 name);
1022 1029 } else {
1023 1030 (void) printf(gettext("will destroy %s\n"),
1024 1031 name);
1025 1032 }
1026 1033 }
1027 1034 } else if (cb->cb_firstsnap != NULL) {
1028 1035 /* end of this range */
1029 1036 uint64_t used = 0;
1030 1037 err = lzc_snaprange_space(cb->cb_firstsnap,
1031 1038 cb->cb_prevsnap, &used);
1032 1039 cb->cb_snapused += used;
1033 1040 free(cb->cb_firstsnap);
1034 1041 cb->cb_firstsnap = NULL;
1035 1042 free(cb->cb_prevsnap);
1036 1043 cb->cb_prevsnap = NULL;
1037 1044 }
1038 1045 zfs_close(zhp);
1039 1046 return (err);
1040 1047 }
1041 1048
1042 1049 static int
1043 1050 destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
1044 1051 {
1045 1052 int err = 0;
1046 1053 assert(cb->cb_firstsnap == NULL);
1047 1054 assert(cb->cb_prevsnap == NULL);
1048 1055 err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
1049 1056 if (cb->cb_firstsnap != NULL) {
1050 1057 uint64_t used = 0;
1051 1058 if (err == 0) {
1052 1059 err = lzc_snaprange_space(cb->cb_firstsnap,
1053 1060 cb->cb_prevsnap, &used);
1054 1061 }
1055 1062 cb->cb_snapused += used;
1056 1063 free(cb->cb_firstsnap);
1057 1064 cb->cb_firstsnap = NULL;
1058 1065 free(cb->cb_prevsnap);
1059 1066 cb->cb_prevsnap = NULL;
1060 1067 }
1061 1068 return (err);
1062 1069 }
1063 1070
1064 1071 static int
1065 1072 snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
1066 1073 {
1067 1074 destroy_cbdata_t *cb = arg;
1068 1075 int err = 0;
1069 1076
1070 1077 /* Check for clones. */
1071 1078 if (!cb->cb_doclones && !cb->cb_defer_destroy) {
1072 1079 cb->cb_target = zhp;
1073 1080 cb->cb_first = B_TRUE;
1074 1081 err = zfs_iter_dependents(zhp, B_TRUE,
1075 1082 destroy_check_dependent, cb);
1076 1083 }
1077 1084
1078 1085 if (err == 0) {
1079 1086 if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
1080 1087 nomem();
1081 1088 }
1082 1089 zfs_close(zhp);
1083 1090 return (err);
1084 1091 }
1085 1092
1086 1093 static int
1087 1094 gather_snapshots(zfs_handle_t *zhp, void *arg)
1088 1095 {
1089 1096 destroy_cbdata_t *cb = arg;
1090 1097 int err = 0;
1091 1098
1092 1099 err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
1093 1100 if (err == ENOENT)
1094 1101 err = 0;
1095 1102 if (err != 0)
1096 1103 goto out;
1097 1104
1098 1105 if (cb->cb_verbose) {
1099 1106 err = destroy_print_snapshots(zhp, cb);
1100 1107 if (err != 0)
1101 1108 goto out;
1102 1109 }
1103 1110
1104 1111 if (cb->cb_recurse)
1105 1112 err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
1106 1113
1107 1114 out:
1108 1115 zfs_close(zhp);
1109 1116 return (err);
1110 1117 }
1111 1118
1112 1119 static int
1113 1120 destroy_clones(destroy_cbdata_t *cb)
1114 1121 {
1115 1122 nvpair_t *pair;
1116 1123 for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
1117 1124 pair != NULL;
1118 1125 pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
1119 1126 zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
1120 1127 ZFS_TYPE_SNAPSHOT);
1121 1128 if (zhp != NULL) {
1122 1129 boolean_t defer = cb->cb_defer_destroy;
1123 1130 int err = 0;
1124 1131
1125 1132 /*
1126 1133 * We can't defer destroy non-snapshots, so set it to
1127 1134 * false while destroying the clones.
1128 1135 */
1129 1136 cb->cb_defer_destroy = B_FALSE;
1130 1137 err = zfs_iter_dependents(zhp, B_FALSE,
1131 1138 destroy_callback, cb);
1132 1139 cb->cb_defer_destroy = defer;
1133 1140 zfs_close(zhp);
1134 1141 if (err != 0)
1135 1142 return (err);
1136 1143 }
1137 1144 }
1138 1145 return (0);
1139 1146 }
1140 1147
1141 1148 static int
1142 1149 zfs_do_destroy(int argc, char **argv)
1143 1150 {
1144 1151 destroy_cbdata_t cb = { 0 };
1145 1152 int c;
1146 1153 zfs_handle_t *zhp;
1147 1154 char *at;
1148 1155 zfs_type_t type = ZFS_TYPE_DATASET;
1149 1156
1150 1157 /* check options */
1151 1158 while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
1152 1159 switch (c) {
1153 1160 case 'v':
1154 1161 cb.cb_verbose = B_TRUE;
1155 1162 break;
1156 1163 case 'p':
1157 1164 cb.cb_verbose = B_TRUE;
1158 1165 cb.cb_parsable = B_TRUE;
1159 1166 break;
1160 1167 case 'n':
1161 1168 cb.cb_dryrun = B_TRUE;
1162 1169 break;
1163 1170 case 'd':
1164 1171 cb.cb_defer_destroy = B_TRUE;
1165 1172 type = ZFS_TYPE_SNAPSHOT;
1166 1173 break;
1167 1174 case 'f':
1168 1175 cb.cb_force = B_TRUE;
1169 1176 break;
1170 1177 case 'r':
1171 1178 cb.cb_recurse = B_TRUE;
1172 1179 break;
1173 1180 case 'R':
1174 1181 cb.cb_recurse = B_TRUE;
1175 1182 cb.cb_doclones = B_TRUE;
1176 1183 break;
1177 1184 case '?':
1178 1185 default:
1179 1186 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1180 1187 optopt);
1181 1188 usage(B_FALSE);
1182 1189 }
1183 1190 }
1184 1191
1185 1192 argc -= optind;
1186 1193 argv += optind;
1187 1194
1188 1195 /* check number of arguments */
1189 1196 if (argc == 0) {
1190 1197 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1191 1198 usage(B_FALSE);
1192 1199 }
1193 1200 if (argc > 1) {
1194 1201 (void) fprintf(stderr, gettext("too many arguments\n"));
1195 1202 usage(B_FALSE);
1196 1203 }
1197 1204
1198 1205 at = strchr(argv[0], '@');
1199 1206 if (at != NULL) {
1200 1207 int err = 0;
1201 1208
1202 1209 /* Build the list of snaps to destroy in cb_nvl. */
1203 1210 if (nvlist_alloc(&cb.cb_nvl, NV_UNIQUE_NAME, 0) != 0)
1204 1211 nomem();
1205 1212
1206 1213 *at = '\0';
1207 1214 zhp = zfs_open(g_zfs, argv[0],
1208 1215 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
1209 1216 if (zhp == NULL)
1210 1217 return (1);
1211 1218
1212 1219 cb.cb_snapspec = at + 1;
1213 1220 if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
1214 1221 cb.cb_error) {
1215 1222 zfs_close(zhp);
1216 1223 nvlist_free(cb.cb_nvl);
1217 1224 return (1);
1218 1225 }
1219 1226
1220 1227 if (nvlist_empty(cb.cb_nvl)) {
1221 1228 (void) fprintf(stderr, gettext("could not find any "
1222 1229 "snapshots to destroy; check snapshot names.\n"));
1223 1230 zfs_close(zhp);
1224 1231 nvlist_free(cb.cb_nvl);
1225 1232 return (1);
1226 1233 }
1227 1234
1228 1235 if (cb.cb_verbose) {
1229 1236 char buf[16];
1230 1237 zfs_nicenum(cb.cb_snapused, buf, sizeof (buf));
1231 1238 if (cb.cb_parsable) {
1232 1239 (void) printf("reclaim\t%llu\n",
1233 1240 cb.cb_snapused);
1234 1241 } else if (cb.cb_dryrun) {
1235 1242 (void) printf(gettext("would reclaim %s\n"),
1236 1243 buf);
1237 1244 } else {
1238 1245 (void) printf(gettext("will reclaim %s\n"),
1239 1246 buf);
1240 1247 }
1241 1248 }
1242 1249
1243 1250 if (!cb.cb_dryrun) {
1244 1251 if (cb.cb_doclones)
1245 1252 err = destroy_clones(&cb);
1246 1253 if (err == 0) {
1247 1254 err = zfs_destroy_snaps_nvl(zhp, cb.cb_nvl,
1248 1255 cb.cb_defer_destroy);
1249 1256 }
1250 1257 }
1251 1258
1252 1259 zfs_close(zhp);
1253 1260 nvlist_free(cb.cb_nvl);
1254 1261 if (err != 0)
1255 1262 return (1);
1256 1263 } else {
1257 1264 /* Open the given dataset */
1258 1265 if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
1259 1266 return (1);
1260 1267
1261 1268 cb.cb_target = zhp;
1262 1269
1263 1270 /*
1264 1271 * Perform an explicit check for pools before going any further.
1265 1272 */
1266 1273 if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
1267 1274 zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1268 1275 (void) fprintf(stderr, gettext("cannot destroy '%s': "
1269 1276 "operation does not apply to pools\n"),
1270 1277 zfs_get_name(zhp));
1271 1278 (void) fprintf(stderr, gettext("use 'zfs destroy -r "
1272 1279 "%s' to destroy all datasets in the pool\n"),
1273 1280 zfs_get_name(zhp));
1274 1281 (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
1275 1282 "to destroy the pool itself\n"), zfs_get_name(zhp));
1276 1283 zfs_close(zhp);
1277 1284 return (1);
1278 1285 }
1279 1286
1280 1287 /*
1281 1288 * Check for any dependents and/or clones.
1282 1289 */
1283 1290 cb.cb_first = B_TRUE;
1284 1291 if (!cb.cb_doclones &&
1285 1292 zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
1286 1293 &cb) != 0) {
1287 1294 zfs_close(zhp);
1288 1295 return (1);
1289 1296 }
1290 1297
1291 1298 if (cb.cb_error) {
1292 1299 zfs_close(zhp);
1293 1300 return (1);
1294 1301 }
1295 1302
1296 1303 if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
1297 1304 &cb) != 0) {
1298 1305 zfs_close(zhp);
1299 1306 return (1);
1300 1307 }
1301 1308
1302 1309 /*
1303 1310 * Do the real thing. The callback will close the
1304 1311 * handle regardless of whether it succeeds or not.
1305 1312 */
1306 1313 if (destroy_callback(zhp, &cb) != 0)
1307 1314 return (1);
1308 1315 }
1309 1316
1310 1317 return (0);
1311 1318 }
1312 1319
1313 1320 static boolean_t
1314 1321 is_recvd_column(zprop_get_cbdata_t *cbp)
1315 1322 {
1316 1323 int i;
1317 1324 zfs_get_column_t col;
1318 1325
1319 1326 for (i = 0; i < ZFS_GET_NCOLS &&
1320 1327 (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
1321 1328 if (col == GET_COL_RECVD)
1322 1329 return (B_TRUE);
1323 1330 return (B_FALSE);
1324 1331 }
1325 1332
1326 1333 /*
1327 1334 * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
1328 1335 * < all | property[,property]... > < fs | snap | vol > ...
1329 1336 *
1330 1337 * -r recurse over any child datasets
1331 1338 * -H scripted mode. Headers are stripped, and fields are separated
1332 1339 * by tabs instead of spaces.
1333 1340 * -o Set of fields to display. One of "name,property,value,
1334 1341 * received,source". Default is "name,property,value,source".
1335 1342 * "all" is an alias for all five.
1336 1343 * -s Set of sources to allow. One of
1337 1344 * "local,default,inherited,received,temporary,none". Default is
1338 1345 * all six.
1339 1346 * -p Display values in parsable (literal) format.
1340 1347 *
1341 1348 * Prints properties for the given datasets. The user can control which
1342 1349 * columns to display as well as which property types to allow.
1343 1350 */
1344 1351
1345 1352 /*
1346 1353 * Invoked to display the properties for a single dataset.
1347 1354 */
1348 1355 static int
1349 1356 get_callback(zfs_handle_t *zhp, void *data)
1350 1357 {
1351 1358 char buf[ZFS_MAXPROPLEN];
1352 1359 char rbuf[ZFS_MAXPROPLEN];
1353 1360 zprop_source_t sourcetype;
1354 1361 char source[ZFS_MAXNAMELEN];
1355 1362 zprop_get_cbdata_t *cbp = data;
1356 1363 nvlist_t *user_props = zfs_get_user_props(zhp);
1357 1364 zprop_list_t *pl = cbp->cb_proplist;
1358 1365 nvlist_t *propval;
1359 1366 char *strval;
1360 1367 char *sourceval;
1361 1368 boolean_t received = is_recvd_column(cbp);
1362 1369
1363 1370 for (; pl != NULL; pl = pl->pl_next) {
1364 1371 char *recvdval = NULL;
1365 1372 /*
1366 1373 * Skip the special fake placeholder. This will also skip over
1367 1374 * the name property when 'all' is specified.
1368 1375 */
1369 1376 if (pl->pl_prop == ZFS_PROP_NAME &&
1370 1377 pl == cbp->cb_proplist)
1371 1378 continue;
1372 1379
1373 1380 if (pl->pl_prop != ZPROP_INVAL) {
1374 1381 if (zfs_prop_get(zhp, pl->pl_prop, buf,
1375 1382 sizeof (buf), &sourcetype, source,
1376 1383 sizeof (source),
1377 1384 cbp->cb_literal) != 0) {
1378 1385 if (pl->pl_all)
1379 1386 continue;
1380 1387 if (!zfs_prop_valid_for_type(pl->pl_prop,
1381 1388 ZFS_TYPE_DATASET)) {
1382 1389 (void) fprintf(stderr,
1383 1390 gettext("No such property '%s'\n"),
1384 1391 zfs_prop_to_name(pl->pl_prop));
1385 1392 continue;
1386 1393 }
1387 1394 sourcetype = ZPROP_SRC_NONE;
1388 1395 (void) strlcpy(buf, "-", sizeof (buf));
1389 1396 }
1390 1397
1391 1398 if (received && (zfs_prop_get_recvd(zhp,
1392 1399 zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
1393 1400 cbp->cb_literal) == 0))
1394 1401 recvdval = rbuf;
1395 1402
1396 1403 zprop_print_one_property(zfs_get_name(zhp), cbp,
1397 1404 zfs_prop_to_name(pl->pl_prop),
1398 1405 buf, sourcetype, source, recvdval);
1399 1406 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
1400 1407 sourcetype = ZPROP_SRC_LOCAL;
1401 1408
1402 1409 if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
1403 1410 buf, sizeof (buf), cbp->cb_literal) != 0) {
1404 1411 sourcetype = ZPROP_SRC_NONE;
1405 1412 (void) strlcpy(buf, "-", sizeof (buf));
1406 1413 }
1407 1414
1408 1415 zprop_print_one_property(zfs_get_name(zhp), cbp,
1409 1416 pl->pl_user_prop, buf, sourcetype, source, NULL);
1410 1417 } else if (zfs_prop_written(pl->pl_user_prop)) {
1411 1418 sourcetype = ZPROP_SRC_LOCAL;
1412 1419
1413 1420 if (zfs_prop_get_written(zhp, pl->pl_user_prop,
1414 1421 buf, sizeof (buf), cbp->cb_literal) != 0) {
1415 1422 sourcetype = ZPROP_SRC_NONE;
1416 1423 (void) strlcpy(buf, "-", sizeof (buf));
1417 1424 }
1418 1425
1419 1426 zprop_print_one_property(zfs_get_name(zhp), cbp,
1420 1427 pl->pl_user_prop, buf, sourcetype, source, NULL);
1421 1428 } else {
1422 1429 if (nvlist_lookup_nvlist(user_props,
1423 1430 pl->pl_user_prop, &propval) != 0) {
1424 1431 if (pl->pl_all)
1425 1432 continue;
1426 1433 sourcetype = ZPROP_SRC_NONE;
1427 1434 strval = "-";
1428 1435 } else {
1429 1436 verify(nvlist_lookup_string(propval,
1430 1437 ZPROP_VALUE, &strval) == 0);
1431 1438 verify(nvlist_lookup_string(propval,
1432 1439 ZPROP_SOURCE, &sourceval) == 0);
1433 1440
1434 1441 if (strcmp(sourceval,
1435 1442 zfs_get_name(zhp)) == 0) {
1436 1443 sourcetype = ZPROP_SRC_LOCAL;
1437 1444 } else if (strcmp(sourceval,
1438 1445 ZPROP_SOURCE_VAL_RECVD) == 0) {
1439 1446 sourcetype = ZPROP_SRC_RECEIVED;
1440 1447 } else {
1441 1448 sourcetype = ZPROP_SRC_INHERITED;
1442 1449 (void) strlcpy(source,
1443 1450 sourceval, sizeof (source));
1444 1451 }
1445 1452 }
1446 1453
1447 1454 if (received && (zfs_prop_get_recvd(zhp,
1448 1455 pl->pl_user_prop, rbuf, sizeof (rbuf),
1449 1456 cbp->cb_literal) == 0))
1450 1457 recvdval = rbuf;
1451 1458
1452 1459 zprop_print_one_property(zfs_get_name(zhp), cbp,
1453 1460 pl->pl_user_prop, strval, sourcetype,
1454 1461 source, recvdval);
1455 1462 }
1456 1463 }
1457 1464
1458 1465 return (0);
1459 1466 }
1460 1467
1461 1468 static int
1462 1469 zfs_do_get(int argc, char **argv)
1463 1470 {
1464 1471 zprop_get_cbdata_t cb = { 0 };
1465 1472 int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1466 1473 int types = ZFS_TYPE_DATASET;
1467 1474 char *value, *fields;
1468 1475 int ret = 0;
1469 1476 int limit = 0;
1470 1477 zprop_list_t fake_name = { 0 };
1471 1478
1472 1479 /*
1473 1480 * Set up default columns and sources.
1474 1481 */
1475 1482 cb.cb_sources = ZPROP_SRC_ALL;
1476 1483 cb.cb_columns[0] = GET_COL_NAME;
1477 1484 cb.cb_columns[1] = GET_COL_PROPERTY;
1478 1485 cb.cb_columns[2] = GET_COL_VALUE;
1479 1486 cb.cb_columns[3] = GET_COL_SOURCE;
1480 1487 cb.cb_type = ZFS_TYPE_DATASET;
1481 1488
1482 1489 /* check options */
1483 1490 while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
1484 1491 switch (c) {
1485 1492 case 'p':
1486 1493 cb.cb_literal = B_TRUE;
1487 1494 break;
1488 1495 case 'd':
1489 1496 limit = parse_depth(optarg, &flags);
1490 1497 break;
1491 1498 case 'r':
1492 1499 flags |= ZFS_ITER_RECURSE;
1493 1500 break;
1494 1501 case 'H':
1495 1502 cb.cb_scripted = B_TRUE;
1496 1503 break;
1497 1504 case ':':
1498 1505 (void) fprintf(stderr, gettext("missing argument for "
1499 1506 "'%c' option\n"), optopt);
1500 1507 usage(B_FALSE);
1501 1508 break;
1502 1509 case 'o':
1503 1510 /*
1504 1511 * Process the set of columns to display. We zero out
1505 1512 * the structure to give us a blank slate.
1506 1513 */
1507 1514 bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1508 1515 i = 0;
1509 1516 while (*optarg != '\0') {
1510 1517 static char *col_subopts[] =
1511 1518 { "name", "property", "value", "received",
1512 1519 "source", "all", NULL };
1513 1520
1514 1521 if (i == ZFS_GET_NCOLS) {
1515 1522 (void) fprintf(stderr, gettext("too "
1516 1523 "many fields given to -o "
1517 1524 "option\n"));
1518 1525 usage(B_FALSE);
1519 1526 }
1520 1527
1521 1528 switch (getsubopt(&optarg, col_subopts,
1522 1529 &value)) {
1523 1530 case 0:
1524 1531 cb.cb_columns[i++] = GET_COL_NAME;
1525 1532 break;
1526 1533 case 1:
1527 1534 cb.cb_columns[i++] = GET_COL_PROPERTY;
1528 1535 break;
1529 1536 case 2:
1530 1537 cb.cb_columns[i++] = GET_COL_VALUE;
1531 1538 break;
1532 1539 case 3:
1533 1540 cb.cb_columns[i++] = GET_COL_RECVD;
1534 1541 flags |= ZFS_ITER_RECVD_PROPS;
1535 1542 break;
1536 1543 case 4:
1537 1544 cb.cb_columns[i++] = GET_COL_SOURCE;
1538 1545 break;
1539 1546 case 5:
1540 1547 if (i > 0) {
1541 1548 (void) fprintf(stderr,
1542 1549 gettext("\"all\" conflicts "
1543 1550 "with specific fields "
1544 1551 "given to -o option\n"));
1545 1552 usage(B_FALSE);
1546 1553 }
1547 1554 cb.cb_columns[0] = GET_COL_NAME;
1548 1555 cb.cb_columns[1] = GET_COL_PROPERTY;
1549 1556 cb.cb_columns[2] = GET_COL_VALUE;
1550 1557 cb.cb_columns[3] = GET_COL_RECVD;
1551 1558 cb.cb_columns[4] = GET_COL_SOURCE;
1552 1559 flags |= ZFS_ITER_RECVD_PROPS;
1553 1560 i = ZFS_GET_NCOLS;
1554 1561 break;
1555 1562 default:
1556 1563 (void) fprintf(stderr,
1557 1564 gettext("invalid column name "
1558 1565 "'%s'\n"), value);
1559 1566 usage(B_FALSE);
1560 1567 }
1561 1568 }
1562 1569 break;
1563 1570
1564 1571 case 's':
1565 1572 cb.cb_sources = 0;
1566 1573 while (*optarg != '\0') {
1567 1574 static char *source_subopts[] = {
1568 1575 "local", "default", "inherited",
1569 1576 "received", "temporary", "none",
1570 1577 NULL };
1571 1578
1572 1579 switch (getsubopt(&optarg, source_subopts,
1573 1580 &value)) {
1574 1581 case 0:
1575 1582 cb.cb_sources |= ZPROP_SRC_LOCAL;
1576 1583 break;
1577 1584 case 1:
1578 1585 cb.cb_sources |= ZPROP_SRC_DEFAULT;
1579 1586 break;
1580 1587 case 2:
1581 1588 cb.cb_sources |= ZPROP_SRC_INHERITED;
1582 1589 break;
1583 1590 case 3:
1584 1591 cb.cb_sources |= ZPROP_SRC_RECEIVED;
1585 1592 break;
1586 1593 case 4:
1587 1594 cb.cb_sources |= ZPROP_SRC_TEMPORARY;
1588 1595 break;
1589 1596 case 5:
1590 1597 cb.cb_sources |= ZPROP_SRC_NONE;
1591 1598 break;
1592 1599 default:
1593 1600 (void) fprintf(stderr,
1594 1601 gettext("invalid source "
1595 1602 "'%s'\n"), value);
1596 1603 usage(B_FALSE);
1597 1604 }
1598 1605 }
1599 1606 break;
1600 1607
1601 1608 case 't':
1602 1609 types = 0;
1603 1610 flags &= ~ZFS_ITER_PROP_LISTSNAPS;
1604 1611 while (*optarg != '\0') {
1605 1612 static char *type_subopts[] = { "filesystem",
1606 1613 "volume", "snapshot", "all", NULL };
1607 1614
1608 1615 switch (getsubopt(&optarg, type_subopts,
1609 1616 &value)) {
1610 1617 case 0:
1611 1618 types |= ZFS_TYPE_FILESYSTEM;
1612 1619 break;
1613 1620 case 1:
1614 1621 types |= ZFS_TYPE_VOLUME;
1615 1622 break;
1616 1623 case 2:
1617 1624 types |= ZFS_TYPE_SNAPSHOT;
1618 1625 break;
1619 1626 case 3:
1620 1627 types = ZFS_TYPE_DATASET;
1621 1628 break;
1622 1629
1623 1630 default:
1624 1631 (void) fprintf(stderr,
1625 1632 gettext("invalid type '%s'\n"),
1626 1633 value);
1627 1634 usage(B_FALSE);
1628 1635 }
1629 1636 }
1630 1637 break;
1631 1638
1632 1639 case '?':
1633 1640 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1634 1641 optopt);
1635 1642 usage(B_FALSE);
1636 1643 }
1637 1644 }
1638 1645
1639 1646 argc -= optind;
1640 1647 argv += optind;
1641 1648
1642 1649 if (argc < 1) {
1643 1650 (void) fprintf(stderr, gettext("missing property "
1644 1651 "argument\n"));
1645 1652 usage(B_FALSE);
1646 1653 }
1647 1654
1648 1655 fields = argv[0];
1649 1656
1650 1657 if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
1651 1658 != 0)
1652 1659 usage(B_FALSE);
1653 1660
1654 1661 argc--;
1655 1662 argv++;
1656 1663
1657 1664 /*
1658 1665 * As part of zfs_expand_proplist(), we keep track of the maximum column
1659 1666 * width for each property. For the 'NAME' (and 'SOURCE') columns, we
1660 1667 * need to know the maximum name length. However, the user likely did
1661 1668 * not specify 'name' as one of the properties to fetch, so we need to
1662 1669 * make sure we always include at least this property for
1663 1670 * print_get_headers() to work properly.
1664 1671 */
1665 1672 if (cb.cb_proplist != NULL) {
1666 1673 fake_name.pl_prop = ZFS_PROP_NAME;
1667 1674 fake_name.pl_width = strlen(gettext("NAME"));
1668 1675 fake_name.pl_next = cb.cb_proplist;
1669 1676 cb.cb_proplist = &fake_name;
1670 1677 }
1671 1678
1672 1679 cb.cb_first = B_TRUE;
1673 1680
1674 1681 /* run for each object */
1675 1682 ret = zfs_for_each(argc, argv, flags, types, NULL,
1676 1683 &cb.cb_proplist, limit, get_callback, &cb);
1677 1684
1678 1685 if (cb.cb_proplist == &fake_name)
1679 1686 zprop_free_list(fake_name.pl_next);
1680 1687 else
1681 1688 zprop_free_list(cb.cb_proplist);
1682 1689
1683 1690 return (ret);
1684 1691 }
1685 1692
1686 1693 /*
1687 1694 * inherit [-rS] <property> <fs|vol> ...
1688 1695 *
1689 1696 * -r Recurse over all children
1690 1697 * -S Revert to received value, if any
1691 1698 *
1692 1699 * For each dataset specified on the command line, inherit the given property
1693 1700 * from its parent. Inheriting a property at the pool level will cause it to
1694 1701 * use the default value. The '-r' flag will recurse over all children, and is
1695 1702 * useful for setting a property on a hierarchy-wide basis, regardless of any
1696 1703 * local modifications for each dataset.
1697 1704 */
1698 1705
1699 1706 typedef struct inherit_cbdata {
1700 1707 const char *cb_propname;
1701 1708 boolean_t cb_received;
1702 1709 } inherit_cbdata_t;
1703 1710
1704 1711 static int
1705 1712 inherit_recurse_cb(zfs_handle_t *zhp, void *data)
1706 1713 {
1707 1714 inherit_cbdata_t *cb = data;
1708 1715 zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
1709 1716
1710 1717 /*
1711 1718 * If we're doing it recursively, then ignore properties that
1712 1719 * are not valid for this type of dataset.
1713 1720 */
1714 1721 if (prop != ZPROP_INVAL &&
1715 1722 !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
1716 1723 return (0);
1717 1724
1718 1725 return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1719 1726 }
1720 1727
1721 1728 static int
1722 1729 inherit_cb(zfs_handle_t *zhp, void *data)
1723 1730 {
1724 1731 inherit_cbdata_t *cb = data;
1725 1732
1726 1733 return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1727 1734 }
1728 1735
1729 1736 static int
1730 1737 zfs_do_inherit(int argc, char **argv)
1731 1738 {
1732 1739 int c;
1733 1740 zfs_prop_t prop;
1734 1741 inherit_cbdata_t cb = { 0 };
1735 1742 char *propname;
1736 1743 int ret = 0;
1737 1744 int flags = 0;
1738 1745 boolean_t received = B_FALSE;
1739 1746
1740 1747 /* check options */
1741 1748 while ((c = getopt(argc, argv, "rS")) != -1) {
1742 1749 switch (c) {
1743 1750 case 'r':
1744 1751 flags |= ZFS_ITER_RECURSE;
1745 1752 break;
1746 1753 case 'S':
1747 1754 received = B_TRUE;
1748 1755 break;
1749 1756 case '?':
1750 1757 default:
1751 1758 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1752 1759 optopt);
1753 1760 usage(B_FALSE);
1754 1761 }
1755 1762 }
1756 1763
1757 1764 argc -= optind;
1758 1765 argv += optind;
1759 1766
1760 1767 /* check number of arguments */
1761 1768 if (argc < 1) {
1762 1769 (void) fprintf(stderr, gettext("missing property argument\n"));
1763 1770 usage(B_FALSE);
1764 1771 }
1765 1772 if (argc < 2) {
1766 1773 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1767 1774 usage(B_FALSE);
1768 1775 }
1769 1776
1770 1777 propname = argv[0];
1771 1778 argc--;
1772 1779 argv++;
1773 1780
1774 1781 if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
1775 1782 if (zfs_prop_readonly(prop)) {
1776 1783 (void) fprintf(stderr, gettext(
1777 1784 "%s property is read-only\n"),
1778 1785 propname);
1779 1786 return (1);
1780 1787 }
1781 1788 if (!zfs_prop_inheritable(prop) && !received) {
1782 1789 (void) fprintf(stderr, gettext("'%s' property cannot "
1783 1790 "be inherited\n"), propname);
1784 1791 if (prop == ZFS_PROP_QUOTA ||
1785 1792 prop == ZFS_PROP_RESERVATION ||
1786 1793 prop == ZFS_PROP_REFQUOTA ||
1787 1794 prop == ZFS_PROP_REFRESERVATION)
1788 1795 (void) fprintf(stderr, gettext("use 'zfs set "
1789 1796 "%s=none' to clear\n"), propname);
1790 1797 return (1);
1791 1798 }
1792 1799 if (received && (prop == ZFS_PROP_VOLSIZE ||
1793 1800 prop == ZFS_PROP_VERSION)) {
1794 1801 (void) fprintf(stderr, gettext("'%s' property cannot "
1795 1802 "be reverted to a received value\n"), propname);
1796 1803 return (1);
1797 1804 }
1798 1805 } else if (!zfs_prop_user(propname)) {
1799 1806 (void) fprintf(stderr, gettext("invalid property '%s'\n"),
1800 1807 propname);
1801 1808 usage(B_FALSE);
1802 1809 }
1803 1810
1804 1811 cb.cb_propname = propname;
1805 1812 cb.cb_received = received;
1806 1813
1807 1814 if (flags & ZFS_ITER_RECURSE) {
1808 1815 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1809 1816 NULL, NULL, 0, inherit_recurse_cb, &cb);
1810 1817 } else {
1811 1818 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1812 1819 NULL, NULL, 0, inherit_cb, &cb);
1813 1820 }
1814 1821
1815 1822 return (ret);
1816 1823 }
1817 1824
1818 1825 typedef struct upgrade_cbdata {
1819 1826 uint64_t cb_numupgraded;
1820 1827 uint64_t cb_numsamegraded;
1821 1828 uint64_t cb_numfailed;
1822 1829 uint64_t cb_version;
1823 1830 boolean_t cb_newer;
1824 1831 boolean_t cb_foundone;
1825 1832 char cb_lastfs[ZFS_MAXNAMELEN];
1826 1833 } upgrade_cbdata_t;
1827 1834
1828 1835 static int
1829 1836 same_pool(zfs_handle_t *zhp, const char *name)
1830 1837 {
1831 1838 int len1 = strcspn(name, "/@");
1832 1839 const char *zhname = zfs_get_name(zhp);
1833 1840 int len2 = strcspn(zhname, "/@");
1834 1841
1835 1842 if (len1 != len2)
1836 1843 return (B_FALSE);
1837 1844 return (strncmp(name, zhname, len1) == 0);
1838 1845 }
1839 1846
1840 1847 static int
1841 1848 upgrade_list_callback(zfs_handle_t *zhp, void *data)
1842 1849 {
1843 1850 upgrade_cbdata_t *cb = data;
1844 1851 int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1845 1852
1846 1853 /* list if it's old/new */
1847 1854 if ((!cb->cb_newer && version < ZPL_VERSION) ||
1848 1855 (cb->cb_newer && version > ZPL_VERSION)) {
1849 1856 char *str;
1850 1857 if (cb->cb_newer) {
1851 1858 str = gettext("The following filesystems are "
1852 1859 "formatted using a newer software version and\n"
1853 1860 "cannot be accessed on the current system.\n\n");
1854 1861 } else {
1855 1862 str = gettext("The following filesystems are "
1856 1863 "out of date, and can be upgraded. After being\n"
1857 1864 "upgraded, these filesystems (and any 'zfs send' "
1858 1865 "streams generated from\n"
1859 1866 "subsequent snapshots) will no longer be "
1860 1867 "accessible by older software versions.\n\n");
1861 1868 }
1862 1869
1863 1870 if (!cb->cb_foundone) {
1864 1871 (void) puts(str);
1865 1872 (void) printf(gettext("VER FILESYSTEM\n"));
1866 1873 (void) printf(gettext("--- ------------\n"));
1867 1874 cb->cb_foundone = B_TRUE;
1868 1875 }
1869 1876
1870 1877 (void) printf("%2u %s\n", version, zfs_get_name(zhp));
1871 1878 }
1872 1879
1873 1880 return (0);
1874 1881 }
1875 1882
1876 1883 static int
1877 1884 upgrade_set_callback(zfs_handle_t *zhp, void *data)
1878 1885 {
1879 1886 upgrade_cbdata_t *cb = data;
1880 1887 int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1881 1888 int needed_spa_version;
1882 1889 int spa_version;
1883 1890
1884 1891 if (zfs_spa_version(zhp, &spa_version) < 0)
1885 1892 return (-1);
1886 1893
1887 1894 needed_spa_version = zfs_spa_version_map(cb->cb_version);
1888 1895
1889 1896 if (needed_spa_version < 0)
1890 1897 return (-1);
1891 1898
1892 1899 if (spa_version < needed_spa_version) {
1893 1900 /* can't upgrade */
1894 1901 (void) printf(gettext("%s: can not be "
1895 1902 "upgraded; the pool version needs to first "
1896 1903 "be upgraded\nto version %d\n\n"),
1897 1904 zfs_get_name(zhp), needed_spa_version);
1898 1905 cb->cb_numfailed++;
1899 1906 return (0);
1900 1907 }
1901 1908
1902 1909 /* upgrade */
1903 1910 if (version < cb->cb_version) {
1904 1911 char verstr[16];
1905 1912 (void) snprintf(verstr, sizeof (verstr),
1906 1913 "%llu", cb->cb_version);
1907 1914 if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
1908 1915 /*
1909 1916 * If they did "zfs upgrade -a", then we could
1910 1917 * be doing ioctls to different pools. We need
1911 1918 * to log this history once to each pool, and bypass
1912 1919 * the normal history logging that happens in main().
1913 1920 */
1914 1921 (void) zpool_log_history(g_zfs, history_str);
1915 1922 log_history = B_FALSE;
1916 1923 }
1917 1924 if (zfs_prop_set(zhp, "version", verstr) == 0)
1918 1925 cb->cb_numupgraded++;
1919 1926 else
1920 1927 cb->cb_numfailed++;
1921 1928 (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
1922 1929 } else if (version > cb->cb_version) {
1923 1930 /* can't downgrade */
1924 1931 (void) printf(gettext("%s: can not be downgraded; "
1925 1932 "it is already at version %u\n"),
1926 1933 zfs_get_name(zhp), version);
1927 1934 cb->cb_numfailed++;
1928 1935 } else {
1929 1936 cb->cb_numsamegraded++;
1930 1937 }
1931 1938 return (0);
1932 1939 }
1933 1940
1934 1941 /*
1935 1942 * zfs upgrade
1936 1943 * zfs upgrade -v
1937 1944 * zfs upgrade [-r] [-V <version>] <-a | filesystem>
1938 1945 */
1939 1946 static int
1940 1947 zfs_do_upgrade(int argc, char **argv)
1941 1948 {
1942 1949 boolean_t all = B_FALSE;
1943 1950 boolean_t showversions = B_FALSE;
1944 1951 int ret = 0;
1945 1952 upgrade_cbdata_t cb = { 0 };
1946 1953 char c;
1947 1954 int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1948 1955
1949 1956 /* check options */
1950 1957 while ((c = getopt(argc, argv, "rvV:a")) != -1) {
1951 1958 switch (c) {
1952 1959 case 'r':
1953 1960 flags |= ZFS_ITER_RECURSE;
1954 1961 break;
1955 1962 case 'v':
1956 1963 showversions = B_TRUE;
1957 1964 break;
1958 1965 case 'V':
1959 1966 if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
1960 1967 optarg, &cb.cb_version) != 0) {
1961 1968 (void) fprintf(stderr,
1962 1969 gettext("invalid version %s\n"), optarg);
1963 1970 usage(B_FALSE);
1964 1971 }
1965 1972 break;
1966 1973 case 'a':
1967 1974 all = B_TRUE;
1968 1975 break;
1969 1976 case '?':
1970 1977 default:
1971 1978 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1972 1979 optopt);
1973 1980 usage(B_FALSE);
1974 1981 }
1975 1982 }
1976 1983
1977 1984 argc -= optind;
1978 1985 argv += optind;
1979 1986
1980 1987 if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
1981 1988 usage(B_FALSE);
1982 1989 if (showversions && (flags & ZFS_ITER_RECURSE || all ||
1983 1990 cb.cb_version || argc))
1984 1991 usage(B_FALSE);
1985 1992 if ((all || argc) && (showversions))
1986 1993 usage(B_FALSE);
1987 1994 if (all && argc)
1988 1995 usage(B_FALSE);
1989 1996
1990 1997 if (showversions) {
1991 1998 /* Show info on available versions. */
1992 1999 (void) printf(gettext("The following filesystem versions are "
1993 2000 "supported:\n\n"));
1994 2001 (void) printf(gettext("VER DESCRIPTION\n"));
1995 2002 (void) printf("--- -----------------------------------------"
1996 2003 "---------------\n");
1997 2004 (void) printf(gettext(" 1 Initial ZFS filesystem version\n"));
1998 2005 (void) printf(gettext(" 2 Enhanced directory entries\n"));
1999 2006 (void) printf(gettext(" 3 Case insensitive and filesystem "
2000 2007 "user identifier (FUID)\n"));
2001 2008 (void) printf(gettext(" 4 userquota, groupquota "
2002 2009 "properties\n"));
2003 2010 (void) printf(gettext(" 5 System attributes\n"));
2004 2011 (void) printf(gettext("\nFor more information on a particular "
2005 2012 "version, including supported releases,\n"));
2006 2013 (void) printf("see the ZFS Administration Guide.\n\n");
2007 2014 ret = 0;
2008 2015 } else if (argc || all) {
2009 2016 /* Upgrade filesystems */
2010 2017 if (cb.cb_version == 0)
2011 2018 cb.cb_version = ZPL_VERSION;
2012 2019 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
2013 2020 NULL, NULL, 0, upgrade_set_callback, &cb);
2014 2021 (void) printf(gettext("%llu filesystems upgraded\n"),
2015 2022 cb.cb_numupgraded);
2016 2023 if (cb.cb_numsamegraded) {
2017 2024 (void) printf(gettext("%llu filesystems already at "
2018 2025 "this version\n"),
2019 2026 cb.cb_numsamegraded);
2020 2027 }
2021 2028 if (cb.cb_numfailed != 0)
2022 2029 ret = 1;
2023 2030 } else {
2024 2031 /* List old-version filesytems */
2025 2032 boolean_t found;
2026 2033 (void) printf(gettext("This system is currently running "
2027 2034 "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
2028 2035
2029 2036 flags |= ZFS_ITER_RECURSE;
2030 2037 ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2031 2038 NULL, NULL, 0, upgrade_list_callback, &cb);
2032 2039
2033 2040 found = cb.cb_foundone;
2034 2041 cb.cb_foundone = B_FALSE;
2035 2042 cb.cb_newer = B_TRUE;
2036 2043
2037 2044 ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2038 2045 NULL, NULL, 0, upgrade_list_callback, &cb);
2039 2046
2040 2047 if (!cb.cb_foundone && !found) {
2041 2048 (void) printf(gettext("All filesystems are "
2042 2049 "formatted with the current version.\n"));
2043 2050 }
2044 2051 }
2045 2052
2046 2053 return (ret);
2047 2054 }
2048 2055
2049 2056 /*
2050 2057 * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2051 2058 * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2052 2059 * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2053 2060 * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2054 2061 *
2055 2062 * -H Scripted mode; elide headers and separate columns by tabs.
2056 2063 * -i Translate SID to POSIX ID.
2057 2064 * -n Print numeric ID instead of user/group name.
2058 2065 * -o Control which fields to display.
2059 2066 * -p Use exact (parseable) numeric output.
2060 2067 * -s Specify sort columns, descending order.
2061 2068 * -S Specify sort columns, ascending order.
2062 2069 * -t Control which object types to display.
2063 2070 *
2064 2071 * Displays space consumed by, and quotas on, each user in the specified
2065 2072 * filesystem or snapshot.
2066 2073 */
2067 2074
2068 2075 /* us_field_types, us_field_hdr and us_field_names should be kept in sync */
2069 2076 enum us_field_types {
2070 2077 USFIELD_TYPE,
2071 2078 USFIELD_NAME,
2072 2079 USFIELD_USED,
2073 2080 USFIELD_QUOTA
2074 2081 };
2075 2082 static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
2076 2083 static char *us_field_names[] = { "type", "name", "used", "quota" };
2077 2084 #define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))
2078 2085
2079 2086 #define USTYPE_PSX_GRP (1 << 0)
2080 2087 #define USTYPE_PSX_USR (1 << 1)
2081 2088 #define USTYPE_SMB_GRP (1 << 2)
2082 2089 #define USTYPE_SMB_USR (1 << 3)
2083 2090 #define USTYPE_ALL \
2084 2091 (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR)
2085 2092
2086 2093 static int us_type_bits[] = {
2087 2094 USTYPE_PSX_GRP,
2088 2095 USTYPE_PSX_USR,
2089 2096 USTYPE_SMB_GRP,
2090 2097 USTYPE_SMB_USR,
2091 2098 USTYPE_ALL
2092 2099 };
2093 2100 static char *us_type_names[] = { "posixgroup", "posxiuser", "smbgroup",
2094 2101 "smbuser", "all" };
2095 2102
2096 2103 typedef struct us_node {
2097 2104 nvlist_t *usn_nvl;
2098 2105 uu_avl_node_t usn_avlnode;
2099 2106 uu_list_node_t usn_listnode;
2100 2107 } us_node_t;
2101 2108
2102 2109 typedef struct us_cbdata {
2103 2110 nvlist_t **cb_nvlp;
2104 2111 uu_avl_pool_t *cb_avl_pool;
2105 2112 uu_avl_t *cb_avl;
2106 2113 boolean_t cb_numname;
2107 2114 boolean_t cb_nicenum;
2108 2115 boolean_t cb_sid2posix;
2109 2116 zfs_userquota_prop_t cb_prop;
2110 2117 zfs_sort_column_t *cb_sortcol;
2111 2118 size_t cb_width[USFIELD_LAST];
2112 2119 } us_cbdata_t;
2113 2120
2114 2121 static boolean_t us_populated = B_FALSE;
2115 2122
2116 2123 typedef struct {
2117 2124 zfs_sort_column_t *si_sortcol;
2118 2125 boolean_t si_numname;
2119 2126 } us_sort_info_t;
2120 2127
2121 2128 static int
2122 2129 us_field_index(char *field)
2123 2130 {
2124 2131 int i;
2125 2132
2126 2133 for (i = 0; i < USFIELD_LAST; i++) {
2127 2134 if (strcmp(field, us_field_names[i]) == 0)
2128 2135 return (i);
2129 2136 }
2130 2137
2131 2138 return (-1);
2132 2139 }
2133 2140
2134 2141 static int
2135 2142 us_compare(const void *larg, const void *rarg, void *unused)
2136 2143 {
2137 2144 const us_node_t *l = larg;
2138 2145 const us_node_t *r = rarg;
2139 2146 us_sort_info_t *si = (us_sort_info_t *)unused;
2140 2147 zfs_sort_column_t *sortcol = si->si_sortcol;
2141 2148 boolean_t numname = si->si_numname;
2142 2149 nvlist_t *lnvl = l->usn_nvl;
2143 2150 nvlist_t *rnvl = r->usn_nvl;
2144 2151 int rc = 0;
2145 2152 boolean_t lvb, rvb;
2146 2153
2147 2154 for (; sortcol != NULL; sortcol = sortcol->sc_next) {
2148 2155 char *lvstr = "";
2149 2156 char *rvstr = "";
2150 2157 uint32_t lv32 = 0;
2151 2158 uint32_t rv32 = 0;
2152 2159 uint64_t lv64 = 0;
2153 2160 uint64_t rv64 = 0;
2154 2161 zfs_prop_t prop = sortcol->sc_prop;
2155 2162 const char *propname = NULL;
2156 2163 boolean_t reverse = sortcol->sc_reverse;
2157 2164
2158 2165 switch (prop) {
2159 2166 case ZFS_PROP_TYPE:
2160 2167 propname = "type";
2161 2168 (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
2162 2169 (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
2163 2170 if (rv32 != lv32)
2164 2171 rc = (rv32 < lv32) ? 1 : -1;
2165 2172 break;
2166 2173 case ZFS_PROP_NAME:
2167 2174 propname = "name";
2168 2175 if (numname) {
2169 2176 (void) nvlist_lookup_uint64(lnvl, propname,
2170 2177 &lv64);
2171 2178 (void) nvlist_lookup_uint64(rnvl, propname,
2172 2179 &rv64);
2173 2180 if (rv64 != lv64)
2174 2181 rc = (rv64 < lv64) ? 1 : -1;
2175 2182 } else {
2176 2183 (void) nvlist_lookup_string(lnvl, propname,
2177 2184 &lvstr);
2178 2185 (void) nvlist_lookup_string(rnvl, propname,
2179 2186 &rvstr);
2180 2187 rc = strcmp(lvstr, rvstr);
2181 2188 }
2182 2189 break;
2183 2190 case ZFS_PROP_USED:
2184 2191 case ZFS_PROP_QUOTA:
2185 2192 if (!us_populated)
2186 2193 break;
2187 2194 if (prop == ZFS_PROP_USED)
2188 2195 propname = "used";
2189 2196 else
2190 2197 propname = "quota";
2191 2198 (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
2192 2199 (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
2193 2200 if (rv64 != lv64)
2194 2201 rc = (rv64 < lv64) ? 1 : -1;
2195 2202 break;
2196 2203 }
2197 2204
2198 2205 if (rc != 0) {
2199 2206 if (rc < 0)
2200 2207 return (reverse ? 1 : -1);
2201 2208 else
2202 2209 return (reverse ? -1 : 1);
2203 2210 }
2204 2211 }
2205 2212
2206 2213 /*
2207 2214 * If entries still seem to be the same, check if they are of the same
2208 2215 * type (smbentity is added only if we are doing SID to POSIX ID
2209 2216 * translation where we can have duplicate type/name combinations).
2210 2217 */
2211 2218 if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
2212 2219 nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
2213 2220 lvb != rvb)
2214 2221 return (lvb < rvb ? -1 : 1);
2215 2222
2216 2223 return (0);
2217 2224 }
2218 2225
2219 2226 static inline const char *
2220 2227 us_type2str(unsigned field_type)
2221 2228 {
2222 2229 switch (field_type) {
2223 2230 case USTYPE_PSX_USR:
2224 2231 return ("POSIX User");
2225 2232 case USTYPE_PSX_GRP:
2226 2233 return ("POSIX Group");
2227 2234 case USTYPE_SMB_USR:
2228 2235 return ("SMB User");
2229 2236 case USTYPE_SMB_GRP:
2230 2237 return ("SMB Group");
2231 2238 default:
2232 2239 return ("Undefined");
2233 2240 }
2234 2241 }
2235 2242
2236 2243 static int
2237 2244 userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
2238 2245 {
2239 2246 us_cbdata_t *cb = (us_cbdata_t *)arg;
2240 2247 zfs_userquota_prop_t prop = cb->cb_prop;
2241 2248 char *name = NULL;
2242 2249 char *propname;
2243 2250 char sizebuf[32];
2244 2251 us_node_t *node;
2245 2252 uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
2246 2253 uu_avl_t *avl = cb->cb_avl;
2247 2254 uu_avl_index_t idx;
2248 2255 nvlist_t *props;
2249 2256 us_node_t *n;
2250 2257 zfs_sort_column_t *sortcol = cb->cb_sortcol;
2251 2258 unsigned type;
2252 2259 const char *typestr;
2253 2260 size_t namelen;
2254 2261 size_t typelen;
2255 2262 size_t sizelen;
2256 2263 int typeidx, nameidx, sizeidx;
2257 2264 us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
2258 2265 boolean_t smbentity = B_FALSE;
2259 2266
2260 2267 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
2261 2268 nomem();
2262 2269 node = safe_malloc(sizeof (us_node_t));
2263 2270 uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
2264 2271 node->usn_nvl = props;
2265 2272
2266 2273 if (domain != NULL && domain[0] != '\0') {
2267 2274 /* SMB */
2268 2275 char sid[ZFS_MAXNAMELEN + 32];
2269 2276 uid_t id;
2270 2277 uint64_t classes;
2271 2278 int err;
2272 2279 directory_error_t e;
2273 2280
2274 2281 smbentity = B_TRUE;
2275 2282
2276 2283 (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
2277 2284
2278 2285 if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2279 2286 type = USTYPE_SMB_GRP;
2280 2287 err = sid_to_id(sid, B_FALSE, &id);
2281 2288 } else {
2282 2289 type = USTYPE_SMB_USR;
2283 2290 err = sid_to_id(sid, B_TRUE, &id);
2284 2291 }
2285 2292
2286 2293 if (err == 0) {
2287 2294 rid = id;
2288 2295 if (!cb->cb_sid2posix) {
2289 2296 e = directory_name_from_sid(NULL, sid, &name,
2290 2297 &classes);
2291 2298 if (e != NULL)
2292 2299 directory_error_free(e);
2293 2300 if (name == NULL)
2294 2301 name = sid;
2295 2302 }
2296 2303 }
2297 2304 }
2298 2305
2299 2306 if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
2300 2307 /* POSIX or -i */
2301 2308 if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2302 2309 type = USTYPE_PSX_GRP;
2303 2310 if (!cb->cb_numname) {
2304 2311 struct group *g;
2305 2312
2306 2313 if ((g = getgrgid(rid)) != NULL)
2307 2314 name = g->gr_name;
2308 2315 }
2309 2316 } else {
2310 2317 type = USTYPE_PSX_USR;
2311 2318 if (!cb->cb_numname) {
2312 2319 struct passwd *p;
2313 2320
2314 2321 if ((p = getpwuid(rid)) != NULL)
2315 2322 name = p->pw_name;
2316 2323 }
2317 2324 }
2318 2325 }
2319 2326
2320 2327 /*
2321 2328 * Make sure that the type/name combination is unique when doing
2322 2329 * SID to POSIX ID translation (hence changing the type from SMB to
2323 2330 * POSIX).
2324 2331 */
2325 2332 if (cb->cb_sid2posix &&
2326 2333 nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
2327 2334 nomem();
2328 2335
2329 2336 /* Calculate/update width of TYPE field */
2330 2337 typestr = us_type2str(type);
2331 2338 typelen = strlen(gettext(typestr));
2332 2339 typeidx = us_field_index("type");
2333 2340 if (typelen > cb->cb_width[typeidx])
2334 2341 cb->cb_width[typeidx] = typelen;
2335 2342 if (nvlist_add_uint32(props, "type", type) != 0)
2336 2343 nomem();
2337 2344
2338 2345 /* Calculate/update width of NAME field */
2339 2346 if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
2340 2347 if (nvlist_add_uint64(props, "name", rid) != 0)
2341 2348 nomem();
2342 2349 namelen = snprintf(NULL, 0, "%u", rid);
2343 2350 } else {
2344 2351 if (nvlist_add_string(props, "name", name) != 0)
2345 2352 nomem();
2346 2353 namelen = strlen(name);
2347 2354 }
2348 2355 nameidx = us_field_index("name");
2349 2356 if (namelen > cb->cb_width[nameidx])
2350 2357 cb->cb_width[nameidx] = namelen;
2351 2358
2352 2359 /*
2353 2360 * Check if this type/name combination is in the list and update it;
2354 2361 * otherwise add new node to the list.
2355 2362 */
2356 2363 if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
2357 2364 uu_avl_insert(avl, node, idx);
2358 2365 } else {
2359 2366 nvlist_free(props);
2360 2367 free(node);
2361 2368 node = n;
2362 2369 props = node->usn_nvl;
2363 2370 }
2364 2371
2365 2372 /* Calculate/update width of USED/QUOTA fields */
2366 2373 if (cb->cb_nicenum)
2367 2374 zfs_nicenum(space, sizebuf, sizeof (sizebuf));
2368 2375 else
2369 2376 (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
2370 2377 sizelen = strlen(sizebuf);
2371 2378 if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) {
2372 2379 propname = "used";
2373 2380 if (!nvlist_exists(props, "quota"))
2374 2381 (void) nvlist_add_uint64(props, "quota", 0);
2375 2382 } else {
2376 2383 propname = "quota";
2377 2384 if (!nvlist_exists(props, "used"))
2378 2385 (void) nvlist_add_uint64(props, "used", 0);
2379 2386 }
2380 2387 sizeidx = us_field_index(propname);
2381 2388 if (sizelen > cb->cb_width[sizeidx])
2382 2389 cb->cb_width[sizeidx] = sizelen;
2383 2390
2384 2391 if (nvlist_add_uint64(props, propname, space) != 0)
2385 2392 nomem();
2386 2393
2387 2394 return (0);
2388 2395 }
2389 2396
2390 2397 static void
2391 2398 print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
2392 2399 size_t *width, us_node_t *node)
2393 2400 {
2394 2401 nvlist_t *nvl = node->usn_nvl;
2395 2402 char valstr[ZFS_MAXNAMELEN];
2396 2403 boolean_t first = B_TRUE;
2397 2404 int cfield = 0;
2398 2405 int field;
2399 2406 uint32_t ustype;
2400 2407
2401 2408 /* Check type */
2402 2409 (void) nvlist_lookup_uint32(nvl, "type", &ustype);
2403 2410 if (!(ustype & types))
2404 2411 return;
2405 2412
2406 2413 while ((field = fields[cfield]) != USFIELD_LAST) {
2407 2414 nvpair_t *nvp = NULL;
2408 2415 data_type_t type;
2409 2416 uint32_t val32;
2410 2417 uint64_t val64;
2411 2418 char *strval = NULL;
2412 2419
2413 2420 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
2414 2421 if (strcmp(nvpair_name(nvp),
2415 2422 us_field_names[field]) == 0)
2416 2423 break;
2417 2424 }
2418 2425
2419 2426 type = nvpair_type(nvp);
2420 2427 switch (type) {
2421 2428 case DATA_TYPE_UINT32:
2422 2429 (void) nvpair_value_uint32(nvp, &val32);
2423 2430 break;
2424 2431 case DATA_TYPE_UINT64:
2425 2432 (void) nvpair_value_uint64(nvp, &val64);
2426 2433 break;
2427 2434 case DATA_TYPE_STRING:
2428 2435 (void) nvpair_value_string(nvp, &strval);
2429 2436 break;
2430 2437 default:
2431 2438 (void) fprintf(stderr, "invalid data type\n");
2432 2439 }
2433 2440
2434 2441 switch (field) {
2435 2442 case USFIELD_TYPE:
2436 2443 strval = (char *)us_type2str(val32);
2437 2444 break;
2438 2445 case USFIELD_NAME:
2439 2446 if (type == DATA_TYPE_UINT64) {
2440 2447 (void) sprintf(valstr, "%llu", val64);
2441 2448 strval = valstr;
2442 2449 }
2443 2450 break;
2444 2451 case USFIELD_USED:
2445 2452 case USFIELD_QUOTA:
2446 2453 if (type == DATA_TYPE_UINT64) {
2447 2454 if (parsable) {
2448 2455 (void) sprintf(valstr, "%llu", val64);
2449 2456 } else {
2450 2457 zfs_nicenum(val64, valstr,
2451 2458 sizeof (valstr));
2452 2459 }
2453 2460 if (field == USFIELD_QUOTA &&
2454 2461 strcmp(valstr, "0") == 0)
2455 2462 strval = "none";
2456 2463 else
2457 2464 strval = valstr;
2458 2465 }
2459 2466 break;
2460 2467 }
2461 2468
2462 2469 if (!first) {
2463 2470 if (scripted)
2464 2471 (void) printf("\t");
2465 2472 else
2466 2473 (void) printf(" ");
2467 2474 }
2468 2475 if (scripted)
2469 2476 (void) printf("%s", strval);
2470 2477 else if (field == USFIELD_TYPE || field == USFIELD_NAME)
2471 2478 (void) printf("%-*s", width[field], strval);
2472 2479 else
2473 2480 (void) printf("%*s", width[field], strval);
2474 2481
2475 2482 first = B_FALSE;
2476 2483 cfield++;
2477 2484 }
2478 2485
2479 2486 (void) printf("\n");
2480 2487 }
2481 2488
2482 2489 static void
2483 2490 print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
2484 2491 size_t *width, boolean_t rmnode, uu_avl_t *avl)
2485 2492 {
2486 2493 us_node_t *node;
2487 2494 const char *col;
2488 2495 int cfield = 0;
2489 2496 int field;
2490 2497
2491 2498 if (!scripted) {
2492 2499 boolean_t first = B_TRUE;
2493 2500
2494 2501 while ((field = fields[cfield]) != USFIELD_LAST) {
2495 2502 col = gettext(us_field_hdr[field]);
2496 2503 if (field == USFIELD_TYPE || field == USFIELD_NAME) {
2497 2504 (void) printf(first ? "%-*s" : " %-*s",
2498 2505 width[field], col);
2499 2506 } else {
2500 2507 (void) printf(first ? "%*s" : " %*s",
2501 2508 width[field], col);
2502 2509 }
2503 2510 first = B_FALSE;
2504 2511 cfield++;
2505 2512 }
2506 2513 (void) printf("\n");
2507 2514 }
2508 2515
2509 2516 for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
2510 2517 print_us_node(scripted, parsable, fields, types, width, node);
2511 2518 if (rmnode)
2512 2519 nvlist_free(node->usn_nvl);
2513 2520 }
2514 2521 }
2515 2522
2516 2523 static int
2517 2524 zfs_do_userspace(int argc, char **argv)
2518 2525 {
2519 2526 zfs_handle_t *zhp;
2520 2527 zfs_userquota_prop_t p;
2521 2528 uu_avl_pool_t *avl_pool;
2522 2529 uu_avl_t *avl_tree;
2523 2530 uu_avl_walk_t *walk;
2524 2531 char *delim;
2525 2532 char deffields[] = "type,name,used,quota";
2526 2533 char *ofield = NULL;
2527 2534 char *tfield = NULL;
2528 2535 int cfield = 0;
2529 2536 int fields[256];
2530 2537 int i;
2531 2538 boolean_t scripted = B_FALSE;
2532 2539 boolean_t prtnum = B_FALSE;
2533 2540 boolean_t parsable = B_FALSE;
2534 2541 boolean_t sid2posix = B_FALSE;
2535 2542 int ret = 0;
2536 2543 int c;
2537 2544 zfs_sort_column_t *sortcol = NULL;
2538 2545 int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
2539 2546 us_cbdata_t cb;
2540 2547 us_node_t *node;
2541 2548 us_node_t *rmnode;
2542 2549 uu_list_pool_t *listpool;
2543 2550 uu_list_t *list;
2544 2551 uu_avl_index_t idx = 0;
2545 2552 uu_list_index_t idx2 = 0;
2546 2553
2547 2554 if (argc < 2)
2548 2555 usage(B_FALSE);
2549 2556
2550 2557 if (strcmp(argv[0], "groupspace") == 0)
2551 2558 /* Toggle default group types */
2552 2559 types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
2553 2560
2554 2561 while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
2555 2562 switch (c) {
2556 2563 case 'n':
2557 2564 prtnum = B_TRUE;
2558 2565 break;
2559 2566 case 'H':
2560 2567 scripted = B_TRUE;
2561 2568 break;
2562 2569 case 'p':
2563 2570 parsable = B_TRUE;
2564 2571 break;
2565 2572 case 'o':
2566 2573 ofield = optarg;
2567 2574 break;
2568 2575 case 's':
2569 2576 case 'S':
2570 2577 if (zfs_add_sort_column(&sortcol, optarg,
2571 2578 c == 's' ? B_FALSE : B_TRUE) != 0) {
2572 2579 (void) fprintf(stderr,
2573 2580 gettext("invalid field '%s'\n"), optarg);
2574 2581 usage(B_FALSE);
2575 2582 }
2576 2583 break;
2577 2584 case 't':
2578 2585 tfield = optarg;
2579 2586 break;
2580 2587 case 'i':
2581 2588 sid2posix = B_TRUE;
2582 2589 break;
2583 2590 case ':':
2584 2591 (void) fprintf(stderr, gettext("missing argument for "
2585 2592 "'%c' option\n"), optopt);
2586 2593 usage(B_FALSE);
2587 2594 break;
2588 2595 case '?':
2589 2596 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2590 2597 optopt);
2591 2598 usage(B_FALSE);
2592 2599 }
2593 2600 }
2594 2601
2595 2602 argc -= optind;
2596 2603 argv += optind;
2597 2604
2598 2605 if (argc < 1) {
2599 2606 (void) fprintf(stderr, gettext("missing dataset name\n"));
2600 2607 usage(B_FALSE);
2601 2608 }
2602 2609 if (argc > 1) {
2603 2610 (void) fprintf(stderr, gettext("too many arguments\n"));
2604 2611 usage(B_FALSE);
2605 2612 }
2606 2613
2607 2614 /* Use default output fields if not specified using -o */
2608 2615 if (ofield == NULL)
2609 2616 ofield = deffields;
2610 2617 do {
2611 2618 if ((delim = strchr(ofield, ',')) != NULL)
2612 2619 *delim = '\0';
2613 2620 if ((fields[cfield++] = us_field_index(ofield)) == -1) {
2614 2621 (void) fprintf(stderr, gettext("invalid type '%s' "
2615 2622 "for -o option\n"), ofield);
2616 2623 return (-1);
2617 2624 }
2618 2625 if (delim != NULL)
2619 2626 ofield = delim + 1;
2620 2627 } while (delim != NULL);
2621 2628 fields[cfield] = USFIELD_LAST;
2622 2629
2623 2630 /* Override output types (-t option) */
2624 2631 if (tfield != NULL) {
2625 2632 types = 0;
2626 2633
2627 2634 do {
2628 2635 boolean_t found = B_FALSE;
2629 2636
2630 2637 if ((delim = strchr(tfield, ',')) != NULL)
2631 2638 *delim = '\0';
2632 2639 for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
2633 2640 i++) {
2634 2641 if (strcmp(tfield, us_type_names[i]) == 0) {
2635 2642 found = B_TRUE;
2636 2643 types |= us_type_bits[i];
2637 2644 break;
2638 2645 }
2639 2646 }
2640 2647 if (!found) {
2641 2648 (void) fprintf(stderr, gettext("invalid type "
2642 2649 "'%s' for -t option\n"), tfield);
2643 2650 return (-1);
2644 2651 }
2645 2652 if (delim != NULL)
2646 2653 tfield = delim + 1;
2647 2654 } while (delim != NULL);
2648 2655 }
2649 2656
2650 2657 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
2651 2658 return (1);
2652 2659
2653 2660 if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
2654 2661 offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
2655 2662 nomem();
2656 2663 if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
2657 2664 nomem();
2658 2665
2659 2666 /* Always add default sorting columns */
2660 2667 (void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
2661 2668 (void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
2662 2669
2663 2670 cb.cb_sortcol = sortcol;
2664 2671 cb.cb_numname = prtnum;
2665 2672 cb.cb_nicenum = !parsable;
2666 2673 cb.cb_avl_pool = avl_pool;
2667 2674 cb.cb_avl = avl_tree;
2668 2675 cb.cb_sid2posix = sid2posix;
2669 2676
2670 2677 for (i = 0; i < USFIELD_LAST; i++)
2671 2678 cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
2672 2679
2673 2680 for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
2674 2681 if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) &&
2675 2682 !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
2676 2683 ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
2677 2684 !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
2678 2685 continue;
2679 2686 cb.cb_prop = p;
2680 2687 if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
2681 2688 return (ret);
2682 2689 }
2683 2690
2684 2691 /* Sort the list */
2685 2692 if ((node = uu_avl_first(avl_tree)) == NULL)
2686 2693 return (0);
2687 2694
2688 2695 us_populated = B_TRUE;
2689 2696
2690 2697 listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
2691 2698 offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
2692 2699 list = uu_list_create(listpool, NULL, UU_DEFAULT);
2693 2700 uu_list_node_init(node, &node->usn_listnode, listpool);
2694 2701
2695 2702 while (node != NULL) {
2696 2703 rmnode = node;
2697 2704 node = uu_avl_next(avl_tree, node);
2698 2705 uu_avl_remove(avl_tree, rmnode);
2699 2706 if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
2700 2707 uu_list_insert(list, rmnode, idx2);
2701 2708 }
2702 2709
2703 2710 for (node = uu_list_first(list); node != NULL;
2704 2711 node = uu_list_next(list, node)) {
2705 2712 us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
2706 2713
2707 2714 if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
2708 2715 uu_avl_insert(avl_tree, node, idx);
2709 2716 }
2710 2717
2711 2718 uu_list_destroy(list);
2712 2719 uu_list_pool_destroy(listpool);
2713 2720
2714 2721 /* Print and free node nvlist memory */
2715 2722 print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
2716 2723 cb.cb_avl);
2717 2724
2718 2725 zfs_free_sort_columns(sortcol);
2719 2726
2720 2727 /* Clean up the AVL tree */
2721 2728 if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
2722 2729 nomem();
2723 2730
2724 2731 while ((node = uu_avl_walk_next(walk)) != NULL) {
2725 2732 uu_avl_remove(cb.cb_avl, node);
2726 2733 free(node);
2727 2734 }
2728 2735
2729 2736 uu_avl_walk_end(walk);
2730 2737 uu_avl_destroy(avl_tree);
2731 2738 uu_avl_pool_destroy(avl_pool);
2732 2739
2733 2740 return (ret);
2734 2741 }
2735 2742
2736 2743 /*
2737 2744 * list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...]
2738 2745 * [-s property [-s property]...] [-S property [-S property]...]
2739 2746 * <dataset> ...
2740 2747 *
2741 2748 * -r Recurse over all children
2742 2749 * -d Limit recursion by depth.
2743 2750 * -H Scripted mode; elide headers and separate columns by tabs
2744 2751 * -o Control which fields to display.
2745 2752 * -t Control which object types to display.
2746 2753 * -s Specify sort columns, descending order.
2747 2754 * -S Specify sort columns, ascending order.
2748 2755 *
2749 2756 * When given no arguments, lists all filesystems in the system.
2750 2757 * Otherwise, list the specified datasets, optionally recursing down them if
2751 2758 * '-r' is specified.
2752 2759 */
2753 2760 typedef struct list_cbdata {
2754 2761 boolean_t cb_first;
2755 2762 boolean_t cb_scripted;
2756 2763 zprop_list_t *cb_proplist;
2757 2764 } list_cbdata_t;
2758 2765
2759 2766 /*
2760 2767 * Given a list of columns to display, output appropriate headers for each one.
2761 2768 */
2762 2769 static void
2763 2770 print_header(zprop_list_t *pl)
2764 2771 {
2765 2772 char headerbuf[ZFS_MAXPROPLEN];
2766 2773 const char *header;
2767 2774 int i;
2768 2775 boolean_t first = B_TRUE;
2769 2776 boolean_t right_justify;
2770 2777
2771 2778 for (; pl != NULL; pl = pl->pl_next) {
2772 2779 if (!first) {
2773 2780 (void) printf(" ");
2774 2781 } else {
2775 2782 first = B_FALSE;
2776 2783 }
2777 2784
2778 2785 right_justify = B_FALSE;
2779 2786 if (pl->pl_prop != ZPROP_INVAL) {
2780 2787 header = zfs_prop_column_name(pl->pl_prop);
2781 2788 right_justify = zfs_prop_align_right(pl->pl_prop);
2782 2789 } else {
2783 2790 for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
2784 2791 headerbuf[i] = toupper(pl->pl_user_prop[i]);
2785 2792 headerbuf[i] = '\0';
2786 2793 header = headerbuf;
2787 2794 }
2788 2795
2789 2796 if (pl->pl_next == NULL && !right_justify)
2790 2797 (void) printf("%s", header);
2791 2798 else if (right_justify)
2792 2799 (void) printf("%*s", pl->pl_width, header);
2793 2800 else
2794 2801 (void) printf("%-*s", pl->pl_width, header);
2795 2802 }
2796 2803
2797 2804 (void) printf("\n");
2798 2805 }
2799 2806
2800 2807 /*
2801 2808 * Given a dataset and a list of fields, print out all the properties according
2802 2809 * to the described layout.
2803 2810 */
2804 2811 static void
2805 2812 print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
2806 2813 {
2807 2814 boolean_t first = B_TRUE;
2808 2815 char property[ZFS_MAXPROPLEN];
2809 2816 nvlist_t *userprops = zfs_get_user_props(zhp);
2810 2817 nvlist_t *propval;
2811 2818 char *propstr;
2812 2819 boolean_t right_justify;
2813 2820 int width;
2814 2821
2815 2822 for (; pl != NULL; pl = pl->pl_next) {
2816 2823 if (!first) {
2817 2824 if (scripted)
2818 2825 (void) printf("\t");
2819 2826 else
2820 2827 (void) printf(" ");
2821 2828 } else {
2822 2829 first = B_FALSE;
2823 2830 }
2824 2831
2825 2832 if (pl->pl_prop != ZPROP_INVAL) {
2826 2833 if (zfs_prop_get(zhp, pl->pl_prop, property,
2827 2834 sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
2828 2835 propstr = "-";
2829 2836 else
2830 2837 propstr = property;
2831 2838
2832 2839 right_justify = zfs_prop_align_right(pl->pl_prop);
2833 2840 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
2834 2841 if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
2835 2842 property, sizeof (property), B_FALSE) != 0)
2836 2843 propstr = "-";
2837 2844 else
2838 2845 propstr = property;
2839 2846 right_justify = B_TRUE;
2840 2847 } else if (zfs_prop_written(pl->pl_user_prop)) {
2841 2848 if (zfs_prop_get_written(zhp, pl->pl_user_prop,
2842 2849 property, sizeof (property), B_FALSE) != 0)
2843 2850 propstr = "-";
2844 2851 else
2845 2852 propstr = property;
2846 2853 right_justify = B_TRUE;
2847 2854 } else {
2848 2855 if (nvlist_lookup_nvlist(userprops,
2849 2856 pl->pl_user_prop, &propval) != 0)
2850 2857 propstr = "-";
2851 2858 else
2852 2859 verify(nvlist_lookup_string(propval,
2853 2860 ZPROP_VALUE, &propstr) == 0);
2854 2861 right_justify = B_FALSE;
2855 2862 }
2856 2863
2857 2864 width = pl->pl_width;
2858 2865
2859 2866 /*
2860 2867 * If this is being called in scripted mode, or if this is the
2861 2868 * last column and it is left-justified, don't include a width
2862 2869 * format specifier.
2863 2870 */
2864 2871 if (scripted || (pl->pl_next == NULL && !right_justify))
2865 2872 (void) printf("%s", propstr);
2866 2873 else if (right_justify)
2867 2874 (void) printf("%*s", width, propstr);
2868 2875 else
2869 2876 (void) printf("%-*s", width, propstr);
2870 2877 }
2871 2878
2872 2879 (void) printf("\n");
2873 2880 }
2874 2881
2875 2882 /*
2876 2883 * Generic callback function to list a dataset or snapshot.
2877 2884 */
2878 2885 static int
2879 2886 list_callback(zfs_handle_t *zhp, void *data)
2880 2887 {
2881 2888 list_cbdata_t *cbp = data;
2882 2889
2883 2890 if (cbp->cb_first) {
2884 2891 if (!cbp->cb_scripted)
2885 2892 print_header(cbp->cb_proplist);
2886 2893 cbp->cb_first = B_FALSE;
2887 2894 }
2888 2895
2889 2896 print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted);
2890 2897
2891 2898 return (0);
2892 2899 }
2893 2900
2894 2901 static int
2895 2902 zfs_do_list(int argc, char **argv)
2896 2903 {
2897 2904 int c;
2898 2905 boolean_t scripted = B_FALSE;
2899 2906 static char default_fields[] =
2900 2907 "name,used,available,referenced,mountpoint";
2901 2908 int types = ZFS_TYPE_DATASET;
2902 2909 boolean_t types_specified = B_FALSE;
2903 2910 char *fields = NULL;
2904 2911 list_cbdata_t cb = { 0 };
2905 2912 char *value;
2906 2913 int limit = 0;
2907 2914 int ret = 0;
2908 2915 zfs_sort_column_t *sortcol = NULL;
2909 2916 int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
2910 2917
2911 2918 /* check options */
2912 2919 while ((c = getopt(argc, argv, ":d:o:rt:Hs:S:")) != -1) {
2913 2920 switch (c) {
2914 2921 case 'o':
2915 2922 fields = optarg;
2916 2923 break;
2917 2924 case 'd':
2918 2925 limit = parse_depth(optarg, &flags);
2919 2926 break;
2920 2927 case 'r':
2921 2928 flags |= ZFS_ITER_RECURSE;
2922 2929 break;
2923 2930 case 'H':
2924 2931 scripted = B_TRUE;
2925 2932 break;
2926 2933 case 's':
2927 2934 if (zfs_add_sort_column(&sortcol, optarg,
2928 2935 B_FALSE) != 0) {
2929 2936 (void) fprintf(stderr,
2930 2937 gettext("invalid property '%s'\n"), optarg);
2931 2938 usage(B_FALSE);
2932 2939 }
2933 2940 break;
2934 2941 case 'S':
2935 2942 if (zfs_add_sort_column(&sortcol, optarg,
2936 2943 B_TRUE) != 0) {
2937 2944 (void) fprintf(stderr,
2938 2945 gettext("invalid property '%s'\n"), optarg);
2939 2946 usage(B_FALSE);
2940 2947 }
2941 2948 break;
2942 2949 case 't':
2943 2950 types = 0;
2944 2951 types_specified = B_TRUE;
2945 2952 flags &= ~ZFS_ITER_PROP_LISTSNAPS;
2946 2953 while (*optarg != '\0') {
2947 2954 static char *type_subopts[] = { "filesystem",
2948 2955 "volume", "snapshot", "all", NULL };
2949 2956
2950 2957 switch (getsubopt(&optarg, type_subopts,
2951 2958 &value)) {
2952 2959 case 0:
2953 2960 types |= ZFS_TYPE_FILESYSTEM;
2954 2961 break;
2955 2962 case 1:
2956 2963 types |= ZFS_TYPE_VOLUME;
2957 2964 break;
2958 2965 case 2:
2959 2966 types |= ZFS_TYPE_SNAPSHOT;
2960 2967 break;
2961 2968 case 3:
2962 2969 types = ZFS_TYPE_DATASET;
2963 2970 break;
2964 2971
2965 2972 default:
2966 2973 (void) fprintf(stderr,
2967 2974 gettext("invalid type '%s'\n"),
2968 2975 value);
2969 2976 usage(B_FALSE);
2970 2977 }
2971 2978 }
2972 2979 break;
2973 2980 case ':':
2974 2981 (void) fprintf(stderr, gettext("missing argument for "
2975 2982 "'%c' option\n"), optopt);
2976 2983 usage(B_FALSE);
2977 2984 break;
2978 2985 case '?':
2979 2986 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2980 2987 optopt);
2981 2988 usage(B_FALSE);
2982 2989 }
2983 2990 }
2984 2991
2985 2992 argc -= optind;
2986 2993 argv += optind;
2987 2994
2988 2995 if (fields == NULL)
2989 2996 fields = default_fields;
2990 2997
2991 2998 /*
2992 2999 * If "-o space" and no types were specified, don't display snapshots.
2993 3000 */
2994 3001 if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
2995 3002 types &= ~ZFS_TYPE_SNAPSHOT;
2996 3003
2997 3004 /*
2998 3005 * If the user specifies '-o all', the zprop_get_list() doesn't
2999 3006 * normally include the name of the dataset. For 'zfs list', we always
3000 3007 * want this property to be first.
3001 3008 */
3002 3009 if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
3003 3010 != 0)
3004 3011 usage(B_FALSE);
3005 3012
3006 3013 cb.cb_scripted = scripted;
3007 3014 cb.cb_first = B_TRUE;
3008 3015
3009 3016 ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
3010 3017 limit, list_callback, &cb);
3011 3018
3012 3019 zprop_free_list(cb.cb_proplist);
3013 3020 zfs_free_sort_columns(sortcol);
3014 3021
3015 3022 if (ret == 0 && cb.cb_first && !cb.cb_scripted)
3016 3023 (void) printf(gettext("no datasets available\n"));
3017 3024
3018 3025 return (ret);
3019 3026 }
3020 3027
3021 3028 /*
3022 3029 * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
3023 3030 * zfs rename [-f] -p <fs | vol> <fs | vol>
3024 3031 * zfs rename -r <snap> <snap>
3025 3032 *
3026 3033 * Renames the given dataset to another of the same type.
3027 3034 *
3028 3035 * The '-p' flag creates all the non-existing ancestors of the target first.
3029 3036 */
3030 3037 /* ARGSUSED */
3031 3038 static int
3032 3039 zfs_do_rename(int argc, char **argv)
3033 3040 {
3034 3041 zfs_handle_t *zhp;
3035 3042 int c;
3036 3043 int ret = 0;
3037 3044 boolean_t recurse = B_FALSE;
3038 3045 boolean_t parents = B_FALSE;
3039 3046 boolean_t force_unmount = B_FALSE;
3040 3047
3041 3048 /* check options */
3042 3049 while ((c = getopt(argc, argv, "prf")) != -1) {
3043 3050 switch (c) {
3044 3051 case 'p':
3045 3052 parents = B_TRUE;
3046 3053 break;
3047 3054 case 'r':
3048 3055 recurse = B_TRUE;
3049 3056 break;
3050 3057 case 'f':
3051 3058 force_unmount = B_TRUE;
3052 3059 break;
3053 3060 case '?':
3054 3061 default:
3055 3062 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3056 3063 optopt);
3057 3064 usage(B_FALSE);
3058 3065 }
3059 3066 }
3060 3067
3061 3068 argc -= optind;
3062 3069 argv += optind;
3063 3070
3064 3071 /* check number of arguments */
3065 3072 if (argc < 1) {
3066 3073 (void) fprintf(stderr, gettext("missing source dataset "
3067 3074 "argument\n"));
3068 3075 usage(B_FALSE);
3069 3076 }
3070 3077 if (argc < 2) {
3071 3078 (void) fprintf(stderr, gettext("missing target dataset "
3072 3079 "argument\n"));
3073 3080 usage(B_FALSE);
3074 3081 }
3075 3082 if (argc > 2) {
3076 3083 (void) fprintf(stderr, gettext("too many arguments\n"));
3077 3084 usage(B_FALSE);
3078 3085 }
3079 3086
3080 3087 if (recurse && parents) {
3081 3088 (void) fprintf(stderr, gettext("-p and -r options are mutually "
3082 3089 "exclusive\n"));
3083 3090 usage(B_FALSE);
3084 3091 }
3085 3092
3086 3093 if (recurse && strchr(argv[0], '@') == 0) {
3087 3094 (void) fprintf(stderr, gettext("source dataset for recursive "
3088 3095 "rename must be a snapshot\n"));
3089 3096 usage(B_FALSE);
3090 3097 }
3091 3098
3092 3099 if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
3093 3100 ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
3094 3101 return (1);
3095 3102
3096 3103 /* If we were asked and the name looks good, try to create ancestors. */
3097 3104 if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
3098 3105 zfs_create_ancestors(g_zfs, argv[1]) != 0) {
3099 3106 zfs_close(zhp);
3100 3107 return (1);
3101 3108 }
3102 3109
3103 3110 ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
3104 3111
3105 3112 zfs_close(zhp);
3106 3113 return (ret);
3107 3114 }
3108 3115
3109 3116 /*
3110 3117 * zfs promote <fs>
3111 3118 *
3112 3119 * Promotes the given clone fs to be the parent
3113 3120 */
3114 3121 /* ARGSUSED */
3115 3122 static int
3116 3123 zfs_do_promote(int argc, char **argv)
3117 3124 {
3118 3125 zfs_handle_t *zhp;
3119 3126 int ret = 0;
3120 3127
3121 3128 /* check options */
3122 3129 if (argc > 1 && argv[1][0] == '-') {
3123 3130 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3124 3131 argv[1][1]);
3125 3132 usage(B_FALSE);
3126 3133 }
3127 3134
3128 3135 /* check number of arguments */
3129 3136 if (argc < 2) {
3130 3137 (void) fprintf(stderr, gettext("missing clone filesystem"
3131 3138 " argument\n"));
3132 3139 usage(B_FALSE);
3133 3140 }
3134 3141 if (argc > 2) {
3135 3142 (void) fprintf(stderr, gettext("too many arguments\n"));
3136 3143 usage(B_FALSE);
3137 3144 }
3138 3145
3139 3146 zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3140 3147 if (zhp == NULL)
3141 3148 return (1);
3142 3149
3143 3150 ret = (zfs_promote(zhp) != 0);
3144 3151
3145 3152
3146 3153 zfs_close(zhp);
3147 3154 return (ret);
3148 3155 }
3149 3156
3150 3157 /*
3151 3158 * zfs rollback [-rRf] <snapshot>
3152 3159 *
3153 3160 * -r Delete any intervening snapshots before doing rollback
3154 3161 * -R Delete any snapshots and their clones
3155 3162 * -f ignored for backwards compatability
3156 3163 *
3157 3164 * Given a filesystem, rollback to a specific snapshot, discarding any changes
3158 3165 * since then and making it the active dataset. If more recent snapshots exist,
3159 3166 * the command will complain unless the '-r' flag is given.
3160 3167 */
3161 3168 typedef struct rollback_cbdata {
3162 3169 uint64_t cb_create;
3163 3170 boolean_t cb_first;
3164 3171 int cb_doclones;
3165 3172 char *cb_target;
3166 3173 int cb_error;
3167 3174 boolean_t cb_recurse;
3168 3175 boolean_t cb_dependent;
3169 3176 } rollback_cbdata_t;
3170 3177
3171 3178 /*
3172 3179 * Report any snapshots more recent than the one specified. Used when '-r' is
3173 3180 * not specified. We reuse this same callback for the snapshot dependents - if
3174 3181 * 'cb_dependent' is set, then this is a dependent and we should report it
3175 3182 * without checking the transaction group.
3176 3183 */
3177 3184 static int
3178 3185 rollback_check(zfs_handle_t *zhp, void *data)
3179 3186 {
3180 3187 rollback_cbdata_t *cbp = data;
3181 3188
3182 3189 if (cbp->cb_doclones) {
3183 3190 zfs_close(zhp);
3184 3191 return (0);
3185 3192 }
3186 3193
3187 3194 if (!cbp->cb_dependent) {
3188 3195 if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
3189 3196 zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3190 3197 zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3191 3198 cbp->cb_create) {
3192 3199
3193 3200 if (cbp->cb_first && !cbp->cb_recurse) {
3194 3201 (void) fprintf(stderr, gettext("cannot "
3195 3202 "rollback to '%s': more recent snapshots "
3196 3203 "exist\n"),
3197 3204 cbp->cb_target);
3198 3205 (void) fprintf(stderr, gettext("use '-r' to "
3199 3206 "force deletion of the following "
3200 3207 "snapshots:\n"));
3201 3208 cbp->cb_first = 0;
3202 3209 cbp->cb_error = 1;
3203 3210 }
3204 3211
3205 3212 if (cbp->cb_recurse) {
3206 3213 cbp->cb_dependent = B_TRUE;
3207 3214 if (zfs_iter_dependents(zhp, B_TRUE,
3208 3215 rollback_check, cbp) != 0) {
3209 3216 zfs_close(zhp);
3210 3217 return (-1);
3211 3218 }
3212 3219 cbp->cb_dependent = B_FALSE;
3213 3220 } else {
3214 3221 (void) fprintf(stderr, "%s\n",
3215 3222 zfs_get_name(zhp));
3216 3223 }
3217 3224 }
3218 3225 } else {
3219 3226 if (cbp->cb_first && cbp->cb_recurse) {
3220 3227 (void) fprintf(stderr, gettext("cannot rollback to "
3221 3228 "'%s': clones of previous snapshots exist\n"),
3222 3229 cbp->cb_target);
3223 3230 (void) fprintf(stderr, gettext("use '-R' to "
3224 3231 "force deletion of the following clones and "
3225 3232 "dependents:\n"));
3226 3233 cbp->cb_first = 0;
3227 3234 cbp->cb_error = 1;
3228 3235 }
3229 3236
3230 3237 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
3231 3238 }
3232 3239
3233 3240 zfs_close(zhp);
3234 3241 return (0);
3235 3242 }
3236 3243
3237 3244 static int
3238 3245 zfs_do_rollback(int argc, char **argv)
3239 3246 {
3240 3247 int ret = 0;
3241 3248 int c;
3242 3249 boolean_t force = B_FALSE;
3243 3250 rollback_cbdata_t cb = { 0 };
3244 3251 zfs_handle_t *zhp, *snap;
3245 3252 char parentname[ZFS_MAXNAMELEN];
3246 3253 char *delim;
3247 3254
3248 3255 /* check options */
3249 3256 while ((c = getopt(argc, argv, "rRf")) != -1) {
3250 3257 switch (c) {
3251 3258 case 'r':
3252 3259 cb.cb_recurse = 1;
3253 3260 break;
3254 3261 case 'R':
3255 3262 cb.cb_recurse = 1;
3256 3263 cb.cb_doclones = 1;
3257 3264 break;
3258 3265 case 'f':
3259 3266 force = B_TRUE;
3260 3267 break;
3261 3268 case '?':
3262 3269 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3263 3270 optopt);
3264 3271 usage(B_FALSE);
3265 3272 }
3266 3273 }
3267 3274
3268 3275 argc -= optind;
3269 3276 argv += optind;
3270 3277
3271 3278 /* check number of arguments */
3272 3279 if (argc < 1) {
3273 3280 (void) fprintf(stderr, gettext("missing dataset argument\n"));
3274 3281 usage(B_FALSE);
3275 3282 }
3276 3283 if (argc > 1) {
3277 3284 (void) fprintf(stderr, gettext("too many arguments\n"));
3278 3285 usage(B_FALSE);
3279 3286 }
3280 3287
3281 3288 /* open the snapshot */
3282 3289 if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
3283 3290 return (1);
3284 3291
3285 3292 /* open the parent dataset */
3286 3293 (void) strlcpy(parentname, argv[0], sizeof (parentname));
3287 3294 verify((delim = strrchr(parentname, '@')) != NULL);
3288 3295 *delim = '\0';
3289 3296 if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
3290 3297 zfs_close(snap);
3291 3298 return (1);
3292 3299 }
3293 3300
3294 3301 /*
3295 3302 * Check for more recent snapshots and/or clones based on the presence
3296 3303 * of '-r' and '-R'.
3297 3304 */
3298 3305 cb.cb_target = argv[0];
3299 3306 cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3300 3307 cb.cb_first = B_TRUE;
3301 3308 cb.cb_error = 0;
3302 3309 if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0)
3303 3310 goto out;
3304 3311
3305 3312 if ((ret = cb.cb_error) != 0)
3306 3313 goto out;
3307 3314
3308 3315 /*
3309 3316 * Rollback parent to the given snapshot.
3310 3317 */
3311 3318 ret = zfs_rollback(zhp, snap, force);
3312 3319
3313 3320 out:
3314 3321 zfs_close(snap);
3315 3322 zfs_close(zhp);
3316 3323
3317 3324 if (ret == 0)
3318 3325 return (0);
3319 3326 else
3320 3327 return (1);
3321 3328 }
3322 3329
3323 3330 /*
3324 3331 * zfs set property=value { fs | snap | vol } ...
3325 3332 *
3326 3333 * Sets the given property for all datasets specified on the command line.
3327 3334 */
3328 3335 typedef struct set_cbdata {
3329 3336 char *cb_propname;
3330 3337 char *cb_value;
3331 3338 } set_cbdata_t;
3332 3339
3333 3340 static int
3334 3341 set_callback(zfs_handle_t *zhp, void *data)
3335 3342 {
3336 3343 set_cbdata_t *cbp = data;
3337 3344
3338 3345 if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) {
3339 3346 switch (libzfs_errno(g_zfs)) {
3340 3347 case EZFS_MOUNTFAILED:
3341 3348 (void) fprintf(stderr, gettext("property may be set "
3342 3349 "but unable to remount filesystem\n"));
3343 3350 break;
3344 3351 case EZFS_SHARENFSFAILED:
3345 3352 (void) fprintf(stderr, gettext("property may be set "
3346 3353 "but unable to reshare filesystem\n"));
3347 3354 break;
3348 3355 }
3349 3356 return (1);
3350 3357 }
3351 3358 return (0);
3352 3359 }
3353 3360
3354 3361 static int
3355 3362 zfs_do_set(int argc, char **argv)
3356 3363 {
3357 3364 set_cbdata_t cb;
3358 3365 int ret = 0;
3359 3366
3360 3367 /* check for options */
3361 3368 if (argc > 1 && argv[1][0] == '-') {
3362 3369 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3363 3370 argv[1][1]);
3364 3371 usage(B_FALSE);
3365 3372 }
3366 3373
3367 3374 /* check number of arguments */
3368 3375 if (argc < 2) {
3369 3376 (void) fprintf(stderr, gettext("missing property=value "
3370 3377 "argument\n"));
3371 3378 usage(B_FALSE);
3372 3379 }
3373 3380 if (argc < 3) {
3374 3381 (void) fprintf(stderr, gettext("missing dataset name\n"));
3375 3382 usage(B_FALSE);
3376 3383 }
3377 3384
3378 3385 /* validate property=value argument */
3379 3386 cb.cb_propname = argv[1];
3380 3387 if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) ||
3381 3388 (cb.cb_value[1] == '\0')) {
3382 3389 (void) fprintf(stderr, gettext("missing value in "
3383 3390 "property=value argument\n"));
3384 3391 usage(B_FALSE);
3385 3392 }
3386 3393
3387 3394 *cb.cb_value = '\0';
3388 3395 cb.cb_value++;
3389 3396
3390 3397 if (*cb.cb_propname == '\0') {
3391 3398 (void) fprintf(stderr,
3392 3399 gettext("missing property in property=value argument\n"));
3393 3400 usage(B_FALSE);
3394 3401 }
3395 3402
3396 3403 ret = zfs_for_each(argc - 2, argv + 2, NULL,
3397 3404 ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
3398 3405
3399 3406 return (ret);
3400 3407 }
3401 3408
3402 3409 typedef struct snap_cbdata {
3403 3410 nvlist_t *sd_nvl;
3404 3411 boolean_t sd_recursive;
3405 3412 const char *sd_snapname;
3406 3413 } snap_cbdata_t;
3407 3414
3408 3415 static int
3409 3416 zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
3410 3417 {
3411 3418 snap_cbdata_t *sd = arg;
3412 3419 char *name;
3413 3420 int rv = 0;
3414 3421 int error;
3415 3422
3416 3423 error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
3417 3424 if (error == -1)
3418 3425 nomem();
3419 3426 fnvlist_add_boolean(sd->sd_nvl, name);
3420 3427 free(name);
3421 3428
3422 3429 if (sd->sd_recursive)
3423 3430 rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
3424 3431 zfs_close(zhp);
3425 3432 return (rv);
3426 3433 }
3427 3434
3428 3435 /*
3429 3436 * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
3430 3437 *
3431 3438 * Creates a snapshot with the given name. While functionally equivalent to
3432 3439 * 'zfs create', it is a separate command to differentiate intent.
3433 3440 */
3434 3441 static int
3435 3442 zfs_do_snapshot(int argc, char **argv)
3436 3443 {
3437 3444 int ret = 0;
3438 3445 char c;
3439 3446 nvlist_t *props;
3440 3447 snap_cbdata_t sd = { 0 };
3441 3448 boolean_t multiple_snaps = B_FALSE;
3442 3449
3443 3450 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3444 3451 nomem();
3445 3452 if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
3446 3453 nomem();
3447 3454
3448 3455 /* check options */
3449 3456 while ((c = getopt(argc, argv, "ro:")) != -1) {
3450 3457 switch (c) {
3451 3458 case 'o':
3452 3459 if (parseprop(props))
3453 3460 return (1);
3454 3461 break;
3455 3462 case 'r':
3456 3463 sd.sd_recursive = B_TRUE;
3457 3464 multiple_snaps = B_TRUE;
3458 3465 break;
3459 3466 case '?':
3460 3467 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3461 3468 optopt);
3462 3469 goto usage;
3463 3470 }
3464 3471 }
3465 3472
3466 3473 argc -= optind;
3467 3474 argv += optind;
3468 3475
3469 3476 /* check number of arguments */
3470 3477 if (argc < 1) {
3471 3478 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3472 3479 goto usage;
3473 3480 }
3474 3481
3475 3482 if (argc > 1)
3476 3483 multiple_snaps = B_TRUE;
3477 3484 for (; argc > 0; argc--, argv++) {
3478 3485 char *atp;
3479 3486 zfs_handle_t *zhp;
3480 3487
3481 3488 atp = strchr(argv[0], '@');
3482 3489 if (atp == NULL)
3483 3490 goto usage;
3484 3491 *atp = '\0';
3485 3492 sd.sd_snapname = atp + 1;
3486 3493 zhp = zfs_open(g_zfs, argv[0],
3487 3494 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3488 3495 if (zhp == NULL)
3489 3496 goto usage;
3490 3497 if (zfs_snapshot_cb(zhp, &sd) != 0)
3491 3498 goto usage;
3492 3499 }
3493 3500
3494 3501 ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
3495 3502 nvlist_free(sd.sd_nvl);
3496 3503 nvlist_free(props);
3497 3504 if (ret != 0 && multiple_snaps)
3498 3505 (void) fprintf(stderr, gettext("no snapshots were created\n"));
3499 3506 return (ret != 0);
3500 3507
3501 3508 usage:
3502 3509 nvlist_free(sd.sd_nvl);
3503 3510 nvlist_free(props);
3504 3511 usage(B_FALSE);
3505 3512 return (-1);
3506 3513 }
3507 3514
3508 3515 /*
3509 3516 * Send a backup stream to stdout.
3510 3517 */
3511 3518 static int
3512 3519 zfs_do_send(int argc, char **argv)
3513 3520 {
3514 3521 char *fromname = NULL;
3515 3522 char *toname = NULL;
3516 3523 char *cp;
3517 3524 zfs_handle_t *zhp;
3518 3525 sendflags_t flags = { 0 };
3519 3526 int c, err;
3520 3527 nvlist_t *dbgnv = NULL;
3521 3528 boolean_t extraverbose = B_FALSE;
3522 3529
3523 3530 /* check options */
3524 3531 while ((c = getopt(argc, argv, ":i:I:RDpvnP")) != -1) {
3525 3532 switch (c) {
3526 3533 case 'i':
3527 3534 if (fromname)
3528 3535 usage(B_FALSE);
3529 3536 fromname = optarg;
3530 3537 break;
3531 3538 case 'I':
3532 3539 if (fromname)
3533 3540 usage(B_FALSE);
3534 3541 fromname = optarg;
3535 3542 flags.doall = B_TRUE;
3536 3543 break;
3537 3544 case 'R':
3538 3545 flags.replicate = B_TRUE;
3539 3546 break;
3540 3547 case 'p':
3541 3548 flags.props = B_TRUE;
3542 3549 break;
3543 3550 case 'P':
3544 3551 flags.parsable = B_TRUE;
3545 3552 flags.verbose = B_TRUE;
3546 3553 break;
3547 3554 case 'v':
3548 3555 if (flags.verbose)
3549 3556 extraverbose = B_TRUE;
3550 3557 flags.verbose = B_TRUE;
3551 3558 flags.progress = B_TRUE;
3552 3559 break;
3553 3560 case 'D':
3554 3561 flags.dedup = B_TRUE;
3555 3562 break;
3556 3563 case 'n':
3557 3564 flags.dryrun = B_TRUE;
3558 3565 break;
3559 3566 case ':':
3560 3567 (void) fprintf(stderr, gettext("missing argument for "
3561 3568 "'%c' option\n"), optopt);
3562 3569 usage(B_FALSE);
3563 3570 break;
3564 3571 case '?':
3565 3572 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3566 3573 optopt);
3567 3574 usage(B_FALSE);
3568 3575 }
3569 3576 }
3570 3577
3571 3578 argc -= optind;
3572 3579 argv += optind;
3573 3580
3574 3581 /* check number of arguments */
3575 3582 if (argc < 1) {
3576 3583 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3577 3584 usage(B_FALSE);
3578 3585 }
3579 3586 if (argc > 1) {
3580 3587 (void) fprintf(stderr, gettext("too many arguments\n"));
3581 3588 usage(B_FALSE);
3582 3589 }
3583 3590
3584 3591 if (!flags.dryrun && isatty(STDOUT_FILENO)) {
3585 3592 (void) fprintf(stderr,
3586 3593 gettext("Error: Stream can not be written to a terminal.\n"
3587 3594 "You must redirect standard output.\n"));
3588 3595 return (1);
3589 3596 }
3590 3597
3591 3598 cp = strchr(argv[0], '@');
3592 3599 if (cp == NULL) {
3593 3600 (void) fprintf(stderr,
3594 3601 gettext("argument must be a snapshot\n"));
3595 3602 usage(B_FALSE);
3596 3603 }
3597 3604 *cp = '\0';
3598 3605 toname = cp + 1;
3599 3606 zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3600 3607 if (zhp == NULL)
3601 3608 return (1);
3602 3609
3603 3610 /*
3604 3611 * If they specified the full path to the snapshot, chop off
3605 3612 * everything except the short name of the snapshot, but special
3606 3613 * case if they specify the origin.
3607 3614 */
3608 3615 if (fromname && (cp = strchr(fromname, '@')) != NULL) {
3609 3616 char origin[ZFS_MAXNAMELEN];
3610 3617 zprop_source_t src;
3611 3618
3612 3619 (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
3613 3620 origin, sizeof (origin), &src, NULL, 0, B_FALSE);
3614 3621
3615 3622 if (strcmp(origin, fromname) == 0) {
3616 3623 fromname = NULL;
3617 3624 flags.fromorigin = B_TRUE;
3618 3625 } else {
3619 3626 *cp = '\0';
3620 3627 if (cp != fromname && strcmp(argv[0], fromname)) {
3621 3628 (void) fprintf(stderr,
3622 3629 gettext("incremental source must be "
3623 3630 "in same filesystem\n"));
3624 3631 usage(B_FALSE);
3625 3632 }
3626 3633 fromname = cp + 1;
3627 3634 if (strchr(fromname, '@') || strchr(fromname, '/')) {
3628 3635 (void) fprintf(stderr,
3629 3636 gettext("invalid incremental source\n"));
3630 3637 usage(B_FALSE);
3631 3638 }
3632 3639 }
3633 3640 }
3634 3641
3635 3642 if (flags.replicate && fromname == NULL)
3636 3643 flags.doall = B_TRUE;
3637 3644
3638 3645 err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
3639 3646 extraverbose ? &dbgnv : NULL);
3640 3647
3641 3648 if (extraverbose && dbgnv != NULL) {
3642 3649 /*
3643 3650 * dump_nvlist prints to stdout, but that's been
3644 3651 * redirected to a file. Make it print to stderr
3645 3652 * instead.
3646 3653 */
3647 3654 (void) dup2(STDERR_FILENO, STDOUT_FILENO);
3648 3655 dump_nvlist(dbgnv, 0);
3649 3656 nvlist_free(dbgnv);
3650 3657 }
3651 3658 zfs_close(zhp);
3652 3659
3653 3660 return (err != 0);
3654 3661 }
3655 3662
3656 3663 /*
3657 3664 * zfs receive [-vnFu] [-d | -e] <fs@snap>
3658 3665 *
3659 3666 * Restore a backup stream from stdin.
3660 3667 */
3661 3668 static int
3662 3669 zfs_do_receive(int argc, char **argv)
3663 3670 {
3664 3671 int c, err;
3665 3672 recvflags_t flags = { 0 };
3666 3673
3667 3674 /* check options */
3668 3675 while ((c = getopt(argc, argv, ":denuvF")) != -1) {
3669 3676 switch (c) {
3670 3677 case 'd':
3671 3678 flags.isprefix = B_TRUE;
3672 3679 break;
3673 3680 case 'e':
3674 3681 flags.isprefix = B_TRUE;
3675 3682 flags.istail = B_TRUE;
3676 3683 break;
3677 3684 case 'n':
3678 3685 flags.dryrun = B_TRUE;
3679 3686 break;
3680 3687 case 'u':
3681 3688 flags.nomount = B_TRUE;
3682 3689 break;
3683 3690 case 'v':
3684 3691 flags.verbose = B_TRUE;
3685 3692 break;
3686 3693 case 'F':
3687 3694 flags.force = B_TRUE;
3688 3695 break;
3689 3696 case ':':
3690 3697 (void) fprintf(stderr, gettext("missing argument for "
3691 3698 "'%c' option\n"), optopt);
3692 3699 usage(B_FALSE);
3693 3700 break;
3694 3701 case '?':
3695 3702 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3696 3703 optopt);
3697 3704 usage(B_FALSE);
3698 3705 }
3699 3706 }
3700 3707
3701 3708 argc -= optind;
3702 3709 argv += optind;
3703 3710
3704 3711 /* check number of arguments */
3705 3712 if (argc < 1) {
3706 3713 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3707 3714 usage(B_FALSE);
3708 3715 }
3709 3716 if (argc > 1) {
3710 3717 (void) fprintf(stderr, gettext("too many arguments\n"));
3711 3718 usage(B_FALSE);
3712 3719 }
3713 3720
3714 3721 if (isatty(STDIN_FILENO)) {
3715 3722 (void) fprintf(stderr,
3716 3723 gettext("Error: Backup stream can not be read "
3717 3724 "from a terminal.\n"
↓ open down ↓ |
3451 lines elided |
↑ open up ↑ |
3718 3725 "You must redirect standard input.\n"));
3719 3726 return (1);
3720 3727 }
3721 3728
3722 3729 err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL);
3723 3730
3724 3731 return (err != 0);
3725 3732 }
3726 3733
3727 3734 /*
3735 + * Send a backup stream to stdout in fits format.
3736 + */
3737 +static int
3738 +zfs_do_fits_send(int argc, char **argv)
3739 +{
3740 + char *fromname = NULL;
3741 + char *toname = NULL;
3742 + char *cp;
3743 + zfs_handle_t *zhp;
3744 + sendflags_t flags = { 0 };
3745 + int c, err;
3746 +
3747 + /* check options */
3748 + while ((c = getopt(argc, argv, ":i:v")) != -1) {
3749 + switch (c) {
3750 + case 'i':
3751 + if (fromname)
3752 + usage(B_FALSE);
3753 + fromname = optarg;
3754 + break;
3755 + case 'v':
3756 + flags.verbose = B_TRUE;
3757 + break;
3758 + case ':':
3759 + (void) fprintf(stderr, gettext("missing argument for "
3760 + "'%c' option\n"), optopt);
3761 + usage(B_FALSE);
3762 + break;
3763 + case '?':
3764 + (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3765 + optopt);
3766 + usage(B_FALSE);
3767 + }
3768 + }
3769 +
3770 + argc -= optind;
3771 + argv += optind;
3772 +
3773 + /* check number of arguments */
3774 + if (argc < 1) {
3775 + (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3776 + usage(B_FALSE);
3777 + }
3778 + if (argc > 1) {
3779 + (void) fprintf(stderr, gettext("too many arguments\n"));
3780 + usage(B_FALSE);
3781 + }
3782 +
3783 + if (isatty(STDOUT_FILENO)) {
3784 + (void) fprintf(stderr,
3785 + gettext("Error: Stream can not be written to a terminal.\n"
3786 + "You must redirect standard output.\n"));
3787 + return (1);
3788 + }
3789 +
3790 + cp = strchr(argv[0], '@');
3791 + if (cp == NULL) {
3792 + (void) fprintf(stderr,
3793 + gettext("argument must be a snapshot\n"));
3794 + usage(B_FALSE);
3795 + }
3796 + *cp = '\0';
3797 + toname = cp + 1;
3798 + zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);
3799 + if (zhp == NULL)
3800 + return (1);
3801 +
3802 + /*
3803 + * If they specified the full path to the snapshot, chop off
3804 + * everything except the short name of the snapshot, but special
3805 + * case if they specify the origin.
3806 + */
3807 + if (fromname && (cp = strchr(fromname, '@')) != NULL) {
3808 + char origin[ZFS_MAXNAMELEN];
3809 + zprop_source_t src;
3810 +
3811 + (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
3812 + origin, sizeof (origin), &src, NULL, 0, B_FALSE);
3813 +
3814 + if (strcmp(origin, fromname) == 0) {
3815 + fromname = NULL;
3816 + flags.fromorigin = B_TRUE;
3817 + } else {
3818 + *cp = '\0';
3819 + if (cp != fromname && strcmp(argv[0], fromname)) {
3820 + (void) fprintf(stderr,
3821 + gettext("incremental source must be "
3822 + "in same filesystem\n"));
3823 + usage(B_FALSE);
3824 + }
3825 + fromname = cp + 1;
3826 + if (strchr(fromname, '@') || strchr(fromname, '/')) {
3827 + (void) fprintf(stderr,
3828 + gettext("invalid incremental source\n"));
3829 + usage(B_FALSE);
3830 + }
3831 + }
3832 + }
3833 +
3834 + err = zfs_fits_send(zhp, fromname, toname, &flags, STDOUT_FILENO,
3835 + NULL, 0);
3836 +
3837 + zfs_close(zhp);
3838 +
3839 + return (err != 0);
3840 +}
3841 +
3842 +/*
3728 3843 * allow/unallow stuff
3729 3844 */
3730 3845 /* copied from zfs/sys/dsl_deleg.h */
3731 3846 #define ZFS_DELEG_PERM_CREATE "create"
3732 3847 #define ZFS_DELEG_PERM_DESTROY "destroy"
3733 3848 #define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
3734 3849 #define ZFS_DELEG_PERM_ROLLBACK "rollback"
3735 3850 #define ZFS_DELEG_PERM_CLONE "clone"
3736 3851 #define ZFS_DELEG_PERM_PROMOTE "promote"
3737 3852 #define ZFS_DELEG_PERM_RENAME "rename"
3738 3853 #define ZFS_DELEG_PERM_MOUNT "mount"
3739 3854 #define ZFS_DELEG_PERM_SHARE "share"
3740 3855 #define ZFS_DELEG_PERM_SEND "send"
3741 3856 #define ZFS_DELEG_PERM_RECEIVE "receive"
3742 3857 #define ZFS_DELEG_PERM_ALLOW "allow"
3743 3858 #define ZFS_DELEG_PERM_USERPROP "userprop"
3744 3859 #define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */
3745 3860 #define ZFS_DELEG_PERM_USERQUOTA "userquota"
3746 3861 #define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
3747 3862 #define ZFS_DELEG_PERM_USERUSED "userused"
3748 3863 #define ZFS_DELEG_PERM_GROUPUSED "groupused"
3749 3864 #define ZFS_DELEG_PERM_HOLD "hold"
3750 3865 #define ZFS_DELEG_PERM_RELEASE "release"
3751 3866 #define ZFS_DELEG_PERM_DIFF "diff"
3752 3867
3753 3868 #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
3754 3869
3755 3870 static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
3756 3871 { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
3757 3872 { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
3758 3873 { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
3759 3874 { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
3760 3875 { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
3761 3876 { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
3762 3877 { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
3763 3878 { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
3764 3879 { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
3765 3880 { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
3766 3881 { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
3767 3882 { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
3768 3883 { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
3769 3884 { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
3770 3885 { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
3771 3886
3772 3887 { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
3773 3888 { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
3774 3889 { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
3775 3890 { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
3776 3891 { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
3777 3892 { NULL, ZFS_DELEG_NOTE_NONE }
3778 3893 };
3779 3894
3780 3895 /* permission structure */
3781 3896 typedef struct deleg_perm {
3782 3897 zfs_deleg_who_type_t dp_who_type;
3783 3898 const char *dp_name;
3784 3899 boolean_t dp_local;
3785 3900 boolean_t dp_descend;
3786 3901 } deleg_perm_t;
3787 3902
3788 3903 /* */
3789 3904 typedef struct deleg_perm_node {
3790 3905 deleg_perm_t dpn_perm;
3791 3906
3792 3907 uu_avl_node_t dpn_avl_node;
3793 3908 } deleg_perm_node_t;
3794 3909
3795 3910 typedef struct fs_perm fs_perm_t;
3796 3911
3797 3912 /* permissions set */
3798 3913 typedef struct who_perm {
3799 3914 zfs_deleg_who_type_t who_type;
3800 3915 const char *who_name; /* id */
3801 3916 char who_ug_name[256]; /* user/group name */
3802 3917 fs_perm_t *who_fsperm; /* uplink */
3803 3918
3804 3919 uu_avl_t *who_deleg_perm_avl; /* permissions */
3805 3920 } who_perm_t;
3806 3921
3807 3922 /* */
3808 3923 typedef struct who_perm_node {
3809 3924 who_perm_t who_perm;
3810 3925 uu_avl_node_t who_avl_node;
3811 3926 } who_perm_node_t;
3812 3927
3813 3928 typedef struct fs_perm_set fs_perm_set_t;
3814 3929 /* fs permissions */
3815 3930 struct fs_perm {
3816 3931 const char *fsp_name;
3817 3932
3818 3933 uu_avl_t *fsp_sc_avl; /* sets,create */
3819 3934 uu_avl_t *fsp_uge_avl; /* user,group,everyone */
3820 3935
3821 3936 fs_perm_set_t *fsp_set; /* uplink */
3822 3937 };
3823 3938
3824 3939 /* */
3825 3940 typedef struct fs_perm_node {
3826 3941 fs_perm_t fspn_fsperm;
3827 3942 uu_avl_t *fspn_avl;
3828 3943
3829 3944 uu_list_node_t fspn_list_node;
3830 3945 } fs_perm_node_t;
3831 3946
3832 3947 /* top level structure */
3833 3948 struct fs_perm_set {
3834 3949 uu_list_pool_t *fsps_list_pool;
3835 3950 uu_list_t *fsps_list; /* list of fs_perms */
3836 3951
3837 3952 uu_avl_pool_t *fsps_named_set_avl_pool;
3838 3953 uu_avl_pool_t *fsps_who_perm_avl_pool;
3839 3954 uu_avl_pool_t *fsps_deleg_perm_avl_pool;
3840 3955 };
3841 3956
3842 3957 static inline const char *
3843 3958 deleg_perm_type(zfs_deleg_note_t note)
3844 3959 {
3845 3960 /* subcommands */
3846 3961 switch (note) {
3847 3962 /* SUBCOMMANDS */
3848 3963 /* OTHER */
3849 3964 case ZFS_DELEG_NOTE_GROUPQUOTA:
3850 3965 case ZFS_DELEG_NOTE_GROUPUSED:
3851 3966 case ZFS_DELEG_NOTE_USERPROP:
3852 3967 case ZFS_DELEG_NOTE_USERQUOTA:
3853 3968 case ZFS_DELEG_NOTE_USERUSED:
3854 3969 /* other */
3855 3970 return (gettext("other"));
3856 3971 default:
3857 3972 return (gettext("subcommand"));
3858 3973 }
3859 3974 }
3860 3975
3861 3976 static int inline
3862 3977 who_type2weight(zfs_deleg_who_type_t who_type)
3863 3978 {
3864 3979 int res;
3865 3980 switch (who_type) {
3866 3981 case ZFS_DELEG_NAMED_SET_SETS:
3867 3982 case ZFS_DELEG_NAMED_SET:
3868 3983 res = 0;
3869 3984 break;
3870 3985 case ZFS_DELEG_CREATE_SETS:
3871 3986 case ZFS_DELEG_CREATE:
3872 3987 res = 1;
3873 3988 break;
3874 3989 case ZFS_DELEG_USER_SETS:
3875 3990 case ZFS_DELEG_USER:
3876 3991 res = 2;
3877 3992 break;
3878 3993 case ZFS_DELEG_GROUP_SETS:
3879 3994 case ZFS_DELEG_GROUP:
3880 3995 res = 3;
3881 3996 break;
3882 3997 case ZFS_DELEG_EVERYONE_SETS:
3883 3998 case ZFS_DELEG_EVERYONE:
3884 3999 res = 4;
3885 4000 break;
3886 4001 default:
3887 4002 res = -1;
3888 4003 }
3889 4004
3890 4005 return (res);
3891 4006 }
3892 4007
3893 4008 /* ARGSUSED */
3894 4009 static int
3895 4010 who_perm_compare(const void *larg, const void *rarg, void *unused)
3896 4011 {
3897 4012 const who_perm_node_t *l = larg;
3898 4013 const who_perm_node_t *r = rarg;
3899 4014 zfs_deleg_who_type_t ltype = l->who_perm.who_type;
3900 4015 zfs_deleg_who_type_t rtype = r->who_perm.who_type;
3901 4016 int lweight = who_type2weight(ltype);
3902 4017 int rweight = who_type2weight(rtype);
3903 4018 int res = lweight - rweight;
3904 4019 if (res == 0)
3905 4020 res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
3906 4021 ZFS_MAX_DELEG_NAME-1);
3907 4022
3908 4023 if (res == 0)
3909 4024 return (0);
3910 4025 if (res > 0)
3911 4026 return (1);
3912 4027 else
3913 4028 return (-1);
3914 4029 }
3915 4030
3916 4031 /* ARGSUSED */
3917 4032 static int
3918 4033 deleg_perm_compare(const void *larg, const void *rarg, void *unused)
3919 4034 {
3920 4035 const deleg_perm_node_t *l = larg;
3921 4036 const deleg_perm_node_t *r = rarg;
3922 4037 int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
3923 4038 ZFS_MAX_DELEG_NAME-1);
3924 4039
3925 4040 if (res == 0)
3926 4041 return (0);
3927 4042
3928 4043 if (res > 0)
3929 4044 return (1);
3930 4045 else
3931 4046 return (-1);
3932 4047 }
3933 4048
3934 4049 static inline void
3935 4050 fs_perm_set_init(fs_perm_set_t *fspset)
3936 4051 {
3937 4052 bzero(fspset, sizeof (fs_perm_set_t));
3938 4053
3939 4054 if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
3940 4055 sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
3941 4056 NULL, UU_DEFAULT)) == NULL)
3942 4057 nomem();
3943 4058 if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
3944 4059 UU_DEFAULT)) == NULL)
3945 4060 nomem();
3946 4061
3947 4062 if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
3948 4063 "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
3949 4064 who_perm_node_t, who_avl_node), who_perm_compare,
3950 4065 UU_DEFAULT)) == NULL)
3951 4066 nomem();
3952 4067
3953 4068 if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
3954 4069 "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
3955 4070 who_perm_node_t, who_avl_node), who_perm_compare,
3956 4071 UU_DEFAULT)) == NULL)
3957 4072 nomem();
3958 4073
3959 4074 if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
3960 4075 "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
3961 4076 deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
3962 4077 == NULL)
3963 4078 nomem();
3964 4079 }
3965 4080
3966 4081 static inline void fs_perm_fini(fs_perm_t *);
3967 4082 static inline void who_perm_fini(who_perm_t *);
3968 4083
3969 4084 static inline void
3970 4085 fs_perm_set_fini(fs_perm_set_t *fspset)
3971 4086 {
3972 4087 fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
3973 4088
3974 4089 while (node != NULL) {
3975 4090 fs_perm_node_t *next_node =
3976 4091 uu_list_next(fspset->fsps_list, node);
3977 4092 fs_perm_t *fsperm = &node->fspn_fsperm;
3978 4093 fs_perm_fini(fsperm);
3979 4094 uu_list_remove(fspset->fsps_list, node);
3980 4095 free(node);
3981 4096 node = next_node;
3982 4097 }
3983 4098
3984 4099 uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
3985 4100 uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
3986 4101 uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
3987 4102 }
3988 4103
3989 4104 static inline void
3990 4105 deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
3991 4106 const char *name)
3992 4107 {
3993 4108 deleg_perm->dp_who_type = type;
3994 4109 deleg_perm->dp_name = name;
3995 4110 }
3996 4111
3997 4112 static inline void
3998 4113 who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
3999 4114 zfs_deleg_who_type_t type, const char *name)
4000 4115 {
4001 4116 uu_avl_pool_t *pool;
4002 4117 pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
4003 4118
4004 4119 bzero(who_perm, sizeof (who_perm_t));
4005 4120
4006 4121 if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
4007 4122 UU_DEFAULT)) == NULL)
4008 4123 nomem();
4009 4124
4010 4125 who_perm->who_type = type;
4011 4126 who_perm->who_name = name;
4012 4127 who_perm->who_fsperm = fsperm;
4013 4128 }
4014 4129
4015 4130 static inline void
4016 4131 who_perm_fini(who_perm_t *who_perm)
4017 4132 {
4018 4133 deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
4019 4134
4020 4135 while (node != NULL) {
4021 4136 deleg_perm_node_t *next_node =
4022 4137 uu_avl_next(who_perm->who_deleg_perm_avl, node);
4023 4138
4024 4139 uu_avl_remove(who_perm->who_deleg_perm_avl, node);
4025 4140 free(node);
4026 4141 node = next_node;
4027 4142 }
4028 4143
4029 4144 uu_avl_destroy(who_perm->who_deleg_perm_avl);
4030 4145 }
4031 4146
4032 4147 static inline void
4033 4148 fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
4034 4149 {
4035 4150 uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;
4036 4151 uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;
4037 4152
4038 4153 bzero(fsperm, sizeof (fs_perm_t));
4039 4154
4040 4155 if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
4041 4156 == NULL)
4042 4157 nomem();
4043 4158
4044 4159 if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
4045 4160 == NULL)
4046 4161 nomem();
4047 4162
4048 4163 fsperm->fsp_set = fspset;
4049 4164 fsperm->fsp_name = fsname;
4050 4165 }
4051 4166
4052 4167 static inline void
4053 4168 fs_perm_fini(fs_perm_t *fsperm)
4054 4169 {
4055 4170 who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
4056 4171 while (node != NULL) {
4057 4172 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
4058 4173 node);
4059 4174 who_perm_t *who_perm = &node->who_perm;
4060 4175 who_perm_fini(who_perm);
4061 4176 uu_avl_remove(fsperm->fsp_sc_avl, node);
4062 4177 free(node);
4063 4178 node = next_node;
4064 4179 }
4065 4180
4066 4181 node = uu_avl_first(fsperm->fsp_uge_avl);
4067 4182 while (node != NULL) {
4068 4183 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
4069 4184 node);
4070 4185 who_perm_t *who_perm = &node->who_perm;
4071 4186 who_perm_fini(who_perm);
4072 4187 uu_avl_remove(fsperm->fsp_uge_avl, node);
4073 4188 free(node);
4074 4189 node = next_node;
4075 4190 }
4076 4191
4077 4192 uu_avl_destroy(fsperm->fsp_sc_avl);
4078 4193 uu_avl_destroy(fsperm->fsp_uge_avl);
4079 4194 }
4080 4195
4081 4196 static void inline
4082 4197 set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
4083 4198 zfs_deleg_who_type_t who_type, const char *name, char locality)
4084 4199 {
4085 4200 uu_avl_index_t idx = 0;
4086 4201
4087 4202 deleg_perm_node_t *found_node = NULL;
4088 4203 deleg_perm_t *deleg_perm = &node->dpn_perm;
4089 4204
4090 4205 deleg_perm_init(deleg_perm, who_type, name);
4091 4206
4092 4207 if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4093 4208 == NULL)
4094 4209 uu_avl_insert(avl, node, idx);
4095 4210 else {
4096 4211 node = found_node;
4097 4212 deleg_perm = &node->dpn_perm;
4098 4213 }
4099 4214
4100 4215
4101 4216 switch (locality) {
4102 4217 case ZFS_DELEG_LOCAL:
4103 4218 deleg_perm->dp_local = B_TRUE;
4104 4219 break;
4105 4220 case ZFS_DELEG_DESCENDENT:
4106 4221 deleg_perm->dp_descend = B_TRUE;
4107 4222 break;
4108 4223 case ZFS_DELEG_NA:
4109 4224 break;
4110 4225 default:
4111 4226 assert(B_FALSE); /* invalid locality */
4112 4227 }
4113 4228 }
4114 4229
4115 4230 static inline int
4116 4231 parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
4117 4232 {
4118 4233 nvpair_t *nvp = NULL;
4119 4234 fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
4120 4235 uu_avl_t *avl = who_perm->who_deleg_perm_avl;
4121 4236 zfs_deleg_who_type_t who_type = who_perm->who_type;
4122 4237
4123 4238 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4124 4239 const char *name = nvpair_name(nvp);
4125 4240 data_type_t type = nvpair_type(nvp);
4126 4241 uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
4127 4242 deleg_perm_node_t *node =
4128 4243 safe_malloc(sizeof (deleg_perm_node_t));
4129 4244
4130 4245 assert(type == DATA_TYPE_BOOLEAN);
4131 4246
4132 4247 uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
4133 4248 set_deleg_perm_node(avl, node, who_type, name, locality);
4134 4249 }
4135 4250
4136 4251 return (0);
4137 4252 }
4138 4253
4139 4254 static inline int
4140 4255 parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
4141 4256 {
4142 4257 nvpair_t *nvp = NULL;
4143 4258 fs_perm_set_t *fspset = fsperm->fsp_set;
4144 4259
4145 4260 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4146 4261 nvlist_t *nvl2 = NULL;
4147 4262 const char *name = nvpair_name(nvp);
4148 4263 uu_avl_t *avl = NULL;
4149 4264 uu_avl_pool_t *avl_pool;
4150 4265 zfs_deleg_who_type_t perm_type = name[0];
4151 4266 char perm_locality = name[1];
4152 4267 const char *perm_name = name + 3;
4153 4268 boolean_t is_set = B_TRUE;
4154 4269 who_perm_t *who_perm = NULL;
4155 4270
4156 4271 assert('$' == name[2]);
4157 4272
4158 4273 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4159 4274 return (-1);
4160 4275
4161 4276 switch (perm_type) {
4162 4277 case ZFS_DELEG_CREATE:
4163 4278 case ZFS_DELEG_CREATE_SETS:
4164 4279 case ZFS_DELEG_NAMED_SET:
4165 4280 case ZFS_DELEG_NAMED_SET_SETS:
4166 4281 avl_pool = fspset->fsps_named_set_avl_pool;
4167 4282 avl = fsperm->fsp_sc_avl;
4168 4283 break;
4169 4284 case ZFS_DELEG_USER:
4170 4285 case ZFS_DELEG_USER_SETS:
4171 4286 case ZFS_DELEG_GROUP:
4172 4287 case ZFS_DELEG_GROUP_SETS:
4173 4288 case ZFS_DELEG_EVERYONE:
4174 4289 case ZFS_DELEG_EVERYONE_SETS:
4175 4290 avl_pool = fspset->fsps_who_perm_avl_pool;
4176 4291 avl = fsperm->fsp_uge_avl;
4177 4292 break;
4178 4293 }
4179 4294
4180 4295 if (is_set) {
4181 4296 who_perm_node_t *found_node = NULL;
4182 4297 who_perm_node_t *node = safe_malloc(
4183 4298 sizeof (who_perm_node_t));
4184 4299 who_perm = &node->who_perm;
4185 4300 uu_avl_index_t idx = 0;
4186 4301
4187 4302 uu_avl_node_init(node, &node->who_avl_node, avl_pool);
4188 4303 who_perm_init(who_perm, fsperm, perm_type, perm_name);
4189 4304
4190 4305 if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4191 4306 == NULL) {
4192 4307 if (avl == fsperm->fsp_uge_avl) {
4193 4308 uid_t rid = 0;
4194 4309 struct passwd *p = NULL;
4195 4310 struct group *g = NULL;
4196 4311 const char *nice_name = NULL;
4197 4312
4198 4313 switch (perm_type) {
4199 4314 case ZFS_DELEG_USER_SETS:
4200 4315 case ZFS_DELEG_USER:
4201 4316 rid = atoi(perm_name);
4202 4317 p = getpwuid(rid);
4203 4318 if (p)
4204 4319 nice_name = p->pw_name;
4205 4320 break;
4206 4321 case ZFS_DELEG_GROUP_SETS:
4207 4322 case ZFS_DELEG_GROUP:
4208 4323 rid = atoi(perm_name);
4209 4324 g = getgrgid(rid);
4210 4325 if (g)
4211 4326 nice_name = g->gr_name;
4212 4327 break;
4213 4328 }
4214 4329
4215 4330 if (nice_name != NULL)
4216 4331 (void) strlcpy(
4217 4332 node->who_perm.who_ug_name,
4218 4333 nice_name, 256);
4219 4334 }
4220 4335
4221 4336 uu_avl_insert(avl, node, idx);
4222 4337 } else {
4223 4338 node = found_node;
4224 4339 who_perm = &node->who_perm;
4225 4340 }
4226 4341 }
4227 4342
4228 4343 (void) parse_who_perm(who_perm, nvl2, perm_locality);
4229 4344 }
4230 4345
4231 4346 return (0);
4232 4347 }
4233 4348
4234 4349 static inline int
4235 4350 parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
4236 4351 {
4237 4352 nvpair_t *nvp = NULL;
4238 4353 uu_avl_index_t idx = 0;
4239 4354
4240 4355 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4241 4356 nvlist_t *nvl2 = NULL;
4242 4357 const char *fsname = nvpair_name(nvp);
4243 4358 data_type_t type = nvpair_type(nvp);
4244 4359 fs_perm_t *fsperm = NULL;
4245 4360 fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
4246 4361 if (node == NULL)
4247 4362 nomem();
4248 4363
4249 4364 fsperm = &node->fspn_fsperm;
4250 4365
4251 4366 assert(DATA_TYPE_NVLIST == type);
4252 4367
4253 4368 uu_list_node_init(node, &node->fspn_list_node,
4254 4369 fspset->fsps_list_pool);
4255 4370
4256 4371 idx = uu_list_numnodes(fspset->fsps_list);
4257 4372 fs_perm_init(fsperm, fspset, fsname);
4258 4373
4259 4374 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4260 4375 return (-1);
4261 4376
4262 4377 (void) parse_fs_perm(fsperm, nvl2);
4263 4378
4264 4379 uu_list_insert(fspset->fsps_list, node, idx);
4265 4380 }
4266 4381
4267 4382 return (0);
4268 4383 }
4269 4384
4270 4385 static inline const char *
4271 4386 deleg_perm_comment(zfs_deleg_note_t note)
4272 4387 {
4273 4388 const char *str = "";
4274 4389
4275 4390 /* subcommands */
4276 4391 switch (note) {
4277 4392 /* SUBCOMMANDS */
4278 4393 case ZFS_DELEG_NOTE_ALLOW:
4279 4394 str = gettext("Must also have the permission that is being"
4280 4395 "\n\t\t\t\tallowed");
4281 4396 break;
4282 4397 case ZFS_DELEG_NOTE_CLONE:
4283 4398 str = gettext("Must also have the 'create' ability and 'mount'"
4284 4399 "\n\t\t\t\tability in the origin file system");
4285 4400 break;
4286 4401 case ZFS_DELEG_NOTE_CREATE:
4287 4402 str = gettext("Must also have the 'mount' ability");
4288 4403 break;
4289 4404 case ZFS_DELEG_NOTE_DESTROY:
4290 4405 str = gettext("Must also have the 'mount' ability");
4291 4406 break;
4292 4407 case ZFS_DELEG_NOTE_DIFF:
4293 4408 str = gettext("Allows lookup of paths within a dataset;"
4294 4409 "\n\t\t\t\tgiven an object number. Ordinary users need this"
4295 4410 "\n\t\t\t\tin order to use zfs diff");
4296 4411 break;
4297 4412 case ZFS_DELEG_NOTE_HOLD:
4298 4413 str = gettext("Allows adding a user hold to a snapshot");
4299 4414 break;
4300 4415 case ZFS_DELEG_NOTE_MOUNT:
4301 4416 str = gettext("Allows mount/umount of ZFS datasets");
4302 4417 break;
4303 4418 case ZFS_DELEG_NOTE_PROMOTE:
4304 4419 str = gettext("Must also have the 'mount'\n\t\t\t\tand"
4305 4420 " 'promote' ability in the origin file system");
4306 4421 break;
4307 4422 case ZFS_DELEG_NOTE_RECEIVE:
4308 4423 str = gettext("Must also have the 'mount' and 'create'"
4309 4424 " ability");
4310 4425 break;
4311 4426 case ZFS_DELEG_NOTE_RELEASE:
4312 4427 str = gettext("Allows releasing a user hold which\n\t\t\t\t"
4313 4428 "might destroy the snapshot");
4314 4429 break;
4315 4430 case ZFS_DELEG_NOTE_RENAME:
4316 4431 str = gettext("Must also have the 'mount' and 'create'"
4317 4432 "\n\t\t\t\tability in the new parent");
4318 4433 break;
4319 4434 case ZFS_DELEG_NOTE_ROLLBACK:
4320 4435 str = gettext("");
4321 4436 break;
4322 4437 case ZFS_DELEG_NOTE_SEND:
4323 4438 str = gettext("");
4324 4439 break;
4325 4440 case ZFS_DELEG_NOTE_SHARE:
4326 4441 str = gettext("Allows sharing file systems over NFS or SMB"
4327 4442 "\n\t\t\t\tprotocols");
4328 4443 break;
4329 4444 case ZFS_DELEG_NOTE_SNAPSHOT:
4330 4445 str = gettext("");
4331 4446 break;
4332 4447 /*
4333 4448 * case ZFS_DELEG_NOTE_VSCAN:
4334 4449 * str = gettext("");
4335 4450 * break;
4336 4451 */
4337 4452 /* OTHER */
4338 4453 case ZFS_DELEG_NOTE_GROUPQUOTA:
4339 4454 str = gettext("Allows accessing any groupquota@... property");
4340 4455 break;
4341 4456 case ZFS_DELEG_NOTE_GROUPUSED:
4342 4457 str = gettext("Allows reading any groupused@... property");
4343 4458 break;
4344 4459 case ZFS_DELEG_NOTE_USERPROP:
4345 4460 str = gettext("Allows changing any user property");
4346 4461 break;
4347 4462 case ZFS_DELEG_NOTE_USERQUOTA:
4348 4463 str = gettext("Allows accessing any userquota@... property");
4349 4464 break;
4350 4465 case ZFS_DELEG_NOTE_USERUSED:
4351 4466 str = gettext("Allows reading any userused@... property");
4352 4467 break;
4353 4468 /* other */
4354 4469 default:
4355 4470 str = "";
4356 4471 }
4357 4472
4358 4473 return (str);
4359 4474 }
4360 4475
4361 4476 struct allow_opts {
4362 4477 boolean_t local;
4363 4478 boolean_t descend;
4364 4479 boolean_t user;
4365 4480 boolean_t group;
4366 4481 boolean_t everyone;
4367 4482 boolean_t create;
4368 4483 boolean_t set;
4369 4484 boolean_t recursive; /* unallow only */
4370 4485 boolean_t prt_usage;
4371 4486
4372 4487 boolean_t prt_perms;
4373 4488 char *who;
4374 4489 char *perms;
4375 4490 const char *dataset;
4376 4491 };
4377 4492
4378 4493 static inline int
4379 4494 prop_cmp(const void *a, const void *b)
4380 4495 {
4381 4496 const char *str1 = *(const char **)a;
4382 4497 const char *str2 = *(const char **)b;
4383 4498 return (strcmp(str1, str2));
4384 4499 }
4385 4500
4386 4501 static void
4387 4502 allow_usage(boolean_t un, boolean_t requested, const char *msg)
4388 4503 {
4389 4504 const char *opt_desc[] = {
4390 4505 "-h", gettext("show this help message and exit"),
4391 4506 "-l", gettext("set permission locally"),
4392 4507 "-d", gettext("set permission for descents"),
4393 4508 "-u", gettext("set permission for user"),
4394 4509 "-g", gettext("set permission for group"),
4395 4510 "-e", gettext("set permission for everyone"),
4396 4511 "-c", gettext("set create time permission"),
4397 4512 "-s", gettext("define permission set"),
4398 4513 /* unallow only */
4399 4514 "-r", gettext("remove permissions recursively"),
4400 4515 };
4401 4516 size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
4402 4517 size_t allow_size = unallow_size - 2;
4403 4518 const char *props[ZFS_NUM_PROPS];
4404 4519 int i;
4405 4520 size_t count = 0;
4406 4521 FILE *fp = requested ? stdout : stderr;
4407 4522 zprop_desc_t *pdtbl = zfs_prop_get_table();
4408 4523 const char *fmt = gettext("%-16s %-14s\t%s\n");
4409 4524
4410 4525 (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
4411 4526 HELP_ALLOW));
4412 4527 (void) fprintf(fp, gettext("Options:\n"));
4413 4528 for (int i = 0; i < (un ? unallow_size : allow_size); i++) {
4414 4529 const char *opt = opt_desc[i++];
4415 4530 const char *optdsc = opt_desc[i];
4416 4531 (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);
4417 4532 }
4418 4533
4419 4534 (void) fprintf(fp, gettext("\nThe following permissions are "
4420 4535 "supported:\n\n"));
4421 4536 (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
4422 4537 gettext("NOTES"));
4423 4538 for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
4424 4539 const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
4425 4540 zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
4426 4541 const char *perm_type = deleg_perm_type(perm_note);
4427 4542 const char *perm_comment = deleg_perm_comment(perm_note);
4428 4543 (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
4429 4544 }
4430 4545
4431 4546 for (i = 0; i < ZFS_NUM_PROPS; i++) {
4432 4547 zprop_desc_t *pd = &pdtbl[i];
4433 4548 if (pd->pd_visible != B_TRUE)
4434 4549 continue;
4435 4550
4436 4551 if (pd->pd_attr == PROP_READONLY)
4437 4552 continue;
4438 4553
4439 4554 props[count++] = pd->pd_name;
4440 4555 }
4441 4556 props[count] = NULL;
4442 4557
4443 4558 qsort(props, count, sizeof (char *), prop_cmp);
4444 4559
4445 4560 for (i = 0; i < count; i++)
4446 4561 (void) fprintf(fp, fmt, props[i], gettext("property"), "");
4447 4562
4448 4563 if (msg != NULL)
4449 4564 (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
4450 4565
4451 4566 exit(requested ? 0 : 2);
4452 4567 }
4453 4568
4454 4569 static inline const char *
4455 4570 munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
4456 4571 char **permsp)
4457 4572 {
4458 4573 if (un && argc == expected_argc - 1)
4459 4574 *permsp = NULL;
4460 4575 else if (argc == expected_argc)
4461 4576 *permsp = argv[argc - 2];
4462 4577 else
4463 4578 allow_usage(un, B_FALSE,
4464 4579 gettext("wrong number of parameters\n"));
4465 4580
4466 4581 return (argv[argc - 1]);
4467 4582 }
4468 4583
4469 4584 static void
4470 4585 parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
4471 4586 {
4472 4587 int uge_sum = opts->user + opts->group + opts->everyone;
4473 4588 int csuge_sum = opts->create + opts->set + uge_sum;
4474 4589 int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
4475 4590 int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
4476 4591
4477 4592 if (uge_sum > 1)
4478 4593 allow_usage(un, B_FALSE,
4479 4594 gettext("-u, -g, and -e are mutually exclusive\n"));
4480 4595
4481 4596 if (opts->prt_usage)
4482 4597 if (argc == 0 && all_sum == 0)
4483 4598 allow_usage(un, B_TRUE, NULL);
4484 4599 else
4485 4600 usage(B_FALSE);
4486 4601
4487 4602 if (opts->set) {
4488 4603 if (csuge_sum > 1)
4489 4604 allow_usage(un, B_FALSE,
4490 4605 gettext("invalid options combined with -s\n"));
4491 4606
4492 4607 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4493 4608 if (argv[0][0] != '@')
4494 4609 allow_usage(un, B_FALSE,
4495 4610 gettext("invalid set name: missing '@' prefix\n"));
4496 4611 opts->who = argv[0];
4497 4612 } else if (opts->create) {
4498 4613 if (ldcsuge_sum > 1)
4499 4614 allow_usage(un, B_FALSE,
4500 4615 gettext("invalid options combined with -c\n"));
4501 4616 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4502 4617 } else if (opts->everyone) {
4503 4618 if (csuge_sum > 1)
4504 4619 allow_usage(un, B_FALSE,
4505 4620 gettext("invalid options combined with -e\n"));
4506 4621 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4507 4622 } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
4508 4623 == 0) {
4509 4624 opts->everyone = B_TRUE;
4510 4625 argc--;
4511 4626 argv++;
4512 4627 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4513 4628 } else if (argc == 1 && !un) {
4514 4629 opts->prt_perms = B_TRUE;
4515 4630 opts->dataset = argv[argc-1];
4516 4631 } else {
4517 4632 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4518 4633 opts->who = argv[0];
4519 4634 }
4520 4635
4521 4636 if (!opts->local && !opts->descend) {
4522 4637 opts->local = B_TRUE;
4523 4638 opts->descend = B_TRUE;
4524 4639 }
4525 4640 }
4526 4641
4527 4642 static void
4528 4643 store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
4529 4644 const char *who, char *perms, nvlist_t *top_nvl)
4530 4645 {
4531 4646 int i;
4532 4647 char ld[2] = { '\0', '\0' };
4533 4648 char who_buf[ZFS_MAXNAMELEN+32];
4534 4649 char base_type;
4535 4650 char set_type;
4536 4651 nvlist_t *base_nvl = NULL;
4537 4652 nvlist_t *set_nvl = NULL;
4538 4653 nvlist_t *nvl;
4539 4654
4540 4655 if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
4541 4656 nomem();
4542 4657 if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)
4543 4658 nomem();
4544 4659
4545 4660 switch (type) {
4546 4661 case ZFS_DELEG_NAMED_SET_SETS:
4547 4662 case ZFS_DELEG_NAMED_SET:
4548 4663 set_type = ZFS_DELEG_NAMED_SET_SETS;
4549 4664 base_type = ZFS_DELEG_NAMED_SET;
4550 4665 ld[0] = ZFS_DELEG_NA;
4551 4666 break;
4552 4667 case ZFS_DELEG_CREATE_SETS:
4553 4668 case ZFS_DELEG_CREATE:
4554 4669 set_type = ZFS_DELEG_CREATE_SETS;
4555 4670 base_type = ZFS_DELEG_CREATE;
4556 4671 ld[0] = ZFS_DELEG_NA;
4557 4672 break;
4558 4673 case ZFS_DELEG_USER_SETS:
4559 4674 case ZFS_DELEG_USER:
4560 4675 set_type = ZFS_DELEG_USER_SETS;
4561 4676 base_type = ZFS_DELEG_USER;
4562 4677 if (local)
4563 4678 ld[0] = ZFS_DELEG_LOCAL;
4564 4679 if (descend)
4565 4680 ld[1] = ZFS_DELEG_DESCENDENT;
4566 4681 break;
4567 4682 case ZFS_DELEG_GROUP_SETS:
4568 4683 case ZFS_DELEG_GROUP:
4569 4684 set_type = ZFS_DELEG_GROUP_SETS;
4570 4685 base_type = ZFS_DELEG_GROUP;
4571 4686 if (local)
4572 4687 ld[0] = ZFS_DELEG_LOCAL;
4573 4688 if (descend)
4574 4689 ld[1] = ZFS_DELEG_DESCENDENT;
4575 4690 break;
4576 4691 case ZFS_DELEG_EVERYONE_SETS:
4577 4692 case ZFS_DELEG_EVERYONE:
4578 4693 set_type = ZFS_DELEG_EVERYONE_SETS;
4579 4694 base_type = ZFS_DELEG_EVERYONE;
4580 4695 if (local)
4581 4696 ld[0] = ZFS_DELEG_LOCAL;
4582 4697 if (descend)
4583 4698 ld[1] = ZFS_DELEG_DESCENDENT;
4584 4699 }
4585 4700
4586 4701 if (perms != NULL) {
4587 4702 char *curr = perms;
4588 4703 char *end = curr + strlen(perms);
4589 4704
4590 4705 while (curr < end) {
4591 4706 char *delim = strchr(curr, ',');
4592 4707 if (delim == NULL)
4593 4708 delim = end;
4594 4709 else
4595 4710 *delim = '\0';
4596 4711
4597 4712 if (curr[0] == '@')
4598 4713 nvl = set_nvl;
4599 4714 else
4600 4715 nvl = base_nvl;
4601 4716
4602 4717 (void) nvlist_add_boolean(nvl, curr);
4603 4718 if (delim != end)
4604 4719 *delim = ',';
4605 4720 curr = delim + 1;
4606 4721 }
4607 4722
4608 4723 for (i = 0; i < 2; i++) {
4609 4724 char locality = ld[i];
4610 4725 if (locality == 0)
4611 4726 continue;
4612 4727
4613 4728 if (!nvlist_empty(base_nvl)) {
4614 4729 if (who != NULL)
4615 4730 (void) snprintf(who_buf,
4616 4731 sizeof (who_buf), "%c%c$%s",
4617 4732 base_type, locality, who);
4618 4733 else
4619 4734 (void) snprintf(who_buf,
4620 4735 sizeof (who_buf), "%c%c$",
4621 4736 base_type, locality);
4622 4737
4623 4738 (void) nvlist_add_nvlist(top_nvl, who_buf,
4624 4739 base_nvl);
4625 4740 }
4626 4741
4627 4742
4628 4743 if (!nvlist_empty(set_nvl)) {
4629 4744 if (who != NULL)
4630 4745 (void) snprintf(who_buf,
4631 4746 sizeof (who_buf), "%c%c$%s",
4632 4747 set_type, locality, who);
4633 4748 else
4634 4749 (void) snprintf(who_buf,
4635 4750 sizeof (who_buf), "%c%c$",
4636 4751 set_type, locality);
4637 4752
4638 4753 (void) nvlist_add_nvlist(top_nvl, who_buf,
4639 4754 set_nvl);
4640 4755 }
4641 4756 }
4642 4757 } else {
4643 4758 for (i = 0; i < 2; i++) {
4644 4759 char locality = ld[i];
4645 4760 if (locality == 0)
4646 4761 continue;
4647 4762
4648 4763 if (who != NULL)
4649 4764 (void) snprintf(who_buf, sizeof (who_buf),
4650 4765 "%c%c$%s", base_type, locality, who);
4651 4766 else
4652 4767 (void) snprintf(who_buf, sizeof (who_buf),
4653 4768 "%c%c$", base_type, locality);
4654 4769 (void) nvlist_add_boolean(top_nvl, who_buf);
4655 4770
4656 4771 if (who != NULL)
4657 4772 (void) snprintf(who_buf, sizeof (who_buf),
4658 4773 "%c%c$%s", set_type, locality, who);
4659 4774 else
4660 4775 (void) snprintf(who_buf, sizeof (who_buf),
4661 4776 "%c%c$", set_type, locality);
4662 4777 (void) nvlist_add_boolean(top_nvl, who_buf);
4663 4778 }
4664 4779 }
4665 4780 }
4666 4781
4667 4782 static int
4668 4783 construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
4669 4784 {
4670 4785 if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
4671 4786 nomem();
4672 4787
4673 4788 if (opts->set) {
4674 4789 store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
4675 4790 opts->descend, opts->who, opts->perms, *nvlp);
4676 4791 } else if (opts->create) {
4677 4792 store_allow_perm(ZFS_DELEG_CREATE, opts->local,
4678 4793 opts->descend, NULL, opts->perms, *nvlp);
4679 4794 } else if (opts->everyone) {
4680 4795 store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
4681 4796 opts->descend, NULL, opts->perms, *nvlp);
4682 4797 } else {
4683 4798 char *curr = opts->who;
4684 4799 char *end = curr + strlen(curr);
4685 4800
4686 4801 while (curr < end) {
4687 4802 const char *who;
4688 4803 zfs_deleg_who_type_t who_type;
4689 4804 char *endch;
4690 4805 char *delim = strchr(curr, ',');
4691 4806 char errbuf[256];
4692 4807 char id[64];
4693 4808 struct passwd *p = NULL;
4694 4809 struct group *g = NULL;
4695 4810
4696 4811 uid_t rid;
4697 4812 if (delim == NULL)
4698 4813 delim = end;
4699 4814 else
4700 4815 *delim = '\0';
4701 4816
4702 4817 rid = (uid_t)strtol(curr, &endch, 0);
4703 4818 if (opts->user) {
4704 4819 who_type = ZFS_DELEG_USER;
4705 4820 if (*endch != '\0')
4706 4821 p = getpwnam(curr);
4707 4822 else
4708 4823 p = getpwuid(rid);
4709 4824
4710 4825 if (p != NULL)
4711 4826 rid = p->pw_uid;
4712 4827 else {
4713 4828 (void) snprintf(errbuf, 256, gettext(
4714 4829 "invalid user %s"), curr);
4715 4830 allow_usage(un, B_TRUE, errbuf);
4716 4831 }
4717 4832 } else if (opts->group) {
4718 4833 who_type = ZFS_DELEG_GROUP;
4719 4834 if (*endch != '\0')
4720 4835 g = getgrnam(curr);
4721 4836 else
4722 4837 g = getgrgid(rid);
4723 4838
4724 4839 if (g != NULL)
4725 4840 rid = g->gr_gid;
4726 4841 else {
4727 4842 (void) snprintf(errbuf, 256, gettext(
4728 4843 "invalid group %s"), curr);
4729 4844 allow_usage(un, B_TRUE, errbuf);
4730 4845 }
4731 4846 } else {
4732 4847 if (*endch != '\0') {
4733 4848 p = getpwnam(curr);
4734 4849 } else {
4735 4850 p = getpwuid(rid);
4736 4851 }
4737 4852
4738 4853 if (p == NULL)
4739 4854 if (*endch != '\0') {
4740 4855 g = getgrnam(curr);
4741 4856 } else {
4742 4857 g = getgrgid(rid);
4743 4858 }
4744 4859
4745 4860 if (p != NULL) {
4746 4861 who_type = ZFS_DELEG_USER;
4747 4862 rid = p->pw_uid;
4748 4863 } else if (g != NULL) {
4749 4864 who_type = ZFS_DELEG_GROUP;
4750 4865 rid = g->gr_gid;
4751 4866 } else {
4752 4867 (void) snprintf(errbuf, 256, gettext(
4753 4868 "invalid user/group %s"), curr);
4754 4869 allow_usage(un, B_TRUE, errbuf);
4755 4870 }
4756 4871 }
4757 4872
4758 4873 (void) sprintf(id, "%u", rid);
4759 4874 who = id;
4760 4875
4761 4876 store_allow_perm(who_type, opts->local,
4762 4877 opts->descend, who, opts->perms, *nvlp);
4763 4878 curr = delim + 1;
4764 4879 }
4765 4880 }
4766 4881
4767 4882 return (0);
4768 4883 }
4769 4884
4770 4885 static void
4771 4886 print_set_creat_perms(uu_avl_t *who_avl)
4772 4887 {
4773 4888 const char *sc_title[] = {
4774 4889 gettext("Permission sets:\n"),
4775 4890 gettext("Create time permissions:\n"),
4776 4891 NULL
4777 4892 };
4778 4893 const char **title_ptr = sc_title;
4779 4894 who_perm_node_t *who_node = NULL;
4780 4895 int prev_weight = -1;
4781 4896
4782 4897 for (who_node = uu_avl_first(who_avl); who_node != NULL;
4783 4898 who_node = uu_avl_next(who_avl, who_node)) {
4784 4899 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
4785 4900 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
4786 4901 const char *who_name = who_node->who_perm.who_name;
4787 4902 int weight = who_type2weight(who_type);
4788 4903 boolean_t first = B_TRUE;
4789 4904 deleg_perm_node_t *deleg_node;
4790 4905
4791 4906 if (prev_weight != weight) {
4792 4907 (void) printf(*title_ptr++);
4793 4908 prev_weight = weight;
4794 4909 }
4795 4910
4796 4911 if (who_name == NULL || strnlen(who_name, 1) == 0)
4797 4912 (void) printf("\t");
4798 4913 else
4799 4914 (void) printf("\t%s ", who_name);
4800 4915
4801 4916 for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
4802 4917 deleg_node = uu_avl_next(avl, deleg_node)) {
4803 4918 if (first) {
4804 4919 (void) printf("%s",
4805 4920 deleg_node->dpn_perm.dp_name);
4806 4921 first = B_FALSE;
4807 4922 } else
4808 4923 (void) printf(",%s",
4809 4924 deleg_node->dpn_perm.dp_name);
4810 4925 }
4811 4926
4812 4927 (void) printf("\n");
4813 4928 }
4814 4929 }
4815 4930
4816 4931 static void inline
4817 4932 print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
4818 4933 const char *title)
4819 4934 {
4820 4935 who_perm_node_t *who_node = NULL;
4821 4936 boolean_t prt_title = B_TRUE;
4822 4937 uu_avl_walk_t *walk;
4823 4938
4824 4939 if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
4825 4940 nomem();
4826 4941
4827 4942 while ((who_node = uu_avl_walk_next(walk)) != NULL) {
4828 4943 const char *who_name = who_node->who_perm.who_name;
4829 4944 const char *nice_who_name = who_node->who_perm.who_ug_name;
4830 4945 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
4831 4946 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
4832 4947 char delim = ' ';
4833 4948 deleg_perm_node_t *deleg_node;
4834 4949 boolean_t prt_who = B_TRUE;
4835 4950
4836 4951 for (deleg_node = uu_avl_first(avl);
4837 4952 deleg_node != NULL;
4838 4953 deleg_node = uu_avl_next(avl, deleg_node)) {
4839 4954 if (local != deleg_node->dpn_perm.dp_local ||
4840 4955 descend != deleg_node->dpn_perm.dp_descend)
4841 4956 continue;
4842 4957
4843 4958 if (prt_who) {
4844 4959 const char *who = NULL;
4845 4960 if (prt_title) {
4846 4961 prt_title = B_FALSE;
4847 4962 (void) printf(title);
4848 4963 }
4849 4964
4850 4965 switch (who_type) {
4851 4966 case ZFS_DELEG_USER_SETS:
4852 4967 case ZFS_DELEG_USER:
4853 4968 who = gettext("user");
4854 4969 if (nice_who_name)
4855 4970 who_name = nice_who_name;
4856 4971 break;
4857 4972 case ZFS_DELEG_GROUP_SETS:
4858 4973 case ZFS_DELEG_GROUP:
4859 4974 who = gettext("group");
4860 4975 if (nice_who_name)
4861 4976 who_name = nice_who_name;
4862 4977 break;
4863 4978 case ZFS_DELEG_EVERYONE_SETS:
4864 4979 case ZFS_DELEG_EVERYONE:
4865 4980 who = gettext("everyone");
4866 4981 who_name = NULL;
4867 4982 }
4868 4983
4869 4984 prt_who = B_FALSE;
4870 4985 if (who_name == NULL)
4871 4986 (void) printf("\t%s", who);
4872 4987 else
4873 4988 (void) printf("\t%s %s", who, who_name);
4874 4989 }
4875 4990
4876 4991 (void) printf("%c%s", delim,
4877 4992 deleg_node->dpn_perm.dp_name);
4878 4993 delim = ',';
4879 4994 }
4880 4995
4881 4996 if (!prt_who)
4882 4997 (void) printf("\n");
4883 4998 }
4884 4999
4885 5000 uu_avl_walk_end(walk);
4886 5001 }
4887 5002
4888 5003 static void
4889 5004 print_fs_perms(fs_perm_set_t *fspset)
4890 5005 {
4891 5006 fs_perm_node_t *node = NULL;
4892 5007 char buf[ZFS_MAXNAMELEN+32];
4893 5008 const char *dsname = buf;
4894 5009
4895 5010 for (node = uu_list_first(fspset->fsps_list); node != NULL;
4896 5011 node = uu_list_next(fspset->fsps_list, node)) {
4897 5012 uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
4898 5013 uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
4899 5014 int left = 0;
4900 5015
4901 5016 (void) snprintf(buf, ZFS_MAXNAMELEN+32,
4902 5017 gettext("---- Permissions on %s "),
4903 5018 node->fspn_fsperm.fsp_name);
4904 5019 (void) printf(dsname);
4905 5020 left = 70 - strlen(buf);
4906 5021 while (left-- > 0)
4907 5022 (void) printf("-");
4908 5023 (void) printf("\n");
4909 5024
4910 5025 print_set_creat_perms(sc_avl);
4911 5026 print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
4912 5027 gettext("Local permissions:\n"));
4913 5028 print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
4914 5029 gettext("Descendent permissions:\n"));
4915 5030 print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
4916 5031 gettext("Local+Descendent permissions:\n"));
4917 5032 }
4918 5033 }
4919 5034
4920 5035 static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
4921 5036
4922 5037 struct deleg_perms {
4923 5038 boolean_t un;
4924 5039 nvlist_t *nvl;
4925 5040 };
4926 5041
4927 5042 static int
4928 5043 set_deleg_perms(zfs_handle_t *zhp, void *data)
4929 5044 {
4930 5045 struct deleg_perms *perms = (struct deleg_perms *)data;
4931 5046 zfs_type_t zfs_type = zfs_get_type(zhp);
4932 5047
4933 5048 if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
4934 5049 return (0);
4935 5050
4936 5051 return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
4937 5052 }
4938 5053
4939 5054 static int
4940 5055 zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
4941 5056 {
4942 5057 zfs_handle_t *zhp;
4943 5058 nvlist_t *perm_nvl = NULL;
4944 5059 nvlist_t *update_perm_nvl = NULL;
4945 5060 int error = 1;
4946 5061 int c;
4947 5062 struct allow_opts opts = { 0 };
4948 5063
4949 5064 const char *optstr = un ? "ldugecsrh" : "ldugecsh";
4950 5065
4951 5066 /* check opts */
4952 5067 while ((c = getopt(argc, argv, optstr)) != -1) {
4953 5068 switch (c) {
4954 5069 case 'l':
4955 5070 opts.local = B_TRUE;
4956 5071 break;
4957 5072 case 'd':
4958 5073 opts.descend = B_TRUE;
4959 5074 break;
4960 5075 case 'u':
4961 5076 opts.user = B_TRUE;
4962 5077 break;
4963 5078 case 'g':
4964 5079 opts.group = B_TRUE;
4965 5080 break;
4966 5081 case 'e':
4967 5082 opts.everyone = B_TRUE;
4968 5083 break;
4969 5084 case 's':
4970 5085 opts.set = B_TRUE;
4971 5086 break;
4972 5087 case 'c':
4973 5088 opts.create = B_TRUE;
4974 5089 break;
4975 5090 case 'r':
4976 5091 opts.recursive = B_TRUE;
4977 5092 break;
4978 5093 case ':':
4979 5094 (void) fprintf(stderr, gettext("missing argument for "
4980 5095 "'%c' option\n"), optopt);
4981 5096 usage(B_FALSE);
4982 5097 break;
4983 5098 case 'h':
4984 5099 opts.prt_usage = B_TRUE;
4985 5100 break;
4986 5101 case '?':
4987 5102 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
4988 5103 optopt);
4989 5104 usage(B_FALSE);
4990 5105 }
4991 5106 }
4992 5107
4993 5108 argc -= optind;
4994 5109 argv += optind;
4995 5110
4996 5111 /* check arguments */
4997 5112 parse_allow_args(argc, argv, un, &opts);
4998 5113
4999 5114 /* try to open the dataset */
5000 5115 if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
5001 5116 ZFS_TYPE_VOLUME)) == NULL) {
5002 5117 (void) fprintf(stderr, "Failed to open dataset: %s\n",
5003 5118 opts.dataset);
5004 5119 return (-1);
5005 5120 }
5006 5121
5007 5122 if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
5008 5123 goto cleanup2;
5009 5124
5010 5125 fs_perm_set_init(&fs_perm_set);
5011 5126 if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
5012 5127 (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
5013 5128 goto cleanup1;
5014 5129 }
5015 5130
5016 5131 if (opts.prt_perms)
5017 5132 print_fs_perms(&fs_perm_set);
5018 5133 else {
5019 5134 (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
5020 5135 if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
5021 5136 goto cleanup0;
5022 5137
5023 5138 if (un && opts.recursive) {
5024 5139 struct deleg_perms data = { un, update_perm_nvl };
5025 5140 if (zfs_iter_filesystems(zhp, set_deleg_perms,
5026 5141 &data) != 0)
5027 5142 goto cleanup0;
5028 5143 }
5029 5144 }
5030 5145
5031 5146 error = 0;
5032 5147
5033 5148 cleanup0:
5034 5149 nvlist_free(perm_nvl);
5035 5150 if (update_perm_nvl != NULL)
5036 5151 nvlist_free(update_perm_nvl);
5037 5152 cleanup1:
5038 5153 fs_perm_set_fini(&fs_perm_set);
5039 5154 cleanup2:
5040 5155 zfs_close(zhp);
5041 5156
5042 5157 return (error);
5043 5158 }
5044 5159
5045 5160 /*
5046 5161 * zfs allow [-r] [-t] <tag> <snap> ...
5047 5162 *
5048 5163 * -r Recursively hold
5049 5164 * -t Temporary hold (hidden option)
5050 5165 *
5051 5166 * Apply a user-hold with the given tag to the list of snapshots.
5052 5167 */
5053 5168 static int
5054 5169 zfs_do_allow(int argc, char **argv)
5055 5170 {
5056 5171 return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
5057 5172 }
5058 5173
5059 5174 /*
5060 5175 * zfs unallow [-r] [-t] <tag> <snap> ...
5061 5176 *
5062 5177 * -r Recursively hold
5063 5178 * -t Temporary hold (hidden option)
5064 5179 *
5065 5180 * Apply a user-hold with the given tag to the list of snapshots.
5066 5181 */
5067 5182 static int
5068 5183 zfs_do_unallow(int argc, char **argv)
5069 5184 {
5070 5185 return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
5071 5186 }
5072 5187
5073 5188 static int
5074 5189 zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
5075 5190 {
5076 5191 int errors = 0;
5077 5192 int i;
5078 5193 const char *tag;
5079 5194 boolean_t recursive = B_FALSE;
5080 5195 boolean_t temphold = B_FALSE;
5081 5196 const char *opts = holding ? "rt" : "r";
5082 5197 int c;
5083 5198
5084 5199 /* check options */
5085 5200 while ((c = getopt(argc, argv, opts)) != -1) {
5086 5201 switch (c) {
5087 5202 case 'r':
5088 5203 recursive = B_TRUE;
5089 5204 break;
5090 5205 case 't':
5091 5206 temphold = B_TRUE;
5092 5207 break;
5093 5208 case '?':
5094 5209 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5095 5210 optopt);
5096 5211 usage(B_FALSE);
5097 5212 }
5098 5213 }
5099 5214
5100 5215 argc -= optind;
5101 5216 argv += optind;
5102 5217
5103 5218 /* check number of arguments */
5104 5219 if (argc < 2)
5105 5220 usage(B_FALSE);
5106 5221
5107 5222 tag = argv[0];
5108 5223 --argc;
5109 5224 ++argv;
5110 5225
5111 5226 if (holding && tag[0] == '.') {
5112 5227 /* tags starting with '.' are reserved for libzfs */
5113 5228 (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
5114 5229 usage(B_FALSE);
5115 5230 }
5116 5231
5117 5232 for (i = 0; i < argc; ++i) {
5118 5233 zfs_handle_t *zhp;
5119 5234 char parent[ZFS_MAXNAMELEN];
5120 5235 const char *delim;
5121 5236 char *path = argv[i];
5122 5237
5123 5238 delim = strchr(path, '@');
5124 5239 if (delim == NULL) {
5125 5240 (void) fprintf(stderr,
5126 5241 gettext("'%s' is not a snapshot\n"), path);
5127 5242 ++errors;
5128 5243 continue;
5129 5244 }
5130 5245 (void) strncpy(parent, path, delim - path);
5131 5246 parent[delim - path] = '\0';
5132 5247
5133 5248 zhp = zfs_open(g_zfs, parent,
5134 5249 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
5135 5250 if (zhp == NULL) {
5136 5251 ++errors;
5137 5252 continue;
5138 5253 }
5139 5254 if (holding) {
5140 5255 if (zfs_hold(zhp, delim+1, tag, recursive,
5141 5256 temphold, B_FALSE, -1, 0, 0) != 0)
5142 5257 ++errors;
5143 5258 } else {
5144 5259 if (zfs_release(zhp, delim+1, tag, recursive) != 0)
5145 5260 ++errors;
5146 5261 }
5147 5262 zfs_close(zhp);
5148 5263 }
5149 5264
5150 5265 return (errors != 0);
5151 5266 }
5152 5267
5153 5268 /*
5154 5269 * zfs hold [-r] [-t] <tag> <snap> ...
5155 5270 *
5156 5271 * -r Recursively hold
5157 5272 * -t Temporary hold (hidden option)
5158 5273 *
5159 5274 * Apply a user-hold with the given tag to the list of snapshots.
5160 5275 */
5161 5276 static int
5162 5277 zfs_do_hold(int argc, char **argv)
5163 5278 {
5164 5279 return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5165 5280 }
5166 5281
5167 5282 /*
5168 5283 * zfs release [-r] <tag> <snap> ...
5169 5284 *
5170 5285 * -r Recursively release
5171 5286 *
5172 5287 * Release a user-hold with the given tag from the list of snapshots.
5173 5288 */
5174 5289 static int
5175 5290 zfs_do_release(int argc, char **argv)
5176 5291 {
5177 5292 return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5178 5293 }
5179 5294
5180 5295 typedef struct holds_cbdata {
5181 5296 boolean_t cb_recursive;
5182 5297 const char *cb_snapname;
5183 5298 nvlist_t **cb_nvlp;
5184 5299 size_t cb_max_namelen;
5185 5300 size_t cb_max_taglen;
5186 5301 } holds_cbdata_t;
5187 5302
5188 5303 #define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
5189 5304 #define DATETIME_BUF_LEN (32)
5190 5305 /*
5191 5306 *
5192 5307 */
5193 5308 static void
5194 5309 print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
5195 5310 {
5196 5311 int i;
5197 5312 nvpair_t *nvp = NULL;
5198 5313 char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
5199 5314 const char *col;
5200 5315
5201 5316 if (!scripted) {
5202 5317 for (i = 0; i < 3; i++) {
5203 5318 col = gettext(hdr_cols[i]);
5204 5319 if (i < 2)
5205 5320 (void) printf("%-*s ", i ? tagwidth : nwidth,
5206 5321 col);
5207 5322 else
5208 5323 (void) printf("%s\n", col);
5209 5324 }
5210 5325 }
5211 5326
5212 5327 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5213 5328 char *zname = nvpair_name(nvp);
5214 5329 nvlist_t *nvl2;
5215 5330 nvpair_t *nvp2 = NULL;
5216 5331 (void) nvpair_value_nvlist(nvp, &nvl2);
5217 5332 while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
5218 5333 char tsbuf[DATETIME_BUF_LEN];
5219 5334 char *tagname = nvpair_name(nvp2);
5220 5335 uint64_t val = 0;
5221 5336 time_t time;
5222 5337 struct tm t;
5223 5338 char sep = scripted ? '\t' : ' ';
5224 5339 size_t sepnum = scripted ? 1 : 2;
5225 5340
5226 5341 (void) nvpair_value_uint64(nvp2, &val);
5227 5342 time = (time_t)val;
5228 5343 (void) localtime_r(&time, &t);
5229 5344 (void) strftime(tsbuf, DATETIME_BUF_LEN,
5230 5345 gettext(STRFTIME_FMT_STR), &t);
5231 5346
5232 5347 (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
5233 5348 sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
5234 5349 }
5235 5350 }
5236 5351 }
5237 5352
5238 5353 /*
5239 5354 * Generic callback function to list a dataset or snapshot.
5240 5355 */
5241 5356 static int
5242 5357 holds_callback(zfs_handle_t *zhp, void *data)
5243 5358 {
5244 5359 holds_cbdata_t *cbp = data;
5245 5360 nvlist_t *top_nvl = *cbp->cb_nvlp;
5246 5361 nvlist_t *nvl = NULL;
5247 5362 nvpair_t *nvp = NULL;
5248 5363 const char *zname = zfs_get_name(zhp);
5249 5364 size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
5250 5365
5251 5366 if (cbp->cb_recursive) {
5252 5367 const char *snapname;
5253 5368 char *delim = strchr(zname, '@');
5254 5369 if (delim == NULL)
5255 5370 return (0);
5256 5371
5257 5372 snapname = delim + 1;
5258 5373 if (strcmp(cbp->cb_snapname, snapname))
5259 5374 return (0);
5260 5375 }
5261 5376
5262 5377 if (zfs_get_holds(zhp, &nvl) != 0)
5263 5378 return (-1);
5264 5379
5265 5380 if (znamelen > cbp->cb_max_namelen)
5266 5381 cbp->cb_max_namelen = znamelen;
5267 5382
5268 5383 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5269 5384 const char *tag = nvpair_name(nvp);
5270 5385 size_t taglen = strnlen(tag, MAXNAMELEN);
5271 5386 if (taglen > cbp->cb_max_taglen)
5272 5387 cbp->cb_max_taglen = taglen;
5273 5388 }
5274 5389
5275 5390 return (nvlist_add_nvlist(top_nvl, zname, nvl));
5276 5391 }
5277 5392
5278 5393 /*
5279 5394 * zfs holds [-r] <snap> ...
5280 5395 *
5281 5396 * -r Recursively hold
5282 5397 */
5283 5398 static int
5284 5399 zfs_do_holds(int argc, char **argv)
5285 5400 {
5286 5401 int errors = 0;
5287 5402 int c;
5288 5403 int i;
5289 5404 boolean_t scripted = B_FALSE;
5290 5405 boolean_t recursive = B_FALSE;
5291 5406 const char *opts = "rH";
5292 5407 nvlist_t *nvl;
5293 5408
5294 5409 int types = ZFS_TYPE_SNAPSHOT;
5295 5410 holds_cbdata_t cb = { 0 };
5296 5411
5297 5412 int limit = 0;
5298 5413 int ret = 0;
5299 5414 int flags = 0;
5300 5415
5301 5416 /* check options */
5302 5417 while ((c = getopt(argc, argv, opts)) != -1) {
5303 5418 switch (c) {
5304 5419 case 'r':
5305 5420 recursive = B_TRUE;
5306 5421 break;
5307 5422 case 'H':
5308 5423 scripted = B_TRUE;
5309 5424 break;
5310 5425 case '?':
5311 5426 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5312 5427 optopt);
5313 5428 usage(B_FALSE);
5314 5429 }
5315 5430 }
5316 5431
5317 5432 if (recursive) {
5318 5433 types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
5319 5434 flags |= ZFS_ITER_RECURSE;
5320 5435 }
5321 5436
5322 5437 argc -= optind;
5323 5438 argv += optind;
5324 5439
5325 5440 /* check number of arguments */
5326 5441 if (argc < 1)
5327 5442 usage(B_FALSE);
5328 5443
5329 5444 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
5330 5445 nomem();
5331 5446
5332 5447 for (i = 0; i < argc; ++i) {
5333 5448 char *snapshot = argv[i];
5334 5449 const char *delim;
5335 5450 const char *snapname;
5336 5451
5337 5452 delim = strchr(snapshot, '@');
5338 5453 if (delim == NULL) {
5339 5454 (void) fprintf(stderr,
5340 5455 gettext("'%s' is not a snapshot\n"), snapshot);
5341 5456 ++errors;
5342 5457 continue;
5343 5458 }
5344 5459 snapname = delim + 1;
5345 5460 if (recursive)
5346 5461 snapshot[delim - snapshot] = '\0';
5347 5462
5348 5463 cb.cb_recursive = recursive;
5349 5464 cb.cb_snapname = snapname;
5350 5465 cb.cb_nvlp = &nvl;
5351 5466
5352 5467 /*
5353 5468 * 1. collect holds data, set format options
5354 5469 */
5355 5470 ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
5356 5471 holds_callback, &cb);
5357 5472 if (ret != 0)
5358 5473 ++errors;
5359 5474 }
5360 5475
5361 5476 /*
5362 5477 * 2. print holds data
5363 5478 */
5364 5479 print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5365 5480
5366 5481 if (nvlist_empty(nvl))
5367 5482 (void) printf(gettext("no datasets available\n"));
5368 5483
5369 5484 nvlist_free(nvl);
5370 5485
5371 5486 return (0 != errors);
5372 5487 }
5373 5488
5374 5489 #define CHECK_SPINNER 30
5375 5490 #define SPINNER_TIME 3 /* seconds */
5376 5491 #define MOUNT_TIME 5 /* seconds */
5377 5492
5378 5493 static int
5379 5494 get_one_dataset(zfs_handle_t *zhp, void *data)
5380 5495 {
5381 5496 static char *spin[] = { "-", "\\", "|", "/" };
5382 5497 static int spinval = 0;
5383 5498 static int spincheck = 0;
5384 5499 static time_t last_spin_time = (time_t)0;
5385 5500 get_all_cb_t *cbp = data;
5386 5501 zfs_type_t type = zfs_get_type(zhp);
5387 5502
5388 5503 if (cbp->cb_verbose) {
5389 5504 if (--spincheck < 0) {
5390 5505 time_t now = time(NULL);
5391 5506 if (last_spin_time + SPINNER_TIME < now) {
5392 5507 update_progress(spin[spinval++ % 4]);
5393 5508 last_spin_time = now;
5394 5509 }
5395 5510 spincheck = CHECK_SPINNER;
5396 5511 }
5397 5512 }
5398 5513
5399 5514 /*
5400 5515 * Interate over any nested datasets.
5401 5516 */
5402 5517 if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
5403 5518 zfs_close(zhp);
5404 5519 return (1);
5405 5520 }
5406 5521
5407 5522 /*
5408 5523 * Skip any datasets whose type does not match.
5409 5524 */
5410 5525 if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
5411 5526 zfs_close(zhp);
5412 5527 return (0);
5413 5528 }
5414 5529 libzfs_add_handle(cbp, zhp);
5415 5530 assert(cbp->cb_used <= cbp->cb_alloc);
5416 5531
5417 5532 return (0);
5418 5533 }
5419 5534
5420 5535 static void
5421 5536 get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
5422 5537 {
5423 5538 get_all_cb_t cb = { 0 };
5424 5539 cb.cb_verbose = verbose;
5425 5540 cb.cb_getone = get_one_dataset;
5426 5541
5427 5542 if (verbose)
5428 5543 set_progress_header(gettext("Reading ZFS config"));
5429 5544 (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
5430 5545
5431 5546 *dslist = cb.cb_handles;
5432 5547 *count = cb.cb_used;
5433 5548
5434 5549 if (verbose)
5435 5550 finish_progress(gettext("done."));
5436 5551 }
5437 5552
5438 5553 /*
5439 5554 * Generic callback for sharing or mounting filesystems. Because the code is so
5440 5555 * similar, we have a common function with an extra parameter to determine which
5441 5556 * mode we are using.
5442 5557 */
5443 5558 #define OP_SHARE 0x1
5444 5559 #define OP_MOUNT 0x2
5445 5560
5446 5561 /*
5447 5562 * Share or mount a dataset.
5448 5563 */
5449 5564 static int
5450 5565 share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
5451 5566 boolean_t explicit, const char *options)
5452 5567 {
5453 5568 char mountpoint[ZFS_MAXPROPLEN];
5454 5569 char shareopts[ZFS_MAXPROPLEN];
5455 5570 char smbshareopts[ZFS_MAXPROPLEN];
5456 5571 const char *cmdname = op == OP_SHARE ? "share" : "mount";
5457 5572 struct mnttab mnt;
5458 5573 uint64_t zoned, canmount;
5459 5574 boolean_t shared_nfs, shared_smb;
5460 5575
5461 5576 assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
5462 5577
5463 5578 /*
5464 5579 * Check to make sure we can mount/share this dataset. If we
5465 5580 * are in the global zone and the filesystem is exported to a
5466 5581 * local zone, or if we are in a local zone and the
5467 5582 * filesystem is not exported, then it is an error.
5468 5583 */
5469 5584 zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
5470 5585
5471 5586 if (zoned && getzoneid() == GLOBAL_ZONEID) {
5472 5587 if (!explicit)
5473 5588 return (0);
5474 5589
5475 5590 (void) fprintf(stderr, gettext("cannot %s '%s': "
5476 5591 "dataset is exported to a local zone\n"), cmdname,
5477 5592 zfs_get_name(zhp));
5478 5593 return (1);
5479 5594
5480 5595 } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
5481 5596 if (!explicit)
5482 5597 return (0);
5483 5598
5484 5599 (void) fprintf(stderr, gettext("cannot %s '%s': "
5485 5600 "permission denied\n"), cmdname,
5486 5601 zfs_get_name(zhp));
5487 5602 return (1);
5488 5603 }
5489 5604
5490 5605 /*
5491 5606 * Ignore any filesystems which don't apply to us. This
5492 5607 * includes those with a legacy mountpoint, or those with
5493 5608 * legacy share options.
5494 5609 */
5495 5610 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
5496 5611 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
5497 5612 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
5498 5613 sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
5499 5614 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
5500 5615 sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
5501 5616
5502 5617 if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
5503 5618 strcmp(smbshareopts, "off") == 0) {
5504 5619 if (!explicit)
5505 5620 return (0);
5506 5621
5507 5622 (void) fprintf(stderr, gettext("cannot share '%s': "
5508 5623 "legacy share\n"), zfs_get_name(zhp));
5509 5624 (void) fprintf(stderr, gettext("use share(1M) to "
5510 5625 "share this filesystem, or set "
5511 5626 "sharenfs property on\n"));
5512 5627 return (1);
5513 5628 }
5514 5629
5515 5630 /*
5516 5631 * We cannot share or mount legacy filesystems. If the
5517 5632 * shareopts is non-legacy but the mountpoint is legacy, we
5518 5633 * treat it as a legacy share.
5519 5634 */
5520 5635 if (strcmp(mountpoint, "legacy") == 0) {
5521 5636 if (!explicit)
5522 5637 return (0);
5523 5638
5524 5639 (void) fprintf(stderr, gettext("cannot %s '%s': "
5525 5640 "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
5526 5641 (void) fprintf(stderr, gettext("use %s(1M) to "
5527 5642 "%s this filesystem\n"), cmdname, cmdname);
5528 5643 return (1);
5529 5644 }
5530 5645
5531 5646 if (strcmp(mountpoint, "none") == 0) {
5532 5647 if (!explicit)
5533 5648 return (0);
5534 5649
5535 5650 (void) fprintf(stderr, gettext("cannot %s '%s': no "
5536 5651 "mountpoint set\n"), cmdname, zfs_get_name(zhp));
5537 5652 return (1);
5538 5653 }
5539 5654
5540 5655 /*
5541 5656 * canmount explicit outcome
5542 5657 * on no pass through
5543 5658 * on yes pass through
5544 5659 * off no return 0
5545 5660 * off yes display error, return 1
5546 5661 * noauto no return 0
5547 5662 * noauto yes pass through
5548 5663 */
5549 5664 canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
5550 5665 if (canmount == ZFS_CANMOUNT_OFF) {
5551 5666 if (!explicit)
5552 5667 return (0);
5553 5668
5554 5669 (void) fprintf(stderr, gettext("cannot %s '%s': "
5555 5670 "'canmount' property is set to 'off'\n"), cmdname,
5556 5671 zfs_get_name(zhp));
5557 5672 return (1);
5558 5673 } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
5559 5674 return (0);
5560 5675 }
5561 5676
5562 5677 /*
5563 5678 * At this point, we have verified that the mountpoint and/or
5564 5679 * shareopts are appropriate for auto management. If the
5565 5680 * filesystem is already mounted or shared, return (failing
5566 5681 * for explicit requests); otherwise mount or share the
5567 5682 * filesystem.
5568 5683 */
5569 5684 switch (op) {
5570 5685 case OP_SHARE:
5571 5686
5572 5687 shared_nfs = zfs_is_shared_nfs(zhp, NULL);
5573 5688 shared_smb = zfs_is_shared_smb(zhp, NULL);
5574 5689
5575 5690 if (shared_nfs && shared_smb ||
5576 5691 (shared_nfs && strcmp(shareopts, "on") == 0 &&
5577 5692 strcmp(smbshareopts, "off") == 0) ||
5578 5693 (shared_smb && strcmp(smbshareopts, "on") == 0 &&
5579 5694 strcmp(shareopts, "off") == 0)) {
5580 5695 if (!explicit)
5581 5696 return (0);
5582 5697
5583 5698 (void) fprintf(stderr, gettext("cannot share "
5584 5699 "'%s': filesystem already shared\n"),
5585 5700 zfs_get_name(zhp));
5586 5701 return (1);
5587 5702 }
5588 5703
5589 5704 if (!zfs_is_mounted(zhp, NULL) &&
5590 5705 zfs_mount(zhp, NULL, 0) != 0)
5591 5706 return (1);
5592 5707
5593 5708 if (protocol == NULL) {
5594 5709 if (zfs_shareall(zhp) != 0)
5595 5710 return (1);
5596 5711 } else if (strcmp(protocol, "nfs") == 0) {
5597 5712 if (zfs_share_nfs(zhp))
5598 5713 return (1);
5599 5714 } else if (strcmp(protocol, "smb") == 0) {
5600 5715 if (zfs_share_smb(zhp))
5601 5716 return (1);
5602 5717 } else {
5603 5718 (void) fprintf(stderr, gettext("cannot share "
5604 5719 "'%s': invalid share type '%s' "
5605 5720 "specified\n"),
5606 5721 zfs_get_name(zhp), protocol);
5607 5722 return (1);
5608 5723 }
5609 5724
5610 5725 break;
5611 5726
5612 5727 case OP_MOUNT:
5613 5728 if (options == NULL)
5614 5729 mnt.mnt_mntopts = "";
5615 5730 else
5616 5731 mnt.mnt_mntopts = (char *)options;
5617 5732
5618 5733 if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
5619 5734 zfs_is_mounted(zhp, NULL)) {
5620 5735 if (!explicit)
5621 5736 return (0);
5622 5737
5623 5738 (void) fprintf(stderr, gettext("cannot mount "
5624 5739 "'%s': filesystem already mounted\n"),
5625 5740 zfs_get_name(zhp));
5626 5741 return (1);
5627 5742 }
5628 5743
5629 5744 if (zfs_mount(zhp, options, flags) != 0)
5630 5745 return (1);
5631 5746 break;
5632 5747 }
5633 5748
5634 5749 return (0);
5635 5750 }
5636 5751
5637 5752 /*
5638 5753 * Reports progress in the form "(current/total)". Not thread-safe.
5639 5754 */
5640 5755 static void
5641 5756 report_mount_progress(int current, int total)
5642 5757 {
5643 5758 static time_t last_progress_time = 0;
5644 5759 time_t now = time(NULL);
5645 5760 char info[32];
5646 5761
5647 5762 /* report 1..n instead of 0..n-1 */
5648 5763 ++current;
5649 5764
5650 5765 /* display header if we're here for the first time */
5651 5766 if (current == 1) {
5652 5767 set_progress_header(gettext("Mounting ZFS filesystems"));
5653 5768 } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
5654 5769 /* too soon to report again */
5655 5770 return;
5656 5771 }
5657 5772
5658 5773 last_progress_time = now;
5659 5774
5660 5775 (void) sprintf(info, "(%d/%d)", current, total);
5661 5776
5662 5777 if (current == total)
5663 5778 finish_progress(info);
5664 5779 else
5665 5780 update_progress(info);
5666 5781 }
5667 5782
5668 5783 static void
5669 5784 append_options(char *mntopts, char *newopts)
5670 5785 {
5671 5786 int len = strlen(mntopts);
5672 5787
5673 5788 /* original length plus new string to append plus 1 for the comma */
5674 5789 if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
5675 5790 (void) fprintf(stderr, gettext("the opts argument for "
5676 5791 "'%c' option is too long (more than %d chars)\n"),
5677 5792 "-o", MNT_LINE_MAX);
5678 5793 usage(B_FALSE);
5679 5794 }
5680 5795
5681 5796 if (*mntopts)
5682 5797 mntopts[len++] = ',';
5683 5798
5684 5799 (void) strcpy(&mntopts[len], newopts);
5685 5800 }
5686 5801
5687 5802 static int
5688 5803 share_mount(int op, int argc, char **argv)
5689 5804 {
5690 5805 int do_all = 0;
5691 5806 boolean_t verbose = B_FALSE;
5692 5807 int c, ret = 0;
5693 5808 char *options = NULL;
5694 5809 int flags = 0;
5695 5810
5696 5811 /* check options */
5697 5812 while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
5698 5813 != -1) {
5699 5814 switch (c) {
5700 5815 case 'a':
5701 5816 do_all = 1;
5702 5817 break;
5703 5818 case 'v':
5704 5819 verbose = B_TRUE;
5705 5820 break;
5706 5821 case 'o':
5707 5822 if (*optarg == '\0') {
5708 5823 (void) fprintf(stderr, gettext("empty mount "
5709 5824 "options (-o) specified\n"));
5710 5825 usage(B_FALSE);
5711 5826 }
5712 5827
5713 5828 if (options == NULL)
5714 5829 options = safe_malloc(MNT_LINE_MAX + 1);
5715 5830
5716 5831 /* option validation is done later */
5717 5832 append_options(options, optarg);
5718 5833 break;
5719 5834
5720 5835 case 'O':
5721 5836 flags |= MS_OVERLAY;
5722 5837 break;
5723 5838 case ':':
5724 5839 (void) fprintf(stderr, gettext("missing argument for "
5725 5840 "'%c' option\n"), optopt);
5726 5841 usage(B_FALSE);
5727 5842 break;
5728 5843 case '?':
5729 5844 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5730 5845 optopt);
5731 5846 usage(B_FALSE);
5732 5847 }
5733 5848 }
5734 5849
5735 5850 argc -= optind;
5736 5851 argv += optind;
5737 5852
5738 5853 /* check number of arguments */
5739 5854 if (do_all) {
5740 5855 zfs_handle_t **dslist = NULL;
5741 5856 size_t i, count = 0;
5742 5857 char *protocol = NULL;
5743 5858
5744 5859 if (op == OP_SHARE && argc > 0) {
5745 5860 if (strcmp(argv[0], "nfs") != 0 &&
5746 5861 strcmp(argv[0], "smb") != 0) {
5747 5862 (void) fprintf(stderr, gettext("share type "
5748 5863 "must be 'nfs' or 'smb'\n"));
5749 5864 usage(B_FALSE);
5750 5865 }
5751 5866 protocol = argv[0];
5752 5867 argc--;
5753 5868 argv++;
5754 5869 }
5755 5870
5756 5871 if (argc != 0) {
5757 5872 (void) fprintf(stderr, gettext("too many arguments\n"));
5758 5873 usage(B_FALSE);
5759 5874 }
5760 5875
5761 5876 start_progress_timer();
5762 5877 get_all_datasets(&dslist, &count, verbose);
5763 5878
5764 5879 if (count == 0)
5765 5880 return (0);
5766 5881
5767 5882 qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
5768 5883
5769 5884 for (i = 0; i < count; i++) {
5770 5885 if (verbose)
5771 5886 report_mount_progress(i, count);
5772 5887
5773 5888 if (share_mount_one(dslist[i], op, flags, protocol,
5774 5889 B_FALSE, options) != 0)
5775 5890 ret = 1;
5776 5891 zfs_close(dslist[i]);
5777 5892 }
5778 5893
5779 5894 free(dslist);
5780 5895 } else if (argc == 0) {
5781 5896 struct mnttab entry;
5782 5897
5783 5898 if ((op == OP_SHARE) || (options != NULL)) {
5784 5899 (void) fprintf(stderr, gettext("missing filesystem "
5785 5900 "argument (specify -a for all)\n"));
5786 5901 usage(B_FALSE);
5787 5902 }
5788 5903
5789 5904 /*
5790 5905 * When mount is given no arguments, go through /etc/mnttab and
5791 5906 * display any active ZFS mounts. We hide any snapshots, since
5792 5907 * they are controlled automatically.
5793 5908 */
5794 5909 rewind(mnttab_file);
5795 5910 while (getmntent(mnttab_file, &entry) == 0) {
5796 5911 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
5797 5912 strchr(entry.mnt_special, '@') != NULL)
5798 5913 continue;
5799 5914
5800 5915 (void) printf("%-30s %s\n", entry.mnt_special,
5801 5916 entry.mnt_mountp);
5802 5917 }
5803 5918
5804 5919 } else {
5805 5920 zfs_handle_t *zhp;
5806 5921
5807 5922 if (argc > 1) {
5808 5923 (void) fprintf(stderr,
5809 5924 gettext("too many arguments\n"));
5810 5925 usage(B_FALSE);
5811 5926 }
5812 5927
5813 5928 if ((zhp = zfs_open(g_zfs, argv[0],
5814 5929 ZFS_TYPE_FILESYSTEM)) == NULL) {
5815 5930 ret = 1;
5816 5931 } else {
5817 5932 ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
5818 5933 options);
5819 5934 zfs_close(zhp);
5820 5935 }
5821 5936 }
5822 5937
5823 5938 return (ret);
5824 5939 }
5825 5940
5826 5941 /*
5827 5942 * zfs mount -a [nfs]
5828 5943 * zfs mount filesystem
5829 5944 *
5830 5945 * Mount all filesystems, or mount the given filesystem.
5831 5946 */
5832 5947 static int
5833 5948 zfs_do_mount(int argc, char **argv)
5834 5949 {
5835 5950 return (share_mount(OP_MOUNT, argc, argv));
5836 5951 }
5837 5952
5838 5953 /*
5839 5954 * zfs share -a [nfs | smb]
5840 5955 * zfs share filesystem
5841 5956 *
5842 5957 * Share all filesystems, or share the given filesystem.
5843 5958 */
5844 5959 static int
5845 5960 zfs_do_share(int argc, char **argv)
5846 5961 {
5847 5962 return (share_mount(OP_SHARE, argc, argv));
5848 5963 }
5849 5964
5850 5965 typedef struct unshare_unmount_node {
5851 5966 zfs_handle_t *un_zhp;
5852 5967 char *un_mountp;
5853 5968 uu_avl_node_t un_avlnode;
5854 5969 } unshare_unmount_node_t;
5855 5970
5856 5971 /* ARGSUSED */
5857 5972 static int
5858 5973 unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
5859 5974 {
5860 5975 const unshare_unmount_node_t *l = larg;
5861 5976 const unshare_unmount_node_t *r = rarg;
5862 5977
5863 5978 return (strcmp(l->un_mountp, r->un_mountp));
5864 5979 }
5865 5980
5866 5981 /*
5867 5982 * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
5868 5983 * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
5869 5984 * and unmount it appropriately.
5870 5985 */
5871 5986 static int
5872 5987 unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
5873 5988 {
5874 5989 zfs_handle_t *zhp;
5875 5990 int ret = 0;
5876 5991 struct stat64 statbuf;
5877 5992 struct extmnttab entry;
5878 5993 const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
5879 5994 ino_t path_inode;
5880 5995
5881 5996 /*
5882 5997 * Search for the path in /etc/mnttab. Rather than looking for the
5883 5998 * specific path, which can be fooled by non-standard paths (i.e. ".."
5884 5999 * or "//"), we stat() the path and search for the corresponding
5885 6000 * (major,minor) device pair.
5886 6001 */
5887 6002 if (stat64(path, &statbuf) != 0) {
5888 6003 (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
5889 6004 cmdname, path, strerror(errno));
5890 6005 return (1);
5891 6006 }
5892 6007 path_inode = statbuf.st_ino;
5893 6008
5894 6009 /*
5895 6010 * Search for the given (major,minor) pair in the mount table.
5896 6011 */
5897 6012 rewind(mnttab_file);
5898 6013 while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
5899 6014 if (entry.mnt_major == major(statbuf.st_dev) &&
5900 6015 entry.mnt_minor == minor(statbuf.st_dev))
5901 6016 break;
5902 6017 }
5903 6018 if (ret != 0) {
5904 6019 if (op == OP_SHARE) {
5905 6020 (void) fprintf(stderr, gettext("cannot %s '%s': not "
5906 6021 "currently mounted\n"), cmdname, path);
5907 6022 return (1);
5908 6023 }
5909 6024 (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
5910 6025 path);
5911 6026 if ((ret = umount2(path, flags)) != 0)
5912 6027 (void) fprintf(stderr, gettext("%s: %s\n"), path,
5913 6028 strerror(errno));
5914 6029 return (ret != 0);
5915 6030 }
5916 6031
5917 6032 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
5918 6033 (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
5919 6034 "filesystem\n"), cmdname, path);
5920 6035 return (1);
5921 6036 }
5922 6037
5923 6038 if ((zhp = zfs_open(g_zfs, entry.mnt_special,
5924 6039 ZFS_TYPE_FILESYSTEM)) == NULL)
5925 6040 return (1);
5926 6041
5927 6042 ret = 1;
5928 6043 if (stat64(entry.mnt_mountp, &statbuf) != 0) {
5929 6044 (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
5930 6045 cmdname, path, strerror(errno));
5931 6046 goto out;
5932 6047 } else if (statbuf.st_ino != path_inode) {
5933 6048 (void) fprintf(stderr, gettext("cannot "
5934 6049 "%s '%s': not a mountpoint\n"), cmdname, path);
5935 6050 goto out;
5936 6051 }
5937 6052
5938 6053 if (op == OP_SHARE) {
5939 6054 char nfs_mnt_prop[ZFS_MAXPROPLEN];
5940 6055 char smbshare_prop[ZFS_MAXPROPLEN];
5941 6056
5942 6057 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
5943 6058 sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
5944 6059 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
5945 6060 sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
5946 6061
5947 6062 if (strcmp(nfs_mnt_prop, "off") == 0 &&
5948 6063 strcmp(smbshare_prop, "off") == 0) {
5949 6064 (void) fprintf(stderr, gettext("cannot unshare "
5950 6065 "'%s': legacy share\n"), path);
5951 6066 (void) fprintf(stderr, gettext("use "
5952 6067 "unshare(1M) to unshare this filesystem\n"));
5953 6068 } else if (!zfs_is_shared(zhp)) {
5954 6069 (void) fprintf(stderr, gettext("cannot unshare '%s': "
5955 6070 "not currently shared\n"), path);
5956 6071 } else {
5957 6072 ret = zfs_unshareall_bypath(zhp, path);
5958 6073 }
5959 6074 } else {
5960 6075 char mtpt_prop[ZFS_MAXPROPLEN];
5961 6076
5962 6077 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
5963 6078 sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
5964 6079
5965 6080 if (is_manual) {
5966 6081 ret = zfs_unmount(zhp, NULL, flags);
5967 6082 } else if (strcmp(mtpt_prop, "legacy") == 0) {
5968 6083 (void) fprintf(stderr, gettext("cannot unmount "
5969 6084 "'%s': legacy mountpoint\n"),
5970 6085 zfs_get_name(zhp));
5971 6086 (void) fprintf(stderr, gettext("use umount(1M) "
5972 6087 "to unmount this filesystem\n"));
5973 6088 } else {
5974 6089 ret = zfs_unmountall(zhp, flags);
5975 6090 }
5976 6091 }
5977 6092
5978 6093 out:
5979 6094 zfs_close(zhp);
5980 6095
5981 6096 return (ret != 0);
5982 6097 }
5983 6098
5984 6099 /*
5985 6100 * Generic callback for unsharing or unmounting a filesystem.
5986 6101 */
5987 6102 static int
5988 6103 unshare_unmount(int op, int argc, char **argv)
5989 6104 {
5990 6105 int do_all = 0;
5991 6106 int flags = 0;
5992 6107 int ret = 0;
5993 6108 int c;
5994 6109 zfs_handle_t *zhp;
5995 6110 char nfs_mnt_prop[ZFS_MAXPROPLEN];
5996 6111 char sharesmb[ZFS_MAXPROPLEN];
5997 6112
5998 6113 /* check options */
5999 6114 while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
6000 6115 switch (c) {
6001 6116 case 'a':
6002 6117 do_all = 1;
6003 6118 break;
6004 6119 case 'f':
6005 6120 flags = MS_FORCE;
6006 6121 break;
6007 6122 case '?':
6008 6123 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6009 6124 optopt);
6010 6125 usage(B_FALSE);
6011 6126 }
6012 6127 }
6013 6128
6014 6129 argc -= optind;
6015 6130 argv += optind;
6016 6131
6017 6132 if (do_all) {
6018 6133 /*
6019 6134 * We could make use of zfs_for_each() to walk all datasets in
6020 6135 * the system, but this would be very inefficient, especially
6021 6136 * since we would have to linearly search /etc/mnttab for each
6022 6137 * one. Instead, do one pass through /etc/mnttab looking for
6023 6138 * zfs entries and call zfs_unmount() for each one.
6024 6139 *
6025 6140 * Things get a little tricky if the administrator has created
6026 6141 * mountpoints beneath other ZFS filesystems. In this case, we
6027 6142 * have to unmount the deepest filesystems first. To accomplish
6028 6143 * this, we place all the mountpoints in an AVL tree sorted by
6029 6144 * the special type (dataset name), and walk the result in
6030 6145 * reverse to make sure to get any snapshots first.
6031 6146 */
6032 6147 struct mnttab entry;
6033 6148 uu_avl_pool_t *pool;
6034 6149 uu_avl_t *tree;
6035 6150 unshare_unmount_node_t *node;
6036 6151 uu_avl_index_t idx;
6037 6152 uu_avl_walk_t *walk;
6038 6153
6039 6154 if (argc != 0) {
6040 6155 (void) fprintf(stderr, gettext("too many arguments\n"));
6041 6156 usage(B_FALSE);
6042 6157 }
6043 6158
6044 6159 if (((pool = uu_avl_pool_create("unmount_pool",
6045 6160 sizeof (unshare_unmount_node_t),
6046 6161 offsetof(unshare_unmount_node_t, un_avlnode),
6047 6162 unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
6048 6163 ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
6049 6164 nomem();
6050 6165
6051 6166 rewind(mnttab_file);
6052 6167 while (getmntent(mnttab_file, &entry) == 0) {
6053 6168
6054 6169 /* ignore non-ZFS entries */
6055 6170 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6056 6171 continue;
6057 6172
6058 6173 /* ignore snapshots */
6059 6174 if (strchr(entry.mnt_special, '@') != NULL)
6060 6175 continue;
6061 6176
6062 6177 if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6063 6178 ZFS_TYPE_FILESYSTEM)) == NULL) {
6064 6179 ret = 1;
6065 6180 continue;
6066 6181 }
6067 6182
6068 6183 switch (op) {
6069 6184 case OP_SHARE:
6070 6185 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6071 6186 nfs_mnt_prop,
6072 6187 sizeof (nfs_mnt_prop),
6073 6188 NULL, NULL, 0, B_FALSE) == 0);
6074 6189 if (strcmp(nfs_mnt_prop, "off") != 0)
6075 6190 break;
6076 6191 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6077 6192 nfs_mnt_prop,
6078 6193 sizeof (nfs_mnt_prop),
6079 6194 NULL, NULL, 0, B_FALSE) == 0);
6080 6195 if (strcmp(nfs_mnt_prop, "off") == 0)
6081 6196 continue;
6082 6197 break;
6083 6198 case OP_MOUNT:
6084 6199 /* Ignore legacy mounts */
6085 6200 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
6086 6201 nfs_mnt_prop,
6087 6202 sizeof (nfs_mnt_prop),
6088 6203 NULL, NULL, 0, B_FALSE) == 0);
6089 6204 if (strcmp(nfs_mnt_prop, "legacy") == 0)
6090 6205 continue;
6091 6206 /* Ignore canmount=noauto mounts */
6092 6207 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
6093 6208 ZFS_CANMOUNT_NOAUTO)
6094 6209 continue;
6095 6210 default:
6096 6211 break;
6097 6212 }
6098 6213
6099 6214 node = safe_malloc(sizeof (unshare_unmount_node_t));
6100 6215 node->un_zhp = zhp;
6101 6216 node->un_mountp = safe_strdup(entry.mnt_mountp);
6102 6217
6103 6218 uu_avl_node_init(node, &node->un_avlnode, pool);
6104 6219
6105 6220 if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
6106 6221 uu_avl_insert(tree, node, idx);
6107 6222 } else {
6108 6223 zfs_close(node->un_zhp);
6109 6224 free(node->un_mountp);
6110 6225 free(node);
6111 6226 }
6112 6227 }
6113 6228
6114 6229 /*
6115 6230 * Walk the AVL tree in reverse, unmounting each filesystem and
6116 6231 * removing it from the AVL tree in the process.
6117 6232 */
6118 6233 if ((walk = uu_avl_walk_start(tree,
6119 6234 UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
6120 6235 nomem();
6121 6236
6122 6237 while ((node = uu_avl_walk_next(walk)) != NULL) {
6123 6238 uu_avl_remove(tree, node);
6124 6239
6125 6240 switch (op) {
6126 6241 case OP_SHARE:
6127 6242 if (zfs_unshareall_bypath(node->un_zhp,
6128 6243 node->un_mountp) != 0)
6129 6244 ret = 1;
6130 6245 break;
6131 6246
6132 6247 case OP_MOUNT:
6133 6248 if (zfs_unmount(node->un_zhp,
6134 6249 node->un_mountp, flags) != 0)
6135 6250 ret = 1;
6136 6251 break;
6137 6252 }
6138 6253
6139 6254 zfs_close(node->un_zhp);
6140 6255 free(node->un_mountp);
6141 6256 free(node);
6142 6257 }
6143 6258
6144 6259 uu_avl_walk_end(walk);
6145 6260 uu_avl_destroy(tree);
6146 6261 uu_avl_pool_destroy(pool);
6147 6262
6148 6263 } else {
6149 6264 if (argc != 1) {
6150 6265 if (argc == 0)
6151 6266 (void) fprintf(stderr,
6152 6267 gettext("missing filesystem argument\n"));
6153 6268 else
6154 6269 (void) fprintf(stderr,
6155 6270 gettext("too many arguments\n"));
6156 6271 usage(B_FALSE);
6157 6272 }
6158 6273
6159 6274 /*
6160 6275 * We have an argument, but it may be a full path or a ZFS
6161 6276 * filesystem. Pass full paths off to unmount_path() (shared by
6162 6277 * manual_unmount), otherwise open the filesystem and pass to
6163 6278 * zfs_unmount().
6164 6279 */
6165 6280 if (argv[0][0] == '/')
6166 6281 return (unshare_unmount_path(op, argv[0],
6167 6282 flags, B_FALSE));
6168 6283
6169 6284 if ((zhp = zfs_open(g_zfs, argv[0],
6170 6285 ZFS_TYPE_FILESYSTEM)) == NULL)
6171 6286 return (1);
6172 6287
6173 6288 verify(zfs_prop_get(zhp, op == OP_SHARE ?
6174 6289 ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
6175 6290 nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
6176 6291 NULL, 0, B_FALSE) == 0);
6177 6292
6178 6293 switch (op) {
6179 6294 case OP_SHARE:
6180 6295 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6181 6296 nfs_mnt_prop,
6182 6297 sizeof (nfs_mnt_prop),
6183 6298 NULL, NULL, 0, B_FALSE) == 0);
6184 6299 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6185 6300 sharesmb, sizeof (sharesmb), NULL, NULL,
6186 6301 0, B_FALSE) == 0);
6187 6302
6188 6303 if (strcmp(nfs_mnt_prop, "off") == 0 &&
6189 6304 strcmp(sharesmb, "off") == 0) {
6190 6305 (void) fprintf(stderr, gettext("cannot "
6191 6306 "unshare '%s': legacy share\n"),
6192 6307 zfs_get_name(zhp));
6193 6308 (void) fprintf(stderr, gettext("use "
6194 6309 "unshare(1M) to unshare this "
6195 6310 "filesystem\n"));
6196 6311 ret = 1;
6197 6312 } else if (!zfs_is_shared(zhp)) {
6198 6313 (void) fprintf(stderr, gettext("cannot "
6199 6314 "unshare '%s': not currently "
6200 6315 "shared\n"), zfs_get_name(zhp));
6201 6316 ret = 1;
6202 6317 } else if (zfs_unshareall(zhp) != 0) {
6203 6318 ret = 1;
6204 6319 }
6205 6320 break;
6206 6321
6207 6322 case OP_MOUNT:
6208 6323 if (strcmp(nfs_mnt_prop, "legacy") == 0) {
6209 6324 (void) fprintf(stderr, gettext("cannot "
6210 6325 "unmount '%s': legacy "
6211 6326 "mountpoint\n"), zfs_get_name(zhp));
6212 6327 (void) fprintf(stderr, gettext("use "
6213 6328 "umount(1M) to unmount this "
6214 6329 "filesystem\n"));
6215 6330 ret = 1;
6216 6331 } else if (!zfs_is_mounted(zhp, NULL)) {
6217 6332 (void) fprintf(stderr, gettext("cannot "
6218 6333 "unmount '%s': not currently "
6219 6334 "mounted\n"),
6220 6335 zfs_get_name(zhp));
6221 6336 ret = 1;
6222 6337 } else if (zfs_unmountall(zhp, flags) != 0) {
6223 6338 ret = 1;
6224 6339 }
6225 6340 break;
6226 6341 }
6227 6342
6228 6343 zfs_close(zhp);
6229 6344 }
6230 6345
6231 6346 return (ret);
6232 6347 }
6233 6348
6234 6349 /*
6235 6350 * zfs unmount -a
6236 6351 * zfs unmount filesystem
6237 6352 *
6238 6353 * Unmount all filesystems, or a specific ZFS filesystem.
6239 6354 */
6240 6355 static int
6241 6356 zfs_do_unmount(int argc, char **argv)
6242 6357 {
6243 6358 return (unshare_unmount(OP_MOUNT, argc, argv));
6244 6359 }
6245 6360
6246 6361 /*
6247 6362 * zfs unshare -a
6248 6363 * zfs unshare filesystem
6249 6364 *
6250 6365 * Unshare all filesystems, or a specific ZFS filesystem.
6251 6366 */
6252 6367 static int
6253 6368 zfs_do_unshare(int argc, char **argv)
6254 6369 {
6255 6370 return (unshare_unmount(OP_SHARE, argc, argv));
6256 6371 }
6257 6372
6258 6373 /*
6259 6374 * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
6260 6375 * 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
6261 6376 */
6262 6377 static int
6263 6378 manual_mount(int argc, char **argv)
6264 6379 {
6265 6380 zfs_handle_t *zhp;
6266 6381 char mountpoint[ZFS_MAXPROPLEN];
6267 6382 char mntopts[MNT_LINE_MAX] = { '\0' };
6268 6383 int ret = 0;
6269 6384 int c;
6270 6385 int flags = 0;
6271 6386 char *dataset, *path;
6272 6387
6273 6388 /* check options */
6274 6389 while ((c = getopt(argc, argv, ":mo:O")) != -1) {
6275 6390 switch (c) {
6276 6391 case 'o':
6277 6392 (void) strlcpy(mntopts, optarg, sizeof (mntopts));
6278 6393 break;
6279 6394 case 'O':
6280 6395 flags |= MS_OVERLAY;
6281 6396 break;
6282 6397 case 'm':
6283 6398 flags |= MS_NOMNTTAB;
6284 6399 break;
6285 6400 case ':':
6286 6401 (void) fprintf(stderr, gettext("missing argument for "
6287 6402 "'%c' option\n"), optopt);
6288 6403 usage(B_FALSE);
6289 6404 break;
6290 6405 case '?':
6291 6406 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6292 6407 optopt);
6293 6408 (void) fprintf(stderr, gettext("usage: mount [-o opts] "
6294 6409 "<path>\n"));
6295 6410 return (2);
6296 6411 }
6297 6412 }
6298 6413
6299 6414 argc -= optind;
6300 6415 argv += optind;
6301 6416
6302 6417 /* check that we only have two arguments */
6303 6418 if (argc != 2) {
6304 6419 if (argc == 0)
6305 6420 (void) fprintf(stderr, gettext("missing dataset "
6306 6421 "argument\n"));
6307 6422 else if (argc == 1)
6308 6423 (void) fprintf(stderr,
6309 6424 gettext("missing mountpoint argument\n"));
6310 6425 else
6311 6426 (void) fprintf(stderr, gettext("too many arguments\n"));
6312 6427 (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
6313 6428 return (2);
6314 6429 }
6315 6430
6316 6431 dataset = argv[0];
6317 6432 path = argv[1];
6318 6433
6319 6434 /* try to open the dataset */
6320 6435 if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
6321 6436 return (1);
6322 6437
6323 6438 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
6324 6439 sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
6325 6440
6326 6441 /* check for legacy mountpoint and complain appropriately */
6327 6442 ret = 0;
6328 6443 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
6329 6444 if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
6330 6445 NULL, 0, mntopts, sizeof (mntopts)) != 0) {
6331 6446 (void) fprintf(stderr, gettext("mount failed: %s\n"),
6332 6447 strerror(errno));
6333 6448 ret = 1;
6334 6449 }
6335 6450 } else {
6336 6451 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
6337 6452 "mounted using 'mount -F zfs'\n"), dataset);
6338 6453 (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
6339 6454 "instead.\n"), path);
6340 6455 (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
6341 6456 "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
6342 6457 (void) fprintf(stderr, gettext("See zfs(1M) for more "
6343 6458 "information.\n"));
6344 6459 ret = 1;
6345 6460 }
6346 6461
6347 6462 return (ret);
6348 6463 }
6349 6464
6350 6465 /*
6351 6466 * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow
6352 6467 * unmounts of non-legacy filesystems, as this is the dominant administrative
6353 6468 * interface.
6354 6469 */
6355 6470 static int
6356 6471 manual_unmount(int argc, char **argv)
6357 6472 {
6358 6473 int flags = 0;
6359 6474 int c;
6360 6475
6361 6476 /* check options */
6362 6477 while ((c = getopt(argc, argv, "f")) != -1) {
6363 6478 switch (c) {
6364 6479 case 'f':
6365 6480 flags = MS_FORCE;
6366 6481 break;
6367 6482 case '?':
6368 6483 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6369 6484 optopt);
6370 6485 (void) fprintf(stderr, gettext("usage: unmount [-f] "
6371 6486 "<path>\n"));
6372 6487 return (2);
6373 6488 }
6374 6489 }
6375 6490
6376 6491 argc -= optind;
6377 6492 argv += optind;
6378 6493
6379 6494 /* check arguments */
6380 6495 if (argc != 1) {
6381 6496 if (argc == 0)
6382 6497 (void) fprintf(stderr, gettext("missing path "
6383 6498 "argument\n"));
6384 6499 else
6385 6500 (void) fprintf(stderr, gettext("too many arguments\n"));
6386 6501 (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
6387 6502 return (2);
6388 6503 }
6389 6504
6390 6505 return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
6391 6506 }
6392 6507
6393 6508 static int
6394 6509 find_command_idx(char *command, int *idx)
6395 6510 {
6396 6511 int i;
6397 6512
6398 6513 for (i = 0; i < NCOMMAND; i++) {
6399 6514 if (command_table[i].name == NULL)
6400 6515 continue;
6401 6516
6402 6517 if (strcmp(command, command_table[i].name) == 0) {
6403 6518 *idx = i;
6404 6519 return (0);
6405 6520 }
6406 6521 }
6407 6522 return (1);
6408 6523 }
6409 6524
6410 6525 static int
6411 6526 zfs_do_diff(int argc, char **argv)
6412 6527 {
6413 6528 zfs_handle_t *zhp;
6414 6529 int flags = 0;
6415 6530 char *tosnap = NULL;
6416 6531 char *fromsnap = NULL;
6417 6532 char *atp, *copy;
6418 6533 int err = 0;
6419 6534 int c;
6420 6535
6421 6536 while ((c = getopt(argc, argv, "FHt")) != -1) {
6422 6537 switch (c) {
6423 6538 case 'F':
6424 6539 flags |= ZFS_DIFF_CLASSIFY;
6425 6540 break;
6426 6541 case 'H':
6427 6542 flags |= ZFS_DIFF_PARSEABLE;
6428 6543 break;
6429 6544 case 't':
6430 6545 flags |= ZFS_DIFF_TIMESTAMP;
6431 6546 break;
6432 6547 default:
6433 6548 (void) fprintf(stderr,
6434 6549 gettext("invalid option '%c'\n"), optopt);
6435 6550 usage(B_FALSE);
6436 6551 }
6437 6552 }
6438 6553
6439 6554 argc -= optind;
6440 6555 argv += optind;
6441 6556
6442 6557 if (argc < 1) {
6443 6558 (void) fprintf(stderr,
6444 6559 gettext("must provide at least one snapshot name\n"));
6445 6560 usage(B_FALSE);
6446 6561 }
6447 6562
6448 6563 if (argc > 2) {
6449 6564 (void) fprintf(stderr, gettext("too many arguments\n"));
6450 6565 usage(B_FALSE);
6451 6566 }
6452 6567
6453 6568 fromsnap = argv[0];
6454 6569 tosnap = (argc == 2) ? argv[1] : NULL;
6455 6570
6456 6571 copy = NULL;
6457 6572 if (*fromsnap != '@')
6458 6573 copy = strdup(fromsnap);
6459 6574 else if (tosnap)
6460 6575 copy = strdup(tosnap);
6461 6576 if (copy == NULL)
6462 6577 usage(B_FALSE);
6463 6578
6464 6579 if (atp = strchr(copy, '@'))
6465 6580 *atp = '\0';
6466 6581
6467 6582 if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
6468 6583 return (1);
6469 6584
6470 6585 free(copy);
6471 6586
6472 6587 /*
6473 6588 * Ignore SIGPIPE so that the library can give us
6474 6589 * information on any failure
6475 6590 */
6476 6591 (void) sigignore(SIGPIPE);
6477 6592
6478 6593 err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
6479 6594
6480 6595 zfs_close(zhp);
6481 6596
6482 6597 return (err != 0);
6483 6598 }
6484 6599
6485 6600 int
6486 6601 main(int argc, char **argv)
6487 6602 {
6488 6603 int ret = 0;
6489 6604 int i;
6490 6605 char *progname;
6491 6606 char *cmdname;
6492 6607
6493 6608 (void) setlocale(LC_ALL, "");
6494 6609 (void) textdomain(TEXT_DOMAIN);
6495 6610
6496 6611 opterr = 0;
6497 6612
6498 6613 if ((g_zfs = libzfs_init()) == NULL) {
6499 6614 (void) fprintf(stderr, gettext("internal error: failed to "
6500 6615 "initialize ZFS library\n"));
6501 6616 return (1);
6502 6617 }
6503 6618
6504 6619 zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
6505 6620
6506 6621 libzfs_print_on_error(g_zfs, B_TRUE);
6507 6622
6508 6623 if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
6509 6624 (void) fprintf(stderr, gettext("internal error: unable to "
6510 6625 "open %s\n"), MNTTAB);
6511 6626 return (1);
6512 6627 }
6513 6628
6514 6629 /*
6515 6630 * This command also doubles as the /etc/fs mount and unmount program.
6516 6631 * Determine if we should take this behavior based on argv[0].
6517 6632 */
6518 6633 progname = basename(argv[0]);
6519 6634 if (strcmp(progname, "mount") == 0) {
6520 6635 ret = manual_mount(argc, argv);
6521 6636 } else if (strcmp(progname, "umount") == 0) {
6522 6637 ret = manual_unmount(argc, argv);
6523 6638 } else {
6524 6639 /*
6525 6640 * Make sure the user has specified some command.
6526 6641 */
6527 6642 if (argc < 2) {
6528 6643 (void) fprintf(stderr, gettext("missing command\n"));
6529 6644 usage(B_FALSE);
6530 6645 }
6531 6646
6532 6647 cmdname = argv[1];
6533 6648
6534 6649 /*
6535 6650 * The 'umount' command is an alias for 'unmount'
6536 6651 */
6537 6652 if (strcmp(cmdname, "umount") == 0)
6538 6653 cmdname = "unmount";
6539 6654
6540 6655 /*
6541 6656 * The 'recv' command is an alias for 'receive'
6542 6657 */
6543 6658 if (strcmp(cmdname, "recv") == 0)
6544 6659 cmdname = "receive";
6545 6660
6546 6661 /*
6547 6662 * Special case '-?'
6548 6663 */
6549 6664 if (strcmp(cmdname, "-?") == 0)
6550 6665 usage(B_TRUE);
6551 6666
6552 6667 /*
6553 6668 * Run the appropriate command.
6554 6669 */
6555 6670 libzfs_mnttab_cache(g_zfs, B_TRUE);
6556 6671 if (find_command_idx(cmdname, &i) == 0) {
6557 6672 current_command = &command_table[i];
6558 6673 ret = command_table[i].func(argc - 1, argv + 1);
6559 6674 } else if (strchr(cmdname, '=') != NULL) {
6560 6675 verify(find_command_idx("set", &i) == 0);
6561 6676 current_command = &command_table[i];
6562 6677 ret = command_table[i].func(argc, argv);
6563 6678 } else {
6564 6679 (void) fprintf(stderr, gettext("unrecognized "
6565 6680 "command '%s'\n"), cmdname);
6566 6681 usage(B_FALSE);
6567 6682 }
6568 6683 libzfs_mnttab_cache(g_zfs, B_FALSE);
6569 6684 }
6570 6685
6571 6686 (void) fclose(mnttab_file);
6572 6687
6573 6688 if (ret == 0 && log_history)
6574 6689 (void) zpool_log_history(g_zfs, history_str);
6575 6690
6576 6691 libzfs_fini(g_zfs);
6577 6692
6578 6693 /*
6579 6694 * The 'ZFS_ABORT' environment variable causes us to dump core on exit
6580 6695 * for the purposes of running ::findleaks.
6581 6696 */
6582 6697 if (getenv("ZFS_ABORT") != NULL) {
6583 6698 (void) printf("dumping core by request\n");
6584 6699 abort();
6585 6700 }
6586 6701
6587 6702 return (ret);
6588 6703 }
↓ open down ↓ |
2851 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX