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