Print this page
3699 zfs hold or release of a non-existent snapshot does not output error
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ new/usr/src/lib/libzfs/common/libzfs_dataset.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
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
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 (c) 2012 by Delphix. All rights reserved.
25 25 * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
26 26 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
27 + * Copyright (c) 2013 Martin Matuska. All rights reserved.
27 28 */
28 29
29 30 #include <ctype.h>
30 31 #include <errno.h>
31 32 #include <libintl.h>
32 33 #include <math.h>
33 34 #include <stdio.h>
34 35 #include <stdlib.h>
35 36 #include <strings.h>
36 37 #include <unistd.h>
37 38 #include <stddef.h>
38 39 #include <zone.h>
39 40 #include <fcntl.h>
40 41 #include <sys/mntent.h>
41 42 #include <sys/mount.h>
42 43 #include <priv.h>
43 44 #include <pwd.h>
44 45 #include <grp.h>
45 46 #include <stddef.h>
46 47 #include <ucred.h>
47 48 #include <idmap.h>
48 49 #include <aclutils.h>
49 50 #include <directory.h>
50 51
51 52 #include <sys/dnode.h>
52 53 #include <sys/spa.h>
53 54 #include <sys/zap.h>
54 55 #include <libzfs.h>
55 56
56 57 #include "zfs_namecheck.h"
57 58 #include "zfs_prop.h"
58 59 #include "libzfs_impl.h"
59 60 #include "zfs_deleg.h"
60 61
61 62 static int userquota_propname_decode(const char *propname, boolean_t zoned,
62 63 zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
63 64
64 65 /*
65 66 * Given a single type (not a mask of types), return the type in a human
66 67 * readable form.
67 68 */
68 69 const char *
69 70 zfs_type_to_name(zfs_type_t type)
70 71 {
71 72 switch (type) {
72 73 case ZFS_TYPE_FILESYSTEM:
73 74 return (dgettext(TEXT_DOMAIN, "filesystem"));
74 75 case ZFS_TYPE_SNAPSHOT:
75 76 return (dgettext(TEXT_DOMAIN, "snapshot"));
76 77 case ZFS_TYPE_VOLUME:
77 78 return (dgettext(TEXT_DOMAIN, "volume"));
78 79 }
79 80
80 81 return (NULL);
81 82 }
82 83
83 84 /*
84 85 * Given a path and mask of ZFS types, return a string describing this dataset.
85 86 * This is used when we fail to open a dataset and we cannot get an exact type.
86 87 * We guess what the type would have been based on the path and the mask of
87 88 * acceptable types.
88 89 */
89 90 static const char *
90 91 path_to_str(const char *path, int types)
91 92 {
92 93 /*
93 94 * When given a single type, always report the exact type.
94 95 */
95 96 if (types == ZFS_TYPE_SNAPSHOT)
96 97 return (dgettext(TEXT_DOMAIN, "snapshot"));
97 98 if (types == ZFS_TYPE_FILESYSTEM)
98 99 return (dgettext(TEXT_DOMAIN, "filesystem"));
99 100 if (types == ZFS_TYPE_VOLUME)
100 101 return (dgettext(TEXT_DOMAIN, "volume"));
101 102
102 103 /*
103 104 * The user is requesting more than one type of dataset. If this is the
104 105 * case, consult the path itself. If we're looking for a snapshot, and
105 106 * a '@' is found, then report it as "snapshot". Otherwise, remove the
106 107 * snapshot attribute and try again.
107 108 */
108 109 if (types & ZFS_TYPE_SNAPSHOT) {
109 110 if (strchr(path, '@') != NULL)
110 111 return (dgettext(TEXT_DOMAIN, "snapshot"));
111 112 return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
112 113 }
113 114
114 115 /*
115 116 * The user has requested either filesystems or volumes.
116 117 * We have no way of knowing a priori what type this would be, so always
117 118 * report it as "filesystem" or "volume", our two primitive types.
118 119 */
119 120 if (types & ZFS_TYPE_FILESYSTEM)
120 121 return (dgettext(TEXT_DOMAIN, "filesystem"));
121 122
122 123 assert(types & ZFS_TYPE_VOLUME);
123 124 return (dgettext(TEXT_DOMAIN, "volume"));
124 125 }
125 126
126 127 /*
127 128 * Validate a ZFS path. This is used even before trying to open the dataset, to
128 129 * provide a more meaningful error message. We call zfs_error_aux() to
129 130 * explain exactly why the name was not valid.
130 131 */
131 132 int
132 133 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
133 134 boolean_t modifying)
134 135 {
135 136 namecheck_err_t why;
136 137 char what;
137 138
138 139 (void) zfs_prop_get_table();
139 140 if (dataset_namecheck(path, &why, &what) != 0) {
140 141 if (hdl != NULL) {
141 142 switch (why) {
142 143 case NAME_ERR_TOOLONG:
143 144 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
144 145 "name is too long"));
145 146 break;
146 147
147 148 case NAME_ERR_LEADING_SLASH:
148 149 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
149 150 "leading slash in name"));
150 151 break;
151 152
152 153 case NAME_ERR_EMPTY_COMPONENT:
153 154 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
154 155 "empty component in name"));
155 156 break;
156 157
157 158 case NAME_ERR_TRAILING_SLASH:
158 159 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
159 160 "trailing slash in name"));
160 161 break;
161 162
162 163 case NAME_ERR_INVALCHAR:
163 164 zfs_error_aux(hdl,
164 165 dgettext(TEXT_DOMAIN, "invalid character "
165 166 "'%c' in name"), what);
166 167 break;
167 168
168 169 case NAME_ERR_MULTIPLE_AT:
169 170 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
170 171 "multiple '@' delimiters in name"));
171 172 break;
172 173
173 174 case NAME_ERR_NOLETTER:
174 175 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
175 176 "pool doesn't begin with a letter"));
176 177 break;
177 178
178 179 case NAME_ERR_RESERVED:
179 180 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
180 181 "name is reserved"));
181 182 break;
182 183
183 184 case NAME_ERR_DISKLIKE:
184 185 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
185 186 "reserved disk name"));
186 187 break;
187 188 }
188 189 }
189 190
190 191 return (0);
191 192 }
192 193
193 194 if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
194 195 if (hdl != NULL)
195 196 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
196 197 "snapshot delimiter '@' in filesystem name"));
197 198 return (0);
198 199 }
199 200
200 201 if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
201 202 if (hdl != NULL)
202 203 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
203 204 "missing '@' delimiter in snapshot name"));
204 205 return (0);
205 206 }
206 207
207 208 if (modifying && strchr(path, '%') != NULL) {
208 209 if (hdl != NULL)
209 210 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
210 211 "invalid character %c in name"), '%');
211 212 return (0);
212 213 }
213 214
214 215 return (-1);
215 216 }
216 217
217 218 int
218 219 zfs_name_valid(const char *name, zfs_type_t type)
219 220 {
220 221 if (type == ZFS_TYPE_POOL)
221 222 return (zpool_name_valid(NULL, B_FALSE, name));
222 223 return (zfs_validate_name(NULL, name, type, B_FALSE));
223 224 }
224 225
225 226 /*
226 227 * This function takes the raw DSL properties, and filters out the user-defined
227 228 * properties into a separate nvlist.
228 229 */
229 230 static nvlist_t *
230 231 process_user_props(zfs_handle_t *zhp, nvlist_t *props)
231 232 {
232 233 libzfs_handle_t *hdl = zhp->zfs_hdl;
233 234 nvpair_t *elem;
234 235 nvlist_t *propval;
235 236 nvlist_t *nvl;
236 237
237 238 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
238 239 (void) no_memory(hdl);
239 240 return (NULL);
240 241 }
241 242
242 243 elem = NULL;
243 244 while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
244 245 if (!zfs_prop_user(nvpair_name(elem)))
245 246 continue;
246 247
247 248 verify(nvpair_value_nvlist(elem, &propval) == 0);
248 249 if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
249 250 nvlist_free(nvl);
250 251 (void) no_memory(hdl);
251 252 return (NULL);
252 253 }
253 254 }
254 255
255 256 return (nvl);
256 257 }
257 258
258 259 static zpool_handle_t *
259 260 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
260 261 {
261 262 libzfs_handle_t *hdl = zhp->zfs_hdl;
262 263 zpool_handle_t *zph;
263 264
264 265 if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
265 266 if (hdl->libzfs_pool_handles != NULL)
266 267 zph->zpool_next = hdl->libzfs_pool_handles;
267 268 hdl->libzfs_pool_handles = zph;
268 269 }
269 270 return (zph);
270 271 }
271 272
272 273 static zpool_handle_t *
273 274 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
274 275 {
275 276 libzfs_handle_t *hdl = zhp->zfs_hdl;
276 277 zpool_handle_t *zph = hdl->libzfs_pool_handles;
277 278
278 279 while ((zph != NULL) &&
279 280 (strncmp(pool_name, zpool_get_name(zph), len) != 0))
280 281 zph = zph->zpool_next;
281 282 return (zph);
282 283 }
283 284
284 285 /*
285 286 * Returns a handle to the pool that contains the provided dataset.
286 287 * If a handle to that pool already exists then that handle is returned.
287 288 * Otherwise, a new handle is created and added to the list of handles.
288 289 */
289 290 static zpool_handle_t *
290 291 zpool_handle(zfs_handle_t *zhp)
291 292 {
292 293 char *pool_name;
293 294 int len;
294 295 zpool_handle_t *zph;
295 296
296 297 len = strcspn(zhp->zfs_name, "/@") + 1;
297 298 pool_name = zfs_alloc(zhp->zfs_hdl, len);
298 299 (void) strlcpy(pool_name, zhp->zfs_name, len);
299 300
300 301 zph = zpool_find_handle(zhp, pool_name, len);
301 302 if (zph == NULL)
302 303 zph = zpool_add_handle(zhp, pool_name);
303 304
304 305 free(pool_name);
305 306 return (zph);
306 307 }
307 308
308 309 void
309 310 zpool_free_handles(libzfs_handle_t *hdl)
310 311 {
311 312 zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
312 313
313 314 while (zph != NULL) {
314 315 next = zph->zpool_next;
315 316 zpool_close(zph);
316 317 zph = next;
317 318 }
318 319 hdl->libzfs_pool_handles = NULL;
319 320 }
320 321
321 322 /*
322 323 * Utility function to gather stats (objset and zpl) for the given object.
323 324 */
324 325 static int
325 326 get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
326 327 {
327 328 libzfs_handle_t *hdl = zhp->zfs_hdl;
328 329
329 330 (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
330 331
331 332 while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
332 333 if (errno == ENOMEM) {
333 334 if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
334 335 return (-1);
335 336 }
336 337 } else {
337 338 return (-1);
338 339 }
339 340 }
340 341 return (0);
341 342 }
342 343
343 344 /*
344 345 * Utility function to get the received properties of the given object.
345 346 */
346 347 static int
347 348 get_recvd_props_ioctl(zfs_handle_t *zhp)
348 349 {
349 350 libzfs_handle_t *hdl = zhp->zfs_hdl;
350 351 nvlist_t *recvdprops;
351 352 zfs_cmd_t zc = { 0 };
352 353 int err;
353 354
354 355 if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
355 356 return (-1);
356 357
357 358 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
358 359
359 360 while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
360 361 if (errno == ENOMEM) {
361 362 if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
362 363 return (-1);
363 364 }
364 365 } else {
365 366 zcmd_free_nvlists(&zc);
366 367 return (-1);
367 368 }
368 369 }
369 370
370 371 err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
371 372 zcmd_free_nvlists(&zc);
372 373 if (err != 0)
373 374 return (-1);
374 375
375 376 nvlist_free(zhp->zfs_recvd_props);
376 377 zhp->zfs_recvd_props = recvdprops;
377 378
378 379 return (0);
379 380 }
380 381
381 382 static int
382 383 put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
383 384 {
384 385 nvlist_t *allprops, *userprops;
385 386
386 387 zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
387 388
388 389 if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
389 390 return (-1);
390 391 }
391 392
392 393 /*
393 394 * XXX Why do we store the user props separately, in addition to
394 395 * storing them in zfs_props?
395 396 */
396 397 if ((userprops = process_user_props(zhp, allprops)) == NULL) {
397 398 nvlist_free(allprops);
398 399 return (-1);
399 400 }
400 401
401 402 nvlist_free(zhp->zfs_props);
402 403 nvlist_free(zhp->zfs_user_props);
403 404
404 405 zhp->zfs_props = allprops;
405 406 zhp->zfs_user_props = userprops;
406 407
407 408 return (0);
408 409 }
409 410
410 411 static int
411 412 get_stats(zfs_handle_t *zhp)
412 413 {
413 414 int rc = 0;
414 415 zfs_cmd_t zc = { 0 };
415 416
416 417 if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
417 418 return (-1);
418 419 if (get_stats_ioctl(zhp, &zc) != 0)
419 420 rc = -1;
420 421 else if (put_stats_zhdl(zhp, &zc) != 0)
421 422 rc = -1;
422 423 zcmd_free_nvlists(&zc);
423 424 return (rc);
424 425 }
425 426
426 427 /*
427 428 * Refresh the properties currently stored in the handle.
428 429 */
429 430 void
430 431 zfs_refresh_properties(zfs_handle_t *zhp)
431 432 {
432 433 (void) get_stats(zhp);
433 434 }
434 435
435 436 /*
436 437 * Makes a handle from the given dataset name. Used by zfs_open() and
437 438 * zfs_iter_* to create child handles on the fly.
438 439 */
439 440 static int
440 441 make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
441 442 {
442 443 if (put_stats_zhdl(zhp, zc) != 0)
443 444 return (-1);
444 445
445 446 /*
446 447 * We've managed to open the dataset and gather statistics. Determine
447 448 * the high-level type.
448 449 */
449 450 if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
450 451 zhp->zfs_head_type = ZFS_TYPE_VOLUME;
451 452 else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
452 453 zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
453 454 else
454 455 abort();
455 456
456 457 if (zhp->zfs_dmustats.dds_is_snapshot)
457 458 zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
458 459 else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
459 460 zhp->zfs_type = ZFS_TYPE_VOLUME;
460 461 else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
461 462 zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
462 463 else
463 464 abort(); /* we should never see any other types */
464 465
465 466 if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)
466 467 return (-1);
467 468
468 469 return (0);
469 470 }
470 471
471 472 zfs_handle_t *
472 473 make_dataset_handle(libzfs_handle_t *hdl, const char *path)
473 474 {
474 475 zfs_cmd_t zc = { 0 };
475 476
476 477 zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
477 478
478 479 if (zhp == NULL)
479 480 return (NULL);
480 481
481 482 zhp->zfs_hdl = hdl;
482 483 (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
483 484 if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
484 485 free(zhp);
485 486 return (NULL);
486 487 }
487 488 if (get_stats_ioctl(zhp, &zc) == -1) {
488 489 zcmd_free_nvlists(&zc);
489 490 free(zhp);
490 491 return (NULL);
491 492 }
492 493 if (make_dataset_handle_common(zhp, &zc) == -1) {
493 494 free(zhp);
494 495 zhp = NULL;
495 496 }
496 497 zcmd_free_nvlists(&zc);
497 498 return (zhp);
498 499 }
499 500
500 501 zfs_handle_t *
501 502 make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
502 503 {
503 504 zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
504 505
505 506 if (zhp == NULL)
506 507 return (NULL);
507 508
508 509 zhp->zfs_hdl = hdl;
509 510 (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
510 511 if (make_dataset_handle_common(zhp, zc) == -1) {
511 512 free(zhp);
512 513 return (NULL);
513 514 }
514 515 return (zhp);
515 516 }
516 517
517 518 zfs_handle_t *
518 519 zfs_handle_dup(zfs_handle_t *zhp_orig)
519 520 {
520 521 zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
521 522
522 523 if (zhp == NULL)
523 524 return (NULL);
524 525
525 526 zhp->zfs_hdl = zhp_orig->zfs_hdl;
526 527 zhp->zpool_hdl = zhp_orig->zpool_hdl;
527 528 (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
528 529 sizeof (zhp->zfs_name));
529 530 zhp->zfs_type = zhp_orig->zfs_type;
530 531 zhp->zfs_head_type = zhp_orig->zfs_head_type;
531 532 zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
532 533 if (zhp_orig->zfs_props != NULL) {
533 534 if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
534 535 (void) no_memory(zhp->zfs_hdl);
535 536 zfs_close(zhp);
536 537 return (NULL);
537 538 }
538 539 }
539 540 if (zhp_orig->zfs_user_props != NULL) {
540 541 if (nvlist_dup(zhp_orig->zfs_user_props,
541 542 &zhp->zfs_user_props, 0) != 0) {
542 543 (void) no_memory(zhp->zfs_hdl);
543 544 zfs_close(zhp);
544 545 return (NULL);
545 546 }
546 547 }
547 548 if (zhp_orig->zfs_recvd_props != NULL) {
548 549 if (nvlist_dup(zhp_orig->zfs_recvd_props,
549 550 &zhp->zfs_recvd_props, 0)) {
550 551 (void) no_memory(zhp->zfs_hdl);
551 552 zfs_close(zhp);
552 553 return (NULL);
553 554 }
554 555 }
555 556 zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
556 557 if (zhp_orig->zfs_mntopts != NULL) {
557 558 zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
558 559 zhp_orig->zfs_mntopts);
559 560 }
560 561 zhp->zfs_props_table = zhp_orig->zfs_props_table;
561 562 return (zhp);
562 563 }
563 564
564 565 /*
565 566 * Opens the given snapshot, filesystem, or volume. The 'types'
566 567 * argument is a mask of acceptable types. The function will print an
567 568 * appropriate error message and return NULL if it can't be opened.
568 569 */
569 570 zfs_handle_t *
570 571 zfs_open(libzfs_handle_t *hdl, const char *path, int types)
571 572 {
572 573 zfs_handle_t *zhp;
573 574 char errbuf[1024];
574 575
575 576 (void) snprintf(errbuf, sizeof (errbuf),
576 577 dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
577 578
578 579 /*
579 580 * Validate the name before we even try to open it.
580 581 */
581 582 if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
582 583 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
583 584 "invalid dataset name"));
584 585 (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
585 586 return (NULL);
586 587 }
587 588
588 589 /*
589 590 * Try to get stats for the dataset, which will tell us if it exists.
590 591 */
591 592 errno = 0;
592 593 if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
593 594 (void) zfs_standard_error(hdl, errno, errbuf);
594 595 return (NULL);
595 596 }
596 597
597 598 if (!(types & zhp->zfs_type)) {
598 599 (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
599 600 zfs_close(zhp);
600 601 return (NULL);
601 602 }
602 603
603 604 return (zhp);
604 605 }
605 606
606 607 /*
607 608 * Release a ZFS handle. Nothing to do but free the associated memory.
608 609 */
609 610 void
610 611 zfs_close(zfs_handle_t *zhp)
611 612 {
612 613 if (zhp->zfs_mntopts)
613 614 free(zhp->zfs_mntopts);
614 615 nvlist_free(zhp->zfs_props);
615 616 nvlist_free(zhp->zfs_user_props);
616 617 nvlist_free(zhp->zfs_recvd_props);
617 618 free(zhp);
618 619 }
619 620
620 621 typedef struct mnttab_node {
621 622 struct mnttab mtn_mt;
622 623 avl_node_t mtn_node;
623 624 } mnttab_node_t;
624 625
625 626 static int
626 627 libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
627 628 {
628 629 const mnttab_node_t *mtn1 = arg1;
629 630 const mnttab_node_t *mtn2 = arg2;
630 631 int rv;
631 632
632 633 rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
633 634
634 635 if (rv == 0)
635 636 return (0);
636 637 return (rv > 0 ? 1 : -1);
637 638 }
638 639
639 640 void
640 641 libzfs_mnttab_init(libzfs_handle_t *hdl)
641 642 {
642 643 assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
643 644 avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
644 645 sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
645 646 }
646 647
647 648 void
648 649 libzfs_mnttab_update(libzfs_handle_t *hdl)
649 650 {
650 651 struct mnttab entry;
651 652
652 653 rewind(hdl->libzfs_mnttab);
653 654 while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
654 655 mnttab_node_t *mtn;
655 656
656 657 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
657 658 continue;
658 659 mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
659 660 mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
660 661 mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
661 662 mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
662 663 mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
663 664 avl_add(&hdl->libzfs_mnttab_cache, mtn);
664 665 }
665 666 }
666 667
667 668 void
668 669 libzfs_mnttab_fini(libzfs_handle_t *hdl)
669 670 {
670 671 void *cookie = NULL;
671 672 mnttab_node_t *mtn;
672 673
673 674 while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
674 675 free(mtn->mtn_mt.mnt_special);
675 676 free(mtn->mtn_mt.mnt_mountp);
676 677 free(mtn->mtn_mt.mnt_fstype);
677 678 free(mtn->mtn_mt.mnt_mntopts);
678 679 free(mtn);
679 680 }
680 681 avl_destroy(&hdl->libzfs_mnttab_cache);
681 682 }
682 683
683 684 void
684 685 libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
685 686 {
686 687 hdl->libzfs_mnttab_enable = enable;
687 688 }
688 689
689 690 int
690 691 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
691 692 struct mnttab *entry)
692 693 {
693 694 mnttab_node_t find;
694 695 mnttab_node_t *mtn;
695 696
696 697 if (!hdl->libzfs_mnttab_enable) {
697 698 struct mnttab srch = { 0 };
698 699
699 700 if (avl_numnodes(&hdl->libzfs_mnttab_cache))
700 701 libzfs_mnttab_fini(hdl);
701 702 rewind(hdl->libzfs_mnttab);
702 703 srch.mnt_special = (char *)fsname;
703 704 srch.mnt_fstype = MNTTYPE_ZFS;
704 705 if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
705 706 return (0);
706 707 else
707 708 return (ENOENT);
708 709 }
709 710
710 711 if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
711 712 libzfs_mnttab_update(hdl);
712 713
713 714 find.mtn_mt.mnt_special = (char *)fsname;
714 715 mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
715 716 if (mtn) {
716 717 *entry = mtn->mtn_mt;
717 718 return (0);
718 719 }
719 720 return (ENOENT);
720 721 }
721 722
722 723 void
723 724 libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
724 725 const char *mountp, const char *mntopts)
725 726 {
726 727 mnttab_node_t *mtn;
727 728
728 729 if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
729 730 return;
730 731 mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
731 732 mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
732 733 mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
733 734 mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
734 735 mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
735 736 avl_add(&hdl->libzfs_mnttab_cache, mtn);
736 737 }
737 738
738 739 void
739 740 libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
740 741 {
741 742 mnttab_node_t find;
742 743 mnttab_node_t *ret;
743 744
744 745 find.mtn_mt.mnt_special = (char *)fsname;
745 746 if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
746 747 avl_remove(&hdl->libzfs_mnttab_cache, ret);
747 748 free(ret->mtn_mt.mnt_special);
748 749 free(ret->mtn_mt.mnt_mountp);
749 750 free(ret->mtn_mt.mnt_fstype);
750 751 free(ret->mtn_mt.mnt_mntopts);
751 752 free(ret);
752 753 }
753 754 }
754 755
755 756 int
756 757 zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
757 758 {
758 759 zpool_handle_t *zpool_handle = zhp->zpool_hdl;
759 760
760 761 if (zpool_handle == NULL)
761 762 return (-1);
762 763
763 764 *spa_version = zpool_get_prop_int(zpool_handle,
764 765 ZPOOL_PROP_VERSION, NULL);
765 766 return (0);
766 767 }
767 768
768 769 /*
769 770 * The choice of reservation property depends on the SPA version.
770 771 */
771 772 static int
772 773 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
773 774 {
774 775 int spa_version;
775 776
776 777 if (zfs_spa_version(zhp, &spa_version) < 0)
777 778 return (-1);
778 779
779 780 if (spa_version >= SPA_VERSION_REFRESERVATION)
780 781 *resv_prop = ZFS_PROP_REFRESERVATION;
781 782 else
782 783 *resv_prop = ZFS_PROP_RESERVATION;
783 784
784 785 return (0);
785 786 }
786 787
787 788 /*
788 789 * Given an nvlist of properties to set, validates that they are correct, and
789 790 * parses any numeric properties (index, boolean, etc) if they are specified as
790 791 * strings.
791 792 */
792 793 nvlist_t *
793 794 zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
794 795 uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
795 796 {
796 797 nvpair_t *elem;
797 798 uint64_t intval;
798 799 char *strval;
799 800 zfs_prop_t prop;
800 801 nvlist_t *ret;
801 802 int chosen_normal = -1;
802 803 int chosen_utf = -1;
803 804
804 805 if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
805 806 (void) no_memory(hdl);
806 807 return (NULL);
807 808 }
808 809
809 810 /*
810 811 * Make sure this property is valid and applies to this type.
811 812 */
812 813
813 814 elem = NULL;
814 815 while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
815 816 const char *propname = nvpair_name(elem);
816 817
817 818 prop = zfs_name_to_prop(propname);
818 819 if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
819 820 /*
820 821 * This is a user property: make sure it's a
821 822 * string, and that it's less than ZAP_MAXNAMELEN.
822 823 */
823 824 if (nvpair_type(elem) != DATA_TYPE_STRING) {
824 825 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
825 826 "'%s' must be a string"), propname);
826 827 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
827 828 goto error;
828 829 }
829 830
830 831 if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
831 832 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
832 833 "property name '%s' is too long"),
833 834 propname);
834 835 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
835 836 goto error;
836 837 }
837 838
838 839 (void) nvpair_value_string(elem, &strval);
839 840 if (nvlist_add_string(ret, propname, strval) != 0) {
840 841 (void) no_memory(hdl);
841 842 goto error;
842 843 }
843 844 continue;
844 845 }
845 846
846 847 /*
847 848 * Currently, only user properties can be modified on
848 849 * snapshots.
849 850 */
850 851 if (type == ZFS_TYPE_SNAPSHOT) {
851 852 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
852 853 "this property can not be modified for snapshots"));
853 854 (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
854 855 goto error;
855 856 }
856 857
857 858 if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
858 859 zfs_userquota_prop_t uqtype;
859 860 char newpropname[128];
860 861 char domain[128];
861 862 uint64_t rid;
862 863 uint64_t valary[3];
863 864
864 865 if (userquota_propname_decode(propname, zoned,
865 866 &uqtype, domain, sizeof (domain), &rid) != 0) {
866 867 zfs_error_aux(hdl,
867 868 dgettext(TEXT_DOMAIN,
868 869 "'%s' has an invalid user/group name"),
869 870 propname);
870 871 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
871 872 goto error;
872 873 }
873 874
874 875 if (uqtype != ZFS_PROP_USERQUOTA &&
875 876 uqtype != ZFS_PROP_GROUPQUOTA) {
876 877 zfs_error_aux(hdl,
877 878 dgettext(TEXT_DOMAIN, "'%s' is readonly"),
878 879 propname);
879 880 (void) zfs_error(hdl, EZFS_PROPREADONLY,
880 881 errbuf);
881 882 goto error;
882 883 }
883 884
884 885 if (nvpair_type(elem) == DATA_TYPE_STRING) {
885 886 (void) nvpair_value_string(elem, &strval);
886 887 if (strcmp(strval, "none") == 0) {
887 888 intval = 0;
888 889 } else if (zfs_nicestrtonum(hdl,
889 890 strval, &intval) != 0) {
890 891 (void) zfs_error(hdl,
891 892 EZFS_BADPROP, errbuf);
892 893 goto error;
893 894 }
894 895 } else if (nvpair_type(elem) ==
895 896 DATA_TYPE_UINT64) {
896 897 (void) nvpair_value_uint64(elem, &intval);
897 898 if (intval == 0) {
898 899 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
899 900 "use 'none' to disable "
900 901 "userquota/groupquota"));
901 902 goto error;
902 903 }
903 904 } else {
904 905 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
905 906 "'%s' must be a number"), propname);
906 907 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
907 908 goto error;
908 909 }
909 910
910 911 /*
911 912 * Encode the prop name as
912 913 * userquota@<hex-rid>-domain, to make it easy
913 914 * for the kernel to decode.
914 915 */
915 916 (void) snprintf(newpropname, sizeof (newpropname),
916 917 "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
917 918 (longlong_t)rid, domain);
918 919 valary[0] = uqtype;
919 920 valary[1] = rid;
920 921 valary[2] = intval;
921 922 if (nvlist_add_uint64_array(ret, newpropname,
922 923 valary, 3) != 0) {
923 924 (void) no_memory(hdl);
924 925 goto error;
925 926 }
926 927 continue;
927 928 } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
928 929 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
929 930 "'%s' is readonly"),
930 931 propname);
931 932 (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
932 933 goto error;
933 934 }
934 935
935 936 if (prop == ZPROP_INVAL) {
936 937 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
937 938 "invalid property '%s'"), propname);
938 939 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
939 940 goto error;
940 941 }
941 942
942 943 if (!zfs_prop_valid_for_type(prop, type)) {
943 944 zfs_error_aux(hdl,
944 945 dgettext(TEXT_DOMAIN, "'%s' does not "
945 946 "apply to datasets of this type"), propname);
946 947 (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
947 948 goto error;
948 949 }
949 950
950 951 if (zfs_prop_readonly(prop) &&
951 952 (!zfs_prop_setonce(prop) || zhp != NULL)) {
952 953 zfs_error_aux(hdl,
953 954 dgettext(TEXT_DOMAIN, "'%s' is readonly"),
954 955 propname);
955 956 (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
956 957 goto error;
957 958 }
958 959
959 960 if (zprop_parse_value(hdl, elem, prop, type, ret,
960 961 &strval, &intval, errbuf) != 0)
961 962 goto error;
962 963
963 964 /*
964 965 * Perform some additional checks for specific properties.
965 966 */
966 967 switch (prop) {
967 968 case ZFS_PROP_VERSION:
968 969 {
969 970 int version;
970 971
971 972 if (zhp == NULL)
972 973 break;
973 974 version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
974 975 if (intval < version) {
975 976 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
976 977 "Can not downgrade; already at version %u"),
977 978 version);
978 979 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
979 980 goto error;
980 981 }
981 982 break;
982 983 }
983 984
984 985 case ZFS_PROP_RECORDSIZE:
985 986 case ZFS_PROP_VOLBLOCKSIZE:
986 987 /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
987 988 if (intval < SPA_MINBLOCKSIZE ||
988 989 intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
989 990 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
990 991 "'%s' must be power of 2 from %u "
991 992 "to %uk"), propname,
992 993 (uint_t)SPA_MINBLOCKSIZE,
993 994 (uint_t)SPA_MAXBLOCKSIZE >> 10);
994 995 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
995 996 goto error;
996 997 }
997 998 break;
998 999
999 1000 case ZFS_PROP_MLSLABEL:
1000 1001 {
1001 1002 /*
1002 1003 * Verify the mlslabel string and convert to
1003 1004 * internal hex label string.
1004 1005 */
1005 1006
1006 1007 m_label_t *new_sl;
1007 1008 char *hex = NULL; /* internal label string */
1008 1009
1009 1010 /* Default value is already OK. */
1010 1011 if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
1011 1012 break;
1012 1013
1013 1014 /* Verify the label can be converted to binary form */
1014 1015 if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
1015 1016 (str_to_label(strval, &new_sl, MAC_LABEL,
1016 1017 L_NO_CORRECTION, NULL) == -1)) {
1017 1018 goto badlabel;
1018 1019 }
1019 1020
1020 1021 /* Now translate to hex internal label string */
1021 1022 if (label_to_str(new_sl, &hex, M_INTERNAL,
1022 1023 DEF_NAMES) != 0) {
1023 1024 if (hex)
1024 1025 free(hex);
1025 1026 goto badlabel;
1026 1027 }
1027 1028 m_label_free(new_sl);
1028 1029
1029 1030 /* If string is already in internal form, we're done. */
1030 1031 if (strcmp(strval, hex) == 0) {
1031 1032 free(hex);
1032 1033 break;
1033 1034 }
1034 1035
1035 1036 /* Replace the label string with the internal form. */
1036 1037 (void) nvlist_remove(ret, zfs_prop_to_name(prop),
1037 1038 DATA_TYPE_STRING);
1038 1039 verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
1039 1040 hex) == 0);
1040 1041 free(hex);
1041 1042
1042 1043 break;
1043 1044
1044 1045 badlabel:
1045 1046 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1046 1047 "invalid mlslabel '%s'"), strval);
1047 1048 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1048 1049 m_label_free(new_sl); /* OK if null */
1049 1050 goto error;
1050 1051
1051 1052 }
1052 1053
1053 1054 case ZFS_PROP_MOUNTPOINT:
1054 1055 {
1055 1056 namecheck_err_t why;
1056 1057
1057 1058 if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
1058 1059 strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
1059 1060 break;
1060 1061
1061 1062 if (mountpoint_namecheck(strval, &why)) {
1062 1063 switch (why) {
1063 1064 case NAME_ERR_LEADING_SLASH:
1064 1065 zfs_error_aux(hdl,
1065 1066 dgettext(TEXT_DOMAIN,
1066 1067 "'%s' must be an absolute path, "
1067 1068 "'none', or 'legacy'"), propname);
1068 1069 break;
1069 1070 case NAME_ERR_TOOLONG:
1070 1071 zfs_error_aux(hdl,
1071 1072 dgettext(TEXT_DOMAIN,
1072 1073 "component of '%s' is too long"),
1073 1074 propname);
1074 1075 break;
1075 1076 }
1076 1077 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1077 1078 goto error;
1078 1079 }
1079 1080 }
1080 1081
1081 1082 /*FALLTHRU*/
1082 1083
1083 1084 case ZFS_PROP_SHARESMB:
1084 1085 case ZFS_PROP_SHARENFS:
1085 1086 /*
1086 1087 * For the mountpoint and sharenfs or sharesmb
1087 1088 * properties, check if it can be set in a
1088 1089 * global/non-global zone based on
1089 1090 * the zoned property value:
1090 1091 *
1091 1092 * global zone non-global zone
1092 1093 * --------------------------------------------------
1093 1094 * zoned=on mountpoint (no) mountpoint (yes)
1094 1095 * sharenfs (no) sharenfs (no)
1095 1096 * sharesmb (no) sharesmb (no)
1096 1097 *
1097 1098 * zoned=off mountpoint (yes) N/A
1098 1099 * sharenfs (yes)
1099 1100 * sharesmb (yes)
1100 1101 */
1101 1102 if (zoned) {
1102 1103 if (getzoneid() == GLOBAL_ZONEID) {
1103 1104 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1104 1105 "'%s' cannot be set on "
1105 1106 "dataset in a non-global zone"),
1106 1107 propname);
1107 1108 (void) zfs_error(hdl, EZFS_ZONED,
1108 1109 errbuf);
1109 1110 goto error;
1110 1111 } else if (prop == ZFS_PROP_SHARENFS ||
1111 1112 prop == ZFS_PROP_SHARESMB) {
1112 1113 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1113 1114 "'%s' cannot be set in "
1114 1115 "a non-global zone"), propname);
1115 1116 (void) zfs_error(hdl, EZFS_ZONED,
1116 1117 errbuf);
1117 1118 goto error;
1118 1119 }
1119 1120 } else if (getzoneid() != GLOBAL_ZONEID) {
1120 1121 /*
1121 1122 * If zoned property is 'off', this must be in
1122 1123 * a global zone. If not, something is wrong.
1123 1124 */
1124 1125 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1125 1126 "'%s' cannot be set while dataset "
1126 1127 "'zoned' property is set"), propname);
1127 1128 (void) zfs_error(hdl, EZFS_ZONED, errbuf);
1128 1129 goto error;
1129 1130 }
1130 1131
1131 1132 /*
1132 1133 * At this point, it is legitimate to set the
1133 1134 * property. Now we want to make sure that the
1134 1135 * property value is valid if it is sharenfs.
1135 1136 */
1136 1137 if ((prop == ZFS_PROP_SHARENFS ||
1137 1138 prop == ZFS_PROP_SHARESMB) &&
1138 1139 strcmp(strval, "on") != 0 &&
1139 1140 strcmp(strval, "off") != 0) {
1140 1141 zfs_share_proto_t proto;
1141 1142
1142 1143 if (prop == ZFS_PROP_SHARESMB)
1143 1144 proto = PROTO_SMB;
1144 1145 else
1145 1146 proto = PROTO_NFS;
1146 1147
1147 1148 /*
1148 1149 * Must be an valid sharing protocol
1149 1150 * option string so init the libshare
1150 1151 * in order to enable the parser and
1151 1152 * then parse the options. We use the
1152 1153 * control API since we don't care about
1153 1154 * the current configuration and don't
1154 1155 * want the overhead of loading it
1155 1156 * until we actually do something.
1156 1157 */
1157 1158
1158 1159 if (zfs_init_libshare(hdl,
1159 1160 SA_INIT_CONTROL_API) != SA_OK) {
1160 1161 /*
1161 1162 * An error occurred so we can't do
1162 1163 * anything
1163 1164 */
1164 1165 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1165 1166 "'%s' cannot be set: problem "
1166 1167 "in share initialization"),
1167 1168 propname);
1168 1169 (void) zfs_error(hdl, EZFS_BADPROP,
1169 1170 errbuf);
1170 1171 goto error;
1171 1172 }
1172 1173
1173 1174 if (zfs_parse_options(strval, proto) != SA_OK) {
1174 1175 /*
1175 1176 * There was an error in parsing so
1176 1177 * deal with it by issuing an error
1177 1178 * message and leaving after
1178 1179 * uninitializing the the libshare
1179 1180 * interface.
1180 1181 */
1181 1182 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1182 1183 "'%s' cannot be set to invalid "
1183 1184 "options"), propname);
1184 1185 (void) zfs_error(hdl, EZFS_BADPROP,
1185 1186 errbuf);
1186 1187 zfs_uninit_libshare(hdl);
1187 1188 goto error;
1188 1189 }
1189 1190 zfs_uninit_libshare(hdl);
1190 1191 }
1191 1192
1192 1193 break;
1193 1194 case ZFS_PROP_UTF8ONLY:
1194 1195 chosen_utf = (int)intval;
1195 1196 break;
1196 1197 case ZFS_PROP_NORMALIZE:
1197 1198 chosen_normal = (int)intval;
1198 1199 break;
1199 1200 }
1200 1201
1201 1202 /*
1202 1203 * For changes to existing volumes, we have some additional
1203 1204 * checks to enforce.
1204 1205 */
1205 1206 if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1206 1207 uint64_t volsize = zfs_prop_get_int(zhp,
1207 1208 ZFS_PROP_VOLSIZE);
1208 1209 uint64_t blocksize = zfs_prop_get_int(zhp,
1209 1210 ZFS_PROP_VOLBLOCKSIZE);
1210 1211 char buf[64];
1211 1212
1212 1213 switch (prop) {
1213 1214 case ZFS_PROP_RESERVATION:
1214 1215 case ZFS_PROP_REFRESERVATION:
1215 1216 if (intval > volsize) {
1216 1217 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1217 1218 "'%s' is greater than current "
1218 1219 "volume size"), propname);
1219 1220 (void) zfs_error(hdl, EZFS_BADPROP,
1220 1221 errbuf);
1221 1222 goto error;
1222 1223 }
1223 1224 break;
1224 1225
1225 1226 case ZFS_PROP_VOLSIZE:
1226 1227 if (intval % blocksize != 0) {
1227 1228 zfs_nicenum(blocksize, buf,
1228 1229 sizeof (buf));
1229 1230 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1230 1231 "'%s' must be a multiple of "
1231 1232 "volume block size (%s)"),
1232 1233 propname, buf);
1233 1234 (void) zfs_error(hdl, EZFS_BADPROP,
1234 1235 errbuf);
1235 1236 goto error;
1236 1237 }
1237 1238
1238 1239 if (intval == 0) {
1239 1240 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1240 1241 "'%s' cannot be zero"),
1241 1242 propname);
1242 1243 (void) zfs_error(hdl, EZFS_BADPROP,
1243 1244 errbuf);
1244 1245 goto error;
1245 1246 }
1246 1247 break;
1247 1248 }
1248 1249 }
1249 1250 }
1250 1251
1251 1252 /*
1252 1253 * If normalization was chosen, but no UTF8 choice was made,
1253 1254 * enforce rejection of non-UTF8 names.
1254 1255 *
1255 1256 * If normalization was chosen, but rejecting non-UTF8 names
1256 1257 * was explicitly not chosen, it is an error.
1257 1258 */
1258 1259 if (chosen_normal > 0 && chosen_utf < 0) {
1259 1260 if (nvlist_add_uint64(ret,
1260 1261 zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
1261 1262 (void) no_memory(hdl);
1262 1263 goto error;
1263 1264 }
1264 1265 } else if (chosen_normal > 0 && chosen_utf == 0) {
1265 1266 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1266 1267 "'%s' must be set 'on' if normalization chosen"),
1267 1268 zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1268 1269 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1269 1270 goto error;
1270 1271 }
1271 1272 return (ret);
1272 1273
1273 1274 error:
1274 1275 nvlist_free(ret);
1275 1276 return (NULL);
1276 1277 }
1277 1278
1278 1279 int
1279 1280 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1280 1281 {
1281 1282 uint64_t old_volsize;
1282 1283 uint64_t new_volsize;
1283 1284 uint64_t old_reservation;
1284 1285 uint64_t new_reservation;
1285 1286 zfs_prop_t resv_prop;
1286 1287 nvlist_t *props;
1287 1288
1288 1289 /*
1289 1290 * If this is an existing volume, and someone is setting the volsize,
1290 1291 * make sure that it matches the reservation, or add it if necessary.
1291 1292 */
1292 1293 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1293 1294 if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1294 1295 return (-1);
1295 1296 old_reservation = zfs_prop_get_int(zhp, resv_prop);
1296 1297
1297 1298 props = fnvlist_alloc();
1298 1299 fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
1299 1300 zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
1300 1301
1301 1302 if ((zvol_volsize_to_reservation(old_volsize, props) !=
1302 1303 old_reservation) || nvlist_exists(nvl,
1303 1304 zfs_prop_to_name(resv_prop))) {
1304 1305 fnvlist_free(props);
1305 1306 return (0);
1306 1307 }
1307 1308 if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1308 1309 &new_volsize) != 0) {
1309 1310 fnvlist_free(props);
1310 1311 return (-1);
1311 1312 }
1312 1313 new_reservation = zvol_volsize_to_reservation(new_volsize, props);
1313 1314 fnvlist_free(props);
1314 1315
1315 1316 if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1316 1317 new_reservation) != 0) {
1317 1318 (void) no_memory(zhp->zfs_hdl);
1318 1319 return (-1);
1319 1320 }
1320 1321 return (1);
1321 1322 }
1322 1323
1323 1324 void
1324 1325 zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
1325 1326 char *errbuf)
1326 1327 {
1327 1328 switch (err) {
1328 1329
1329 1330 case ENOSPC:
1330 1331 /*
1331 1332 * For quotas and reservations, ENOSPC indicates
1332 1333 * something different; setting a quota or reservation
1333 1334 * doesn't use any disk space.
1334 1335 */
1335 1336 switch (prop) {
1336 1337 case ZFS_PROP_QUOTA:
1337 1338 case ZFS_PROP_REFQUOTA:
1338 1339 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1339 1340 "size is less than current used or "
1340 1341 "reserved space"));
1341 1342 (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1342 1343 break;
1343 1344
1344 1345 case ZFS_PROP_RESERVATION:
1345 1346 case ZFS_PROP_REFRESERVATION:
1346 1347 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1347 1348 "size is greater than available space"));
1348 1349 (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1349 1350 break;
1350 1351
1351 1352 default:
1352 1353 (void) zfs_standard_error(hdl, err, errbuf);
1353 1354 break;
1354 1355 }
1355 1356 break;
1356 1357
1357 1358 case EBUSY:
1358 1359 (void) zfs_standard_error(hdl, EBUSY, errbuf);
1359 1360 break;
1360 1361
1361 1362 case EROFS:
1362 1363 (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
1363 1364 break;
1364 1365
1365 1366 case ENOTSUP:
1366 1367 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1367 1368 "pool and or dataset must be upgraded to set this "
1368 1369 "property or value"));
1369 1370 (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
1370 1371 break;
1371 1372
1372 1373 case ERANGE:
1373 1374 if (prop == ZFS_PROP_COMPRESSION) {
1374 1375 (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1375 1376 "property setting is not allowed on "
1376 1377 "bootable datasets"));
1377 1378 (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
1378 1379 } else {
1379 1380 (void) zfs_standard_error(hdl, err, errbuf);
1380 1381 }
1381 1382 break;
1382 1383
1383 1384 case EINVAL:
1384 1385 if (prop == ZPROP_INVAL) {
1385 1386 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1386 1387 } else {
1387 1388 (void) zfs_standard_error(hdl, err, errbuf);
1388 1389 }
1389 1390 break;
1390 1391
1391 1392 case EOVERFLOW:
1392 1393 /*
1393 1394 * This platform can't address a volume this big.
1394 1395 */
1395 1396 #ifdef _ILP32
1396 1397 if (prop == ZFS_PROP_VOLSIZE) {
1397 1398 (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1398 1399 break;
1399 1400 }
1400 1401 #endif
1401 1402 /* FALLTHROUGH */
1402 1403 default:
1403 1404 (void) zfs_standard_error(hdl, err, errbuf);
1404 1405 }
1405 1406 }
1406 1407
1407 1408 /*
1408 1409 * Given a property name and value, set the property for the given dataset.
1409 1410 */
1410 1411 int
1411 1412 zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1412 1413 {
1413 1414 zfs_cmd_t zc = { 0 };
1414 1415 int ret = -1;
1415 1416 prop_changelist_t *cl = NULL;
1416 1417 char errbuf[1024];
1417 1418 libzfs_handle_t *hdl = zhp->zfs_hdl;
1418 1419 nvlist_t *nvl = NULL, *realprops;
1419 1420 zfs_prop_t prop;
1420 1421 boolean_t do_prefix = B_TRUE;
1421 1422 int added_resv;
1422 1423
1423 1424 (void) snprintf(errbuf, sizeof (errbuf),
1424 1425 dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
1425 1426 zhp->zfs_name);
1426 1427
1427 1428 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1428 1429 nvlist_add_string(nvl, propname, propval) != 0) {
1429 1430 (void) no_memory(hdl);
1430 1431 goto error;
1431 1432 }
1432 1433
1433 1434 if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
1434 1435 zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1435 1436 goto error;
1436 1437
1437 1438 nvlist_free(nvl);
1438 1439 nvl = realprops;
1439 1440
1440 1441 prop = zfs_name_to_prop(propname);
1441 1442
1442 1443 if (prop == ZFS_PROP_VOLSIZE) {
1443 1444 if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1)
1444 1445 goto error;
1445 1446 }
1446 1447
1447 1448 if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1448 1449 goto error;
1449 1450
1450 1451 if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1451 1452 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1452 1453 "child dataset with inherited mountpoint is used "
1453 1454 "in a non-global zone"));
1454 1455 ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1455 1456 goto error;
1456 1457 }
1457 1458
1458 1459 /*
1459 1460 * We don't want to unmount & remount the dataset when changing
1460 1461 * its canmount property to 'on' or 'noauto'. We only use
1461 1462 * the changelist logic to unmount when setting canmount=off.
1462 1463 */
1463 1464 if (prop == ZFS_PROP_CANMOUNT) {
1464 1465 uint64_t idx;
1465 1466 int err = zprop_string_to_index(prop, propval, &idx,
1466 1467 ZFS_TYPE_DATASET);
1467 1468 if (err == 0 && idx != ZFS_CANMOUNT_OFF)
1468 1469 do_prefix = B_FALSE;
1469 1470 }
1470 1471
1471 1472 if (do_prefix && (ret = changelist_prefix(cl)) != 0)
1472 1473 goto error;
1473 1474
1474 1475 /*
1475 1476 * Execute the corresponding ioctl() to set this property.
1476 1477 */
1477 1478 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1478 1479
1479 1480 if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1480 1481 goto error;
1481 1482
1482 1483 ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1483 1484
1484 1485 if (ret != 0) {
1485 1486 zfs_setprop_error(hdl, prop, errno, errbuf);
1486 1487 if (added_resv && errno == ENOSPC) {
1487 1488 /* clean up the volsize property we tried to set */
1488 1489 uint64_t old_volsize = zfs_prop_get_int(zhp,
1489 1490 ZFS_PROP_VOLSIZE);
1490 1491 nvlist_free(nvl);
1491 1492 zcmd_free_nvlists(&zc);
1492 1493 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
1493 1494 goto error;
1494 1495 if (nvlist_add_uint64(nvl,
1495 1496 zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1496 1497 old_volsize) != 0)
1497 1498 goto error;
1498 1499 if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1499 1500 goto error;
1500 1501 (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1501 1502 }
1502 1503 } else {
1503 1504 if (do_prefix)
1504 1505 ret = changelist_postfix(cl);
1505 1506
1506 1507 /*
1507 1508 * Refresh the statistics so the new property value
1508 1509 * is reflected.
1509 1510 */
1510 1511 if (ret == 0)
1511 1512 (void) get_stats(zhp);
1512 1513 }
1513 1514
1514 1515 error:
1515 1516 nvlist_free(nvl);
1516 1517 zcmd_free_nvlists(&zc);
1517 1518 if (cl)
1518 1519 changelist_free(cl);
1519 1520 return (ret);
1520 1521 }
1521 1522
1522 1523 /*
1523 1524 * Given a property, inherit the value from the parent dataset, or if received
1524 1525 * is TRUE, revert to the received value, if any.
1525 1526 */
1526 1527 int
1527 1528 zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
1528 1529 {
1529 1530 zfs_cmd_t zc = { 0 };
1530 1531 int ret;
1531 1532 prop_changelist_t *cl;
1532 1533 libzfs_handle_t *hdl = zhp->zfs_hdl;
1533 1534 char errbuf[1024];
1534 1535 zfs_prop_t prop;
1535 1536
1536 1537 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1537 1538 "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1538 1539
1539 1540 zc.zc_cookie = received;
1540 1541 if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
1541 1542 /*
1542 1543 * For user properties, the amount of work we have to do is very
1543 1544 * small, so just do it here.
1544 1545 */
1545 1546 if (!zfs_prop_user(propname)) {
1546 1547 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1547 1548 "invalid property"));
1548 1549 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1549 1550 }
1550 1551
1551 1552 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1552 1553 (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1553 1554
1554 1555 if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
1555 1556 return (zfs_standard_error(hdl, errno, errbuf));
1556 1557
1557 1558 return (0);
1558 1559 }
1559 1560
1560 1561 /*
1561 1562 * Verify that this property is inheritable.
1562 1563 */
1563 1564 if (zfs_prop_readonly(prop))
1564 1565 return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1565 1566
1566 1567 if (!zfs_prop_inheritable(prop) && !received)
1567 1568 return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1568 1569
1569 1570 /*
1570 1571 * Check to see if the value applies to this type
1571 1572 */
1572 1573 if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1573 1574 return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1574 1575
1575 1576 /*
1576 1577 * Normalize the name, to get rid of shorthand abbreviations.
1577 1578 */
1578 1579 propname = zfs_prop_to_name(prop);
1579 1580 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1580 1581 (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1581 1582
1582 1583 if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1583 1584 zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
1584 1585 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1585 1586 "dataset is used in a non-global zone"));
1586 1587 return (zfs_error(hdl, EZFS_ZONED, errbuf));
1587 1588 }
1588 1589
1589 1590 /*
1590 1591 * Determine datasets which will be affected by this change, if any.
1591 1592 */
1592 1593 if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1593 1594 return (-1);
1594 1595
1595 1596 if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1596 1597 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1597 1598 "child dataset with inherited mountpoint is used "
1598 1599 "in a non-global zone"));
1599 1600 ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1600 1601 goto error;
1601 1602 }
1602 1603
1603 1604 if ((ret = changelist_prefix(cl)) != 0)
1604 1605 goto error;
1605 1606
1606 1607 if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
1607 1608 return (zfs_standard_error(hdl, errno, errbuf));
1608 1609 } else {
1609 1610
1610 1611 if ((ret = changelist_postfix(cl)) != 0)
1611 1612 goto error;
1612 1613
1613 1614 /*
1614 1615 * Refresh the statistics so the new property is reflected.
1615 1616 */
1616 1617 (void) get_stats(zhp);
1617 1618 }
1618 1619
1619 1620 error:
1620 1621 changelist_free(cl);
1621 1622 return (ret);
1622 1623 }
1623 1624
1624 1625 /*
1625 1626 * True DSL properties are stored in an nvlist. The following two functions
1626 1627 * extract them appropriately.
1627 1628 */
1628 1629 static uint64_t
1629 1630 getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1630 1631 {
1631 1632 nvlist_t *nv;
1632 1633 uint64_t value;
1633 1634
1634 1635 *source = NULL;
1635 1636 if (nvlist_lookup_nvlist(zhp->zfs_props,
1636 1637 zfs_prop_to_name(prop), &nv) == 0) {
1637 1638 verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
1638 1639 (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1639 1640 } else {
1640 1641 verify(!zhp->zfs_props_table ||
1641 1642 zhp->zfs_props_table[prop] == B_TRUE);
1642 1643 value = zfs_prop_default_numeric(prop);
1643 1644 *source = "";
1644 1645 }
1645 1646
1646 1647 return (value);
1647 1648 }
1648 1649
1649 1650 static char *
1650 1651 getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1651 1652 {
1652 1653 nvlist_t *nv;
1653 1654 char *value;
1654 1655
1655 1656 *source = NULL;
1656 1657 if (nvlist_lookup_nvlist(zhp->zfs_props,
1657 1658 zfs_prop_to_name(prop), &nv) == 0) {
1658 1659 verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
1659 1660 (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1660 1661 } else {
1661 1662 verify(!zhp->zfs_props_table ||
1662 1663 zhp->zfs_props_table[prop] == B_TRUE);
1663 1664 if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
1664 1665 value = "";
1665 1666 *source = "";
1666 1667 }
1667 1668
1668 1669 return (value);
1669 1670 }
1670 1671
1671 1672 static boolean_t
1672 1673 zfs_is_recvd_props_mode(zfs_handle_t *zhp)
1673 1674 {
1674 1675 return (zhp->zfs_props == zhp->zfs_recvd_props);
1675 1676 }
1676 1677
1677 1678 static void
1678 1679 zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1679 1680 {
1680 1681 *cookie = (uint64_t)(uintptr_t)zhp->zfs_props;
1681 1682 zhp->zfs_props = zhp->zfs_recvd_props;
1682 1683 }
1683 1684
1684 1685 static void
1685 1686 zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1686 1687 {
1687 1688 zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie;
1688 1689 *cookie = 0;
1689 1690 }
1690 1691
1691 1692 /*
1692 1693 * Internal function for getting a numeric property. Both zfs_prop_get() and
1693 1694 * zfs_prop_get_int() are built using this interface.
1694 1695 *
1695 1696 * Certain properties can be overridden using 'mount -o'. In this case, scan
1696 1697 * the contents of the /etc/mnttab entry, searching for the appropriate options.
1697 1698 * If they differ from the on-disk values, report the current values and mark
1698 1699 * the source "temporary".
1699 1700 */
1700 1701 static int
1701 1702 get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
1702 1703 char **source, uint64_t *val)
1703 1704 {
1704 1705 zfs_cmd_t zc = { 0 };
1705 1706 nvlist_t *zplprops = NULL;
1706 1707 struct mnttab mnt;
1707 1708 char *mntopt_on = NULL;
1708 1709 char *mntopt_off = NULL;
1709 1710 boolean_t received = zfs_is_recvd_props_mode(zhp);
1710 1711
1711 1712 *source = NULL;
1712 1713
1713 1714 switch (prop) {
1714 1715 case ZFS_PROP_ATIME:
1715 1716 mntopt_on = MNTOPT_ATIME;
1716 1717 mntopt_off = MNTOPT_NOATIME;
1717 1718 break;
1718 1719
1719 1720 case ZFS_PROP_DEVICES:
1720 1721 mntopt_on = MNTOPT_DEVICES;
1721 1722 mntopt_off = MNTOPT_NODEVICES;
1722 1723 break;
1723 1724
1724 1725 case ZFS_PROP_EXEC:
1725 1726 mntopt_on = MNTOPT_EXEC;
1726 1727 mntopt_off = MNTOPT_NOEXEC;
1727 1728 break;
1728 1729
1729 1730 case ZFS_PROP_READONLY:
1730 1731 mntopt_on = MNTOPT_RO;
1731 1732 mntopt_off = MNTOPT_RW;
1732 1733 break;
1733 1734
1734 1735 case ZFS_PROP_SETUID:
1735 1736 mntopt_on = MNTOPT_SETUID;
1736 1737 mntopt_off = MNTOPT_NOSETUID;
1737 1738 break;
1738 1739
1739 1740 case ZFS_PROP_XATTR:
1740 1741 mntopt_on = MNTOPT_XATTR;
1741 1742 mntopt_off = MNTOPT_NOXATTR;
1742 1743 break;
1743 1744
1744 1745 case ZFS_PROP_NBMAND:
1745 1746 mntopt_on = MNTOPT_NBMAND;
1746 1747 mntopt_off = MNTOPT_NONBMAND;
1747 1748 break;
1748 1749 }
1749 1750
1750 1751 /*
1751 1752 * Because looking up the mount options is potentially expensive
1752 1753 * (iterating over all of /etc/mnttab), we defer its calculation until
1753 1754 * we're looking up a property which requires its presence.
1754 1755 */
1755 1756 if (!zhp->zfs_mntcheck &&
1756 1757 (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
1757 1758 libzfs_handle_t *hdl = zhp->zfs_hdl;
1758 1759 struct mnttab entry;
1759 1760
1760 1761 if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
1761 1762 zhp->zfs_mntopts = zfs_strdup(hdl,
1762 1763 entry.mnt_mntopts);
1763 1764 if (zhp->zfs_mntopts == NULL)
1764 1765 return (-1);
1765 1766 }
1766 1767
1767 1768 zhp->zfs_mntcheck = B_TRUE;
1768 1769 }
1769 1770
1770 1771 if (zhp->zfs_mntopts == NULL)
1771 1772 mnt.mnt_mntopts = "";
1772 1773 else
1773 1774 mnt.mnt_mntopts = zhp->zfs_mntopts;
1774 1775
1775 1776 switch (prop) {
1776 1777 case ZFS_PROP_ATIME:
1777 1778 case ZFS_PROP_DEVICES:
1778 1779 case ZFS_PROP_EXEC:
1779 1780 case ZFS_PROP_READONLY:
1780 1781 case ZFS_PROP_SETUID:
1781 1782 case ZFS_PROP_XATTR:
1782 1783 case ZFS_PROP_NBMAND:
1783 1784 *val = getprop_uint64(zhp, prop, source);
1784 1785
1785 1786 if (received)
1786 1787 break;
1787 1788
1788 1789 if (hasmntopt(&mnt, mntopt_on) && !*val) {
1789 1790 *val = B_TRUE;
1790 1791 if (src)
1791 1792 *src = ZPROP_SRC_TEMPORARY;
1792 1793 } else if (hasmntopt(&mnt, mntopt_off) && *val) {
1793 1794 *val = B_FALSE;
1794 1795 if (src)
1795 1796 *src = ZPROP_SRC_TEMPORARY;
1796 1797 }
1797 1798 break;
1798 1799
1799 1800 case ZFS_PROP_CANMOUNT:
1800 1801 case ZFS_PROP_VOLSIZE:
1801 1802 case ZFS_PROP_QUOTA:
1802 1803 case ZFS_PROP_REFQUOTA:
1803 1804 case ZFS_PROP_RESERVATION:
1804 1805 case ZFS_PROP_REFRESERVATION:
1805 1806 *val = getprop_uint64(zhp, prop, source);
1806 1807
1807 1808 if (*source == NULL) {
1808 1809 /* not default, must be local */
1809 1810 *source = zhp->zfs_name;
1810 1811 }
1811 1812 break;
1812 1813
1813 1814 case ZFS_PROP_MOUNTED:
1814 1815 *val = (zhp->zfs_mntopts != NULL);
1815 1816 break;
1816 1817
1817 1818 case ZFS_PROP_NUMCLONES:
1818 1819 *val = zhp->zfs_dmustats.dds_num_clones;
1819 1820 break;
1820 1821
1821 1822 case ZFS_PROP_VERSION:
1822 1823 case ZFS_PROP_NORMALIZE:
1823 1824 case ZFS_PROP_UTF8ONLY:
1824 1825 case ZFS_PROP_CASE:
1825 1826 if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
1826 1827 zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
1827 1828 return (-1);
1828 1829 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1829 1830 if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
1830 1831 zcmd_free_nvlists(&zc);
1831 1832 return (-1);
1832 1833 }
1833 1834 if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
1834 1835 nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
1835 1836 val) != 0) {
1836 1837 zcmd_free_nvlists(&zc);
1837 1838 return (-1);
1838 1839 }
1839 1840 if (zplprops)
1840 1841 nvlist_free(zplprops);
1841 1842 zcmd_free_nvlists(&zc);
1842 1843 break;
1843 1844
1844 1845 default:
1845 1846 switch (zfs_prop_get_type(prop)) {
1846 1847 case PROP_TYPE_NUMBER:
1847 1848 case PROP_TYPE_INDEX:
1848 1849 *val = getprop_uint64(zhp, prop, source);
1849 1850 /*
1850 1851 * If we tried to use a default value for a
1851 1852 * readonly property, it means that it was not
1852 1853 * present.
1853 1854 */
1854 1855 if (zfs_prop_readonly(prop) &&
1855 1856 *source != NULL && (*source)[0] == '\0') {
1856 1857 *source = NULL;
1857 1858 }
1858 1859 break;
1859 1860
1860 1861 case PROP_TYPE_STRING:
1861 1862 default:
1862 1863 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1863 1864 "cannot get non-numeric property"));
1864 1865 return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
1865 1866 dgettext(TEXT_DOMAIN, "internal error")));
1866 1867 }
1867 1868 }
1868 1869
1869 1870 return (0);
1870 1871 }
1871 1872
1872 1873 /*
1873 1874 * Calculate the source type, given the raw source string.
1874 1875 */
1875 1876 static void
1876 1877 get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
1877 1878 char *statbuf, size_t statlen)
1878 1879 {
1879 1880 if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
1880 1881 return;
1881 1882
1882 1883 if (source == NULL) {
1883 1884 *srctype = ZPROP_SRC_NONE;
1884 1885 } else if (source[0] == '\0') {
1885 1886 *srctype = ZPROP_SRC_DEFAULT;
1886 1887 } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {
1887 1888 *srctype = ZPROP_SRC_RECEIVED;
1888 1889 } else {
1889 1890 if (strcmp(source, zhp->zfs_name) == 0) {
1890 1891 *srctype = ZPROP_SRC_LOCAL;
1891 1892 } else {
1892 1893 (void) strlcpy(statbuf, source, statlen);
1893 1894 *srctype = ZPROP_SRC_INHERITED;
1894 1895 }
1895 1896 }
1896 1897
1897 1898 }
1898 1899
1899 1900 int
1900 1901 zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
1901 1902 size_t proplen, boolean_t literal)
1902 1903 {
1903 1904 zfs_prop_t prop;
1904 1905 int err = 0;
1905 1906
1906 1907 if (zhp->zfs_recvd_props == NULL)
1907 1908 if (get_recvd_props_ioctl(zhp) != 0)
1908 1909 return (-1);
1909 1910
1910 1911 prop = zfs_name_to_prop(propname);
1911 1912
1912 1913 if (prop != ZPROP_INVAL) {
1913 1914 uint64_t cookie;
1914 1915 if (!nvlist_exists(zhp->zfs_recvd_props, propname))
1915 1916 return (-1);
1916 1917 zfs_set_recvd_props_mode(zhp, &cookie);
1917 1918 err = zfs_prop_get(zhp, prop, propbuf, proplen,
1918 1919 NULL, NULL, 0, literal);
1919 1920 zfs_unset_recvd_props_mode(zhp, &cookie);
1920 1921 } else {
1921 1922 nvlist_t *propval;
1922 1923 char *recvdval;
1923 1924 if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
1924 1925 propname, &propval) != 0)
1925 1926 return (-1);
1926 1927 verify(nvlist_lookup_string(propval, ZPROP_VALUE,
1927 1928 &recvdval) == 0);
1928 1929 (void) strlcpy(propbuf, recvdval, proplen);
1929 1930 }
1930 1931
1931 1932 return (err == 0 ? 0 : -1);
1932 1933 }
1933 1934
1934 1935 static int
1935 1936 get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
1936 1937 {
1937 1938 nvlist_t *value;
1938 1939 nvpair_t *pair;
1939 1940
1940 1941 value = zfs_get_clones_nvl(zhp);
1941 1942 if (value == NULL)
1942 1943 return (-1);
1943 1944
1944 1945 propbuf[0] = '\0';
1945 1946 for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
1946 1947 pair = nvlist_next_nvpair(value, pair)) {
1947 1948 if (propbuf[0] != '\0')
1948 1949 (void) strlcat(propbuf, ",", proplen);
1949 1950 (void) strlcat(propbuf, nvpair_name(pair), proplen);
1950 1951 }
1951 1952
1952 1953 return (0);
1953 1954 }
1954 1955
1955 1956 struct get_clones_arg {
1956 1957 uint64_t numclones;
1957 1958 nvlist_t *value;
1958 1959 const char *origin;
1959 1960 char buf[ZFS_MAXNAMELEN];
1960 1961 };
1961 1962
1962 1963 int
1963 1964 get_clones_cb(zfs_handle_t *zhp, void *arg)
1964 1965 {
1965 1966 struct get_clones_arg *gca = arg;
1966 1967
1967 1968 if (gca->numclones == 0) {
1968 1969 zfs_close(zhp);
1969 1970 return (0);
1970 1971 }
1971 1972
1972 1973 if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
1973 1974 NULL, NULL, 0, B_TRUE) != 0)
1974 1975 goto out;
1975 1976 if (strcmp(gca->buf, gca->origin) == 0) {
1976 1977 fnvlist_add_boolean(gca->value, zfs_get_name(zhp));
1977 1978 gca->numclones--;
1978 1979 }
1979 1980
1980 1981 out:
1981 1982 (void) zfs_iter_children(zhp, get_clones_cb, gca);
1982 1983 zfs_close(zhp);
1983 1984 return (0);
1984 1985 }
1985 1986
1986 1987 nvlist_t *
1987 1988 zfs_get_clones_nvl(zfs_handle_t *zhp)
1988 1989 {
1989 1990 nvlist_t *nv, *value;
1990 1991
1991 1992 if (nvlist_lookup_nvlist(zhp->zfs_props,
1992 1993 zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
1993 1994 struct get_clones_arg gca;
1994 1995
1995 1996 /*
1996 1997 * if this is a snapshot, then the kernel wasn't able
1997 1998 * to get the clones. Do it by slowly iterating.
1998 1999 */
1999 2000 if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
2000 2001 return (NULL);
2001 2002 if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
2002 2003 return (NULL);
2003 2004 if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
2004 2005 nvlist_free(nv);
2005 2006 return (NULL);
2006 2007 }
2007 2008
2008 2009 gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
2009 2010 gca.value = value;
2010 2011 gca.origin = zhp->zfs_name;
2011 2012
2012 2013 if (gca.numclones != 0) {
2013 2014 zfs_handle_t *root;
2014 2015 char pool[ZFS_MAXNAMELEN];
2015 2016 char *cp = pool;
2016 2017
2017 2018 /* get the pool name */
2018 2019 (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
2019 2020 (void) strsep(&cp, "/@");
2020 2021 root = zfs_open(zhp->zfs_hdl, pool,
2021 2022 ZFS_TYPE_FILESYSTEM);
2022 2023
2023 2024 (void) get_clones_cb(root, &gca);
2024 2025 }
2025 2026
2026 2027 if (gca.numclones != 0 ||
2027 2028 nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
2028 2029 nvlist_add_nvlist(zhp->zfs_props,
2029 2030 zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
2030 2031 nvlist_free(nv);
2031 2032 nvlist_free(value);
2032 2033 return (NULL);
2033 2034 }
2034 2035 nvlist_free(nv);
2035 2036 nvlist_free(value);
2036 2037 verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
2037 2038 zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
2038 2039 }
2039 2040
2040 2041 verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
2041 2042
2042 2043 return (value);
2043 2044 }
2044 2045
2045 2046 /*
2046 2047 * Retrieve a property from the given object. If 'literal' is specified, then
2047 2048 * numbers are left as exact values. Otherwise, numbers are converted to a
2048 2049 * human-readable form.
2049 2050 *
2050 2051 * Returns 0 on success, or -1 on error.
2051 2052 */
2052 2053 int
2053 2054 zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
2054 2055 zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
2055 2056 {
2056 2057 char *source = NULL;
2057 2058 uint64_t val;
2058 2059 char *str;
2059 2060 const char *strval;
2060 2061 boolean_t received = zfs_is_recvd_props_mode(zhp);
2061 2062
2062 2063 /*
2063 2064 * Check to see if this property applies to our object
2064 2065 */
2065 2066 if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
2066 2067 return (-1);
2067 2068
2068 2069 if (received && zfs_prop_readonly(prop))
2069 2070 return (-1);
2070 2071
2071 2072 if (src)
2072 2073 *src = ZPROP_SRC_NONE;
2073 2074
2074 2075 switch (prop) {
2075 2076 case ZFS_PROP_CREATION:
2076 2077 /*
2077 2078 * 'creation' is a time_t stored in the statistics. We convert
2078 2079 * this into a string unless 'literal' is specified.
2079 2080 */
2080 2081 {
2081 2082 val = getprop_uint64(zhp, prop, &source);
2082 2083 time_t time = (time_t)val;
2083 2084 struct tm t;
2084 2085
2085 2086 if (literal ||
2086 2087 localtime_r(&time, &t) == NULL ||
2087 2088 strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
2088 2089 &t) == 0)
2089 2090 (void) snprintf(propbuf, proplen, "%llu", val);
2090 2091 }
2091 2092 break;
2092 2093
2093 2094 case ZFS_PROP_MOUNTPOINT:
2094 2095 /*
2095 2096 * Getting the precise mountpoint can be tricky.
2096 2097 *
2097 2098 * - for 'none' or 'legacy', return those values.
2098 2099 * - for inherited mountpoints, we want to take everything
2099 2100 * after our ancestor and append it to the inherited value.
2100 2101 *
2101 2102 * If the pool has an alternate root, we want to prepend that
2102 2103 * root to any values we return.
2103 2104 */
2104 2105
2105 2106 str = getprop_string(zhp, prop, &source);
2106 2107
2107 2108 if (str[0] == '/') {
2108 2109 char buf[MAXPATHLEN];
2109 2110 char *root = buf;
2110 2111 const char *relpath;
2111 2112
2112 2113 /*
2113 2114 * If we inherit the mountpoint, even from a dataset
2114 2115 * with a received value, the source will be the path of
2115 2116 * the dataset we inherit from. If source is
2116 2117 * ZPROP_SOURCE_VAL_RECVD, the received value is not
2117 2118 * inherited.
2118 2119 */
2119 2120 if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {
2120 2121 relpath = "";
2121 2122 } else {
2122 2123 relpath = zhp->zfs_name + strlen(source);
2123 2124 if (relpath[0] == '/')
2124 2125 relpath++;
2125 2126 }
2126 2127
2127 2128 if ((zpool_get_prop(zhp->zpool_hdl,
2128 2129 ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
2129 2130 (strcmp(root, "-") == 0))
2130 2131 root[0] = '\0';
2131 2132 /*
2132 2133 * Special case an alternate root of '/'. This will
2133 2134 * avoid having multiple leading slashes in the
2134 2135 * mountpoint path.
2135 2136 */
2136 2137 if (strcmp(root, "/") == 0)
2137 2138 root++;
2138 2139
2139 2140 /*
2140 2141 * If the mountpoint is '/' then skip over this
2141 2142 * if we are obtaining either an alternate root or
2142 2143 * an inherited mountpoint.
2143 2144 */
2144 2145 if (str[1] == '\0' && (root[0] != '\0' ||
2145 2146 relpath[0] != '\0'))
2146 2147 str++;
2147 2148
2148 2149 if (relpath[0] == '\0')
2149 2150 (void) snprintf(propbuf, proplen, "%s%s",
2150 2151 root, str);
2151 2152 else
2152 2153 (void) snprintf(propbuf, proplen, "%s%s%s%s",
2153 2154 root, str, relpath[0] == '@' ? "" : "/",
2154 2155 relpath);
2155 2156 } else {
2156 2157 /* 'legacy' or 'none' */
2157 2158 (void) strlcpy(propbuf, str, proplen);
2158 2159 }
2159 2160
2160 2161 break;
2161 2162
2162 2163 case ZFS_PROP_ORIGIN:
2163 2164 (void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
2164 2165 proplen);
2165 2166 /*
2166 2167 * If there is no parent at all, return failure to indicate that
2167 2168 * it doesn't apply to this dataset.
2168 2169 */
2169 2170 if (propbuf[0] == '\0')
2170 2171 return (-1);
2171 2172 break;
2172 2173
2173 2174 case ZFS_PROP_CLONES:
2174 2175 if (get_clones_string(zhp, propbuf, proplen) != 0)
2175 2176 return (-1);
2176 2177 break;
2177 2178
2178 2179 case ZFS_PROP_QUOTA:
2179 2180 case ZFS_PROP_REFQUOTA:
2180 2181 case ZFS_PROP_RESERVATION:
2181 2182 case ZFS_PROP_REFRESERVATION:
2182 2183
2183 2184 if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2184 2185 return (-1);
2185 2186
2186 2187 /*
2187 2188 * If quota or reservation is 0, we translate this into 'none'
2188 2189 * (unless literal is set), and indicate that it's the default
2189 2190 * value. Otherwise, we print the number nicely and indicate
2190 2191 * that its set locally.
2191 2192 */
2192 2193 if (val == 0) {
2193 2194 if (literal)
2194 2195 (void) strlcpy(propbuf, "0", proplen);
2195 2196 else
2196 2197 (void) strlcpy(propbuf, "none", proplen);
2197 2198 } else {
2198 2199 if (literal)
2199 2200 (void) snprintf(propbuf, proplen, "%llu",
2200 2201 (u_longlong_t)val);
2201 2202 else
2202 2203 zfs_nicenum(val, propbuf, proplen);
2203 2204 }
2204 2205 break;
2205 2206
2206 2207 case ZFS_PROP_REFRATIO:
2207 2208 case ZFS_PROP_COMPRESSRATIO:
2208 2209 if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2209 2210 return (-1);
2210 2211 (void) snprintf(propbuf, proplen, "%llu.%02llux",
2211 2212 (u_longlong_t)(val / 100),
2212 2213 (u_longlong_t)(val % 100));
2213 2214 break;
2214 2215
2215 2216 case ZFS_PROP_TYPE:
2216 2217 switch (zhp->zfs_type) {
2217 2218 case ZFS_TYPE_FILESYSTEM:
2218 2219 str = "filesystem";
2219 2220 break;
2220 2221 case ZFS_TYPE_VOLUME:
2221 2222 str = "volume";
2222 2223 break;
2223 2224 case ZFS_TYPE_SNAPSHOT:
2224 2225 str = "snapshot";
2225 2226 break;
2226 2227 default:
2227 2228 abort();
2228 2229 }
2229 2230 (void) snprintf(propbuf, proplen, "%s", str);
2230 2231 break;
2231 2232
2232 2233 case ZFS_PROP_MOUNTED:
2233 2234 /*
2234 2235 * The 'mounted' property is a pseudo-property that described
2235 2236 * whether the filesystem is currently mounted. Even though
2236 2237 * it's a boolean value, the typical values of "on" and "off"
2237 2238 * don't make sense, so we translate to "yes" and "no".
2238 2239 */
2239 2240 if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
2240 2241 src, &source, &val) != 0)
2241 2242 return (-1);
2242 2243 if (val)
2243 2244 (void) strlcpy(propbuf, "yes", proplen);
2244 2245 else
2245 2246 (void) strlcpy(propbuf, "no", proplen);
2246 2247 break;
2247 2248
2248 2249 case ZFS_PROP_NAME:
2249 2250 /*
2250 2251 * The 'name' property is a pseudo-property derived from the
2251 2252 * dataset name. It is presented as a real property to simplify
2252 2253 * consumers.
2253 2254 */
2254 2255 (void) strlcpy(propbuf, zhp->zfs_name, proplen);
2255 2256 break;
2256 2257
2257 2258 case ZFS_PROP_MLSLABEL:
2258 2259 {
2259 2260 m_label_t *new_sl = NULL;
2260 2261 char *ascii = NULL; /* human readable label */
2261 2262
2262 2263 (void) strlcpy(propbuf,
2263 2264 getprop_string(zhp, prop, &source), proplen);
2264 2265
2265 2266 if (literal || (strcasecmp(propbuf,
2266 2267 ZFS_MLSLABEL_DEFAULT) == 0))
2267 2268 break;
2268 2269
2269 2270 /*
2270 2271 * Try to translate the internal hex string to
2271 2272 * human-readable output. If there are any
2272 2273 * problems just use the hex string.
2273 2274 */
2274 2275
2275 2276 if (str_to_label(propbuf, &new_sl, MAC_LABEL,
2276 2277 L_NO_CORRECTION, NULL) == -1) {
2277 2278 m_label_free(new_sl);
2278 2279 break;
2279 2280 }
2280 2281
2281 2282 if (label_to_str(new_sl, &ascii, M_LABEL,
2282 2283 DEF_NAMES) != 0) {
2283 2284 if (ascii)
2284 2285 free(ascii);
2285 2286 m_label_free(new_sl);
2286 2287 break;
2287 2288 }
2288 2289 m_label_free(new_sl);
2289 2290
2290 2291 (void) strlcpy(propbuf, ascii, proplen);
2291 2292 free(ascii);
2292 2293 }
2293 2294 break;
2294 2295
2295 2296 case ZFS_PROP_GUID:
2296 2297 /*
2297 2298 * GUIDs are stored as numbers, but they are identifiers.
2298 2299 * We don't want them to be pretty printed, because pretty
2299 2300 * printing mangles the ID into a truncated and useless value.
2300 2301 */
2301 2302 if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2302 2303 return (-1);
2303 2304 (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
2304 2305 break;
2305 2306
2306 2307 default:
2307 2308 switch (zfs_prop_get_type(prop)) {
2308 2309 case PROP_TYPE_NUMBER:
2309 2310 if (get_numeric_property(zhp, prop, src,
2310 2311 &source, &val) != 0)
2311 2312 return (-1);
2312 2313 if (literal)
2313 2314 (void) snprintf(propbuf, proplen, "%llu",
2314 2315 (u_longlong_t)val);
2315 2316 else
2316 2317 zfs_nicenum(val, propbuf, proplen);
2317 2318 break;
2318 2319
2319 2320 case PROP_TYPE_STRING:
2320 2321 (void) strlcpy(propbuf,
2321 2322 getprop_string(zhp, prop, &source), proplen);
2322 2323 break;
2323 2324
2324 2325 case PROP_TYPE_INDEX:
2325 2326 if (get_numeric_property(zhp, prop, src,
2326 2327 &source, &val) != 0)
2327 2328 return (-1);
2328 2329 if (zfs_prop_index_to_string(prop, val, &strval) != 0)
2329 2330 return (-1);
2330 2331 (void) strlcpy(propbuf, strval, proplen);
2331 2332 break;
2332 2333
2333 2334 default:
2334 2335 abort();
2335 2336 }
2336 2337 }
2337 2338
2338 2339 get_source(zhp, src, source, statbuf, statlen);
2339 2340
2340 2341 return (0);
2341 2342 }
2342 2343
2343 2344 /*
2344 2345 * Utility function to get the given numeric property. Does no validation that
2345 2346 * the given property is the appropriate type; should only be used with
2346 2347 * hard-coded property types.
2347 2348 */
2348 2349 uint64_t
2349 2350 zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
2350 2351 {
2351 2352 char *source;
2352 2353 uint64_t val;
2353 2354
2354 2355 (void) get_numeric_property(zhp, prop, NULL, &source, &val);
2355 2356
2356 2357 return (val);
2357 2358 }
2358 2359
2359 2360 int
2360 2361 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
2361 2362 {
2362 2363 char buf[64];
2363 2364
2364 2365 (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
2365 2366 return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
2366 2367 }
2367 2368
2368 2369 /*
2369 2370 * Similar to zfs_prop_get(), but returns the value as an integer.
2370 2371 */
2371 2372 int
2372 2373 zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
2373 2374 zprop_source_t *src, char *statbuf, size_t statlen)
2374 2375 {
2375 2376 char *source;
2376 2377
2377 2378 /*
2378 2379 * Check to see if this property applies to our object
2379 2380 */
2380 2381 if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
2381 2382 return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
2382 2383 dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
2383 2384 zfs_prop_to_name(prop)));
2384 2385 }
2385 2386
2386 2387 if (src)
2387 2388 *src = ZPROP_SRC_NONE;
2388 2389
2389 2390 if (get_numeric_property(zhp, prop, src, &source, value) != 0)
2390 2391 return (-1);
2391 2392
2392 2393 get_source(zhp, src, source, statbuf, statlen);
2393 2394
2394 2395 return (0);
2395 2396 }
2396 2397
2397 2398 static int
2398 2399 idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
2399 2400 char **domainp, idmap_rid_t *ridp)
2400 2401 {
2401 2402 idmap_get_handle_t *get_hdl = NULL;
2402 2403 idmap_stat status;
2403 2404 int err = EINVAL;
2404 2405
2405 2406 if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
2406 2407 goto out;
2407 2408
2408 2409 if (isuser) {
2409 2410 err = idmap_get_sidbyuid(get_hdl, id,
2410 2411 IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2411 2412 } else {
2412 2413 err = idmap_get_sidbygid(get_hdl, id,
2413 2414 IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2414 2415 }
2415 2416 if (err == IDMAP_SUCCESS &&
2416 2417 idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
2417 2418 status == IDMAP_SUCCESS)
2418 2419 err = 0;
2419 2420 else
2420 2421 err = EINVAL;
2421 2422 out:
2422 2423 if (get_hdl)
2423 2424 idmap_get_destroy(get_hdl);
2424 2425 return (err);
2425 2426 }
2426 2427
2427 2428 /*
2428 2429 * convert the propname into parameters needed by kernel
2429 2430 * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
2430 2431 * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
2431 2432 */
2432 2433 static int
2433 2434 userquota_propname_decode(const char *propname, boolean_t zoned,
2434 2435 zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
2435 2436 {
2436 2437 zfs_userquota_prop_t type;
2437 2438 char *cp, *end;
2438 2439 char *numericsid = NULL;
2439 2440 boolean_t isuser;
2440 2441
2441 2442 domain[0] = '\0';
2442 2443
2443 2444 /* Figure out the property type ({user|group}{quota|space}) */
2444 2445 for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
2445 2446 if (strncmp(propname, zfs_userquota_prop_prefixes[type],
2446 2447 strlen(zfs_userquota_prop_prefixes[type])) == 0)
2447 2448 break;
2448 2449 }
2449 2450 if (type == ZFS_NUM_USERQUOTA_PROPS)
2450 2451 return (EINVAL);
2451 2452 *typep = type;
2452 2453
2453 2454 isuser = (type == ZFS_PROP_USERQUOTA ||
2454 2455 type == ZFS_PROP_USERUSED);
2455 2456
2456 2457 cp = strchr(propname, '@') + 1;
2457 2458
2458 2459 if (strchr(cp, '@')) {
2459 2460 /*
2460 2461 * It's a SID name (eg "user@domain") that needs to be
2461 2462 * turned into S-1-domainID-RID.
2462 2463 */
2463 2464 directory_error_t e;
2464 2465 if (zoned && getzoneid() == GLOBAL_ZONEID)
2465 2466 return (ENOENT);
2466 2467 if (isuser) {
2467 2468 e = directory_sid_from_user_name(NULL,
2468 2469 cp, &numericsid);
2469 2470 } else {
2470 2471 e = directory_sid_from_group_name(NULL,
2471 2472 cp, &numericsid);
2472 2473 }
2473 2474 if (e != NULL) {
2474 2475 directory_error_free(e);
2475 2476 return (ENOENT);
2476 2477 }
2477 2478 if (numericsid == NULL)
2478 2479 return (ENOENT);
2479 2480 cp = numericsid;
2480 2481 /* will be further decoded below */
2481 2482 }
2482 2483
2483 2484 if (strncmp(cp, "S-1-", 4) == 0) {
2484 2485 /* It's a numeric SID (eg "S-1-234-567-89") */
2485 2486 (void) strlcpy(domain, cp, domainlen);
2486 2487 cp = strrchr(domain, '-');
2487 2488 *cp = '\0';
2488 2489 cp++;
2489 2490
2490 2491 errno = 0;
2491 2492 *ridp = strtoull(cp, &end, 10);
2492 2493 if (numericsid) {
2493 2494 free(numericsid);
2494 2495 numericsid = NULL;
2495 2496 }
2496 2497 if (errno != 0 || *end != '\0')
2497 2498 return (EINVAL);
2498 2499 } else if (!isdigit(*cp)) {
2499 2500 /*
2500 2501 * It's a user/group name (eg "user") that needs to be
2501 2502 * turned into a uid/gid
2502 2503 */
2503 2504 if (zoned && getzoneid() == GLOBAL_ZONEID)
2504 2505 return (ENOENT);
2505 2506 if (isuser) {
2506 2507 struct passwd *pw;
2507 2508 pw = getpwnam(cp);
2508 2509 if (pw == NULL)
2509 2510 return (ENOENT);
2510 2511 *ridp = pw->pw_uid;
2511 2512 } else {
2512 2513 struct group *gr;
2513 2514 gr = getgrnam(cp);
2514 2515 if (gr == NULL)
2515 2516 return (ENOENT);
2516 2517 *ridp = gr->gr_gid;
2517 2518 }
2518 2519 } else {
2519 2520 /* It's a user/group ID (eg "12345"). */
2520 2521 uid_t id = strtoul(cp, &end, 10);
2521 2522 idmap_rid_t rid;
2522 2523 char *mapdomain;
2523 2524
2524 2525 if (*end != '\0')
2525 2526 return (EINVAL);
2526 2527 if (id > MAXUID) {
2527 2528 /* It's an ephemeral ID. */
2528 2529 if (idmap_id_to_numeric_domain_rid(id, isuser,
2529 2530 &mapdomain, &rid) != 0)
2530 2531 return (ENOENT);
2531 2532 (void) strlcpy(domain, mapdomain, domainlen);
2532 2533 *ridp = rid;
2533 2534 } else {
2534 2535 *ridp = id;
2535 2536 }
2536 2537 }
2537 2538
2538 2539 ASSERT3P(numericsid, ==, NULL);
2539 2540 return (0);
2540 2541 }
2541 2542
2542 2543 static int
2543 2544 zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
2544 2545 uint64_t *propvalue, zfs_userquota_prop_t *typep)
2545 2546 {
2546 2547 int err;
2547 2548 zfs_cmd_t zc = { 0 };
2548 2549
2549 2550 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2550 2551
2551 2552 err = userquota_propname_decode(propname,
2552 2553 zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
2553 2554 typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
2554 2555 zc.zc_objset_type = *typep;
2555 2556 if (err)
2556 2557 return (err);
2557 2558
2558 2559 err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
2559 2560 if (err)
2560 2561 return (err);
2561 2562
2562 2563 *propvalue = zc.zc_cookie;
2563 2564 return (0);
2564 2565 }
2565 2566
2566 2567 int
2567 2568 zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
2568 2569 uint64_t *propvalue)
2569 2570 {
2570 2571 zfs_userquota_prop_t type;
2571 2572
2572 2573 return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
2573 2574 &type));
2574 2575 }
2575 2576
2576 2577 int
2577 2578 zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
2578 2579 char *propbuf, int proplen, boolean_t literal)
2579 2580 {
2580 2581 int err;
2581 2582 uint64_t propvalue;
2582 2583 zfs_userquota_prop_t type;
2583 2584
2584 2585 err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
2585 2586 &type);
2586 2587
2587 2588 if (err)
2588 2589 return (err);
2589 2590
2590 2591 if (literal) {
2591 2592 (void) snprintf(propbuf, proplen, "%llu", propvalue);
2592 2593 } else if (propvalue == 0 &&
2593 2594 (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
2594 2595 (void) strlcpy(propbuf, "none", proplen);
2595 2596 } else {
2596 2597 zfs_nicenum(propvalue, propbuf, proplen);
2597 2598 }
2598 2599 return (0);
2599 2600 }
2600 2601
2601 2602 int
2602 2603 zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
2603 2604 uint64_t *propvalue)
2604 2605 {
2605 2606 int err;
2606 2607 zfs_cmd_t zc = { 0 };
2607 2608 const char *snapname;
2608 2609
2609 2610 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2610 2611
2611 2612 snapname = strchr(propname, '@') + 1;
2612 2613 if (strchr(snapname, '@')) {
2613 2614 (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
2614 2615 } else {
2615 2616 /* snapname is the short name, append it to zhp's fsname */
2616 2617 char *cp;
2617 2618
2618 2619 (void) strlcpy(zc.zc_value, zhp->zfs_name,
2619 2620 sizeof (zc.zc_value));
2620 2621 cp = strchr(zc.zc_value, '@');
2621 2622 if (cp != NULL)
2622 2623 *cp = '\0';
2623 2624 (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
2624 2625 (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
2625 2626 }
2626 2627
2627 2628 err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
2628 2629 if (err)
2629 2630 return (err);
2630 2631
2631 2632 *propvalue = zc.zc_cookie;
2632 2633 return (0);
2633 2634 }
2634 2635
2635 2636 int
2636 2637 zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
2637 2638 char *propbuf, int proplen, boolean_t literal)
2638 2639 {
2639 2640 int err;
2640 2641 uint64_t propvalue;
2641 2642
2642 2643 err = zfs_prop_get_written_int(zhp, propname, &propvalue);
2643 2644
2644 2645 if (err)
2645 2646 return (err);
2646 2647
2647 2648 if (literal) {
2648 2649 (void) snprintf(propbuf, proplen, "%llu", propvalue);
2649 2650 } else {
2650 2651 zfs_nicenum(propvalue, propbuf, proplen);
2651 2652 }
2652 2653 return (0);
2653 2654 }
2654 2655
2655 2656 /*
2656 2657 * Returns the name of the given zfs handle.
2657 2658 */
2658 2659 const char *
2659 2660 zfs_get_name(const zfs_handle_t *zhp)
2660 2661 {
2661 2662 return (zhp->zfs_name);
2662 2663 }
2663 2664
2664 2665 /*
2665 2666 * Returns the type of the given zfs handle.
2666 2667 */
2667 2668 zfs_type_t
2668 2669 zfs_get_type(const zfs_handle_t *zhp)
2669 2670 {
2670 2671 return (zhp->zfs_type);
2671 2672 }
2672 2673
2673 2674 /*
2674 2675 * Is one dataset name a child dataset of another?
2675 2676 *
2676 2677 * Needs to handle these cases:
2677 2678 * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo"
2678 2679 * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar"
2679 2680 * Descendant? No. No. No. Yes.
2680 2681 */
2681 2682 static boolean_t
2682 2683 is_descendant(const char *ds1, const char *ds2)
2683 2684 {
2684 2685 size_t d1len = strlen(ds1);
2685 2686
2686 2687 /* ds2 can't be a descendant if it's smaller */
2687 2688 if (strlen(ds2) < d1len)
2688 2689 return (B_FALSE);
2689 2690
2690 2691 /* otherwise, compare strings and verify that there's a '/' char */
2691 2692 return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));
2692 2693 }
2693 2694
2694 2695 /*
2695 2696 * Given a complete name, return just the portion that refers to the parent.
2696 2697 * Will return -1 if there is no parent (path is just the name of the
2697 2698 * pool).
2698 2699 */
2699 2700 static int
2700 2701 parent_name(const char *path, char *buf, size_t buflen)
2701 2702 {
2702 2703 char *slashp;
2703 2704
2704 2705 (void) strlcpy(buf, path, buflen);
2705 2706
2706 2707 if ((slashp = strrchr(buf, '/')) == NULL)
2707 2708 return (-1);
2708 2709 *slashp = '\0';
2709 2710
2710 2711 return (0);
2711 2712 }
2712 2713
2713 2714 /*
2714 2715 * If accept_ancestor is false, then check to make sure that the given path has
2715 2716 * a parent, and that it exists. If accept_ancestor is true, then find the
2716 2717 * closest existing ancestor for the given path. In prefixlen return the
2717 2718 * length of already existing prefix of the given path. We also fetch the
2718 2719 * 'zoned' property, which is used to validate property settings when creating
2719 2720 * new datasets.
2720 2721 */
2721 2722 static int
2722 2723 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
2723 2724 boolean_t accept_ancestor, int *prefixlen)
2724 2725 {
2725 2726 zfs_cmd_t zc = { 0 };
2726 2727 char parent[ZFS_MAXNAMELEN];
2727 2728 char *slash;
2728 2729 zfs_handle_t *zhp;
2729 2730 char errbuf[1024];
2730 2731 uint64_t is_zoned;
2731 2732
2732 2733 (void) snprintf(errbuf, sizeof (errbuf),
2733 2734 dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
2734 2735
2735 2736 /* get parent, and check to see if this is just a pool */
2736 2737 if (parent_name(path, parent, sizeof (parent)) != 0) {
2737 2738 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2738 2739 "missing dataset name"));
2739 2740 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2740 2741 }
2741 2742
2742 2743 /* check to see if the pool exists */
2743 2744 if ((slash = strchr(parent, '/')) == NULL)
2744 2745 slash = parent + strlen(parent);
2745 2746 (void) strncpy(zc.zc_name, parent, slash - parent);
2746 2747 zc.zc_name[slash - parent] = '\0';
2747 2748 if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
2748 2749 errno == ENOENT) {
2749 2750 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2750 2751 "no such pool '%s'"), zc.zc_name);
2751 2752 return (zfs_error(hdl, EZFS_NOENT, errbuf));
2752 2753 }
2753 2754
2754 2755 /* check to see if the parent dataset exists */
2755 2756 while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
2756 2757 if (errno == ENOENT && accept_ancestor) {
2757 2758 /*
2758 2759 * Go deeper to find an ancestor, give up on top level.
2759 2760 */
2760 2761 if (parent_name(parent, parent, sizeof (parent)) != 0) {
2761 2762 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2762 2763 "no such pool '%s'"), zc.zc_name);
2763 2764 return (zfs_error(hdl, EZFS_NOENT, errbuf));
2764 2765 }
2765 2766 } else if (errno == ENOENT) {
2766 2767 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2767 2768 "parent does not exist"));
2768 2769 return (zfs_error(hdl, EZFS_NOENT, errbuf));
2769 2770 } else
2770 2771 return (zfs_standard_error(hdl, errno, errbuf));
2771 2772 }
2772 2773
2773 2774 is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2774 2775 if (zoned != NULL)
2775 2776 *zoned = is_zoned;
2776 2777
2777 2778 /* we are in a non-global zone, but parent is in the global zone */
2778 2779 if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {
2779 2780 (void) zfs_standard_error(hdl, EPERM, errbuf);
2780 2781 zfs_close(zhp);
2781 2782 return (-1);
2782 2783 }
2783 2784
2784 2785 /* make sure parent is a filesystem */
2785 2786 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2786 2787 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2787 2788 "parent is not a filesystem"));
2788 2789 (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
2789 2790 zfs_close(zhp);
2790 2791 return (-1);
2791 2792 }
2792 2793
2793 2794 zfs_close(zhp);
2794 2795 if (prefixlen != NULL)
2795 2796 *prefixlen = strlen(parent);
2796 2797 return (0);
2797 2798 }
2798 2799
2799 2800 /*
2800 2801 * Finds whether the dataset of the given type(s) exists.
2801 2802 */
2802 2803 boolean_t
2803 2804 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
2804 2805 {
2805 2806 zfs_handle_t *zhp;
2806 2807
2807 2808 if (!zfs_validate_name(hdl, path, types, B_FALSE))
2808 2809 return (B_FALSE);
2809 2810
2810 2811 /*
2811 2812 * Try to get stats for the dataset, which will tell us if it exists.
2812 2813 */
2813 2814 if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
2814 2815 int ds_type = zhp->zfs_type;
2815 2816
2816 2817 zfs_close(zhp);
2817 2818 if (types & ds_type)
2818 2819 return (B_TRUE);
2819 2820 }
2820 2821 return (B_FALSE);
2821 2822 }
2822 2823
2823 2824 /*
2824 2825 * Given a path to 'target', create all the ancestors between
2825 2826 * the prefixlen portion of the path, and the target itself.
2826 2827 * Fail if the initial prefixlen-ancestor does not already exist.
2827 2828 */
2828 2829 int
2829 2830 create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
2830 2831 {
2831 2832 zfs_handle_t *h;
2832 2833 char *cp;
2833 2834 const char *opname;
2834 2835
2835 2836 /* make sure prefix exists */
2836 2837 cp = target + prefixlen;
2837 2838 if (*cp != '/') {
2838 2839 assert(strchr(cp, '/') == NULL);
2839 2840 h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2840 2841 } else {
2841 2842 *cp = '\0';
2842 2843 h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2843 2844 *cp = '/';
2844 2845 }
2845 2846 if (h == NULL)
2846 2847 return (-1);
2847 2848 zfs_close(h);
2848 2849
2849 2850 /*
2850 2851 * Attempt to create, mount, and share any ancestor filesystems,
2851 2852 * up to the prefixlen-long one.
2852 2853 */
2853 2854 for (cp = target + prefixlen + 1;
2854 2855 cp = strchr(cp, '/'); *cp = '/', cp++) {
2855 2856
2856 2857 *cp = '\0';
2857 2858
2858 2859 h = make_dataset_handle(hdl, target);
2859 2860 if (h) {
2860 2861 /* it already exists, nothing to do here */
2861 2862 zfs_close(h);
2862 2863 continue;
2863 2864 }
2864 2865
2865 2866 if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2866 2867 NULL) != 0) {
2867 2868 opname = dgettext(TEXT_DOMAIN, "create");
2868 2869 goto ancestorerr;
2869 2870 }
2870 2871
2871 2872 h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2872 2873 if (h == NULL) {
2873 2874 opname = dgettext(TEXT_DOMAIN, "open");
2874 2875 goto ancestorerr;
2875 2876 }
2876 2877
2877 2878 if (zfs_mount(h, NULL, 0) != 0) {
2878 2879 opname = dgettext(TEXT_DOMAIN, "mount");
2879 2880 goto ancestorerr;
2880 2881 }
2881 2882
2882 2883 if (zfs_share(h) != 0) {
2883 2884 opname = dgettext(TEXT_DOMAIN, "share");
2884 2885 goto ancestorerr;
2885 2886 }
2886 2887
2887 2888 zfs_close(h);
2888 2889 }
2889 2890
2890 2891 return (0);
2891 2892
2892 2893 ancestorerr:
2893 2894 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2894 2895 "failed to %s ancestor '%s'"), opname, target);
2895 2896 return (-1);
2896 2897 }
2897 2898
2898 2899 /*
2899 2900 * Creates non-existing ancestors of the given path.
2900 2901 */
2901 2902 int
2902 2903 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
2903 2904 {
2904 2905 int prefix;
2905 2906 char *path_copy;
2906 2907 int rc;
2907 2908
2908 2909 if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
2909 2910 return (-1);
2910 2911
2911 2912 if ((path_copy = strdup(path)) != NULL) {
2912 2913 rc = create_parents(hdl, path_copy, prefix);
2913 2914 free(path_copy);
2914 2915 }
2915 2916 if (path_copy == NULL || rc != 0)
2916 2917 return (-1);
2917 2918
2918 2919 return (0);
2919 2920 }
2920 2921
2921 2922 /*
2922 2923 * Create a new filesystem or volume.
2923 2924 */
2924 2925 int
2925 2926 zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2926 2927 nvlist_t *props)
2927 2928 {
2928 2929 int ret;
2929 2930 uint64_t size = 0;
2930 2931 uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
2931 2932 char errbuf[1024];
2932 2933 uint64_t zoned;
2933 2934 dmu_objset_type_t ost;
2934 2935
2935 2936 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2936 2937 "cannot create '%s'"), path);
2937 2938
2938 2939 /* validate the path, taking care to note the extended error message */
2939 2940 if (!zfs_validate_name(hdl, path, type, B_TRUE))
2940 2941 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2941 2942
2942 2943 /* validate parents exist */
2943 2944 if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2944 2945 return (-1);
2945 2946
2946 2947 /*
2947 2948 * The failure modes when creating a dataset of a different type over
2948 2949 * one that already exists is a little strange. In particular, if you
2949 2950 * try to create a dataset on top of an existing dataset, the ioctl()
2950 2951 * will return ENOENT, not EEXIST. To prevent this from happening, we
2951 2952 * first try to see if the dataset exists.
2952 2953 */
2953 2954 if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
2954 2955 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2955 2956 "dataset already exists"));
2956 2957 return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2957 2958 }
2958 2959
2959 2960 if (type == ZFS_TYPE_VOLUME)
2960 2961 ost = DMU_OST_ZVOL;
2961 2962 else
2962 2963 ost = DMU_OST_ZFS;
2963 2964
2964 2965 if (props && (props = zfs_valid_proplist(hdl, type, props,
2965 2966 zoned, NULL, errbuf)) == 0)
2966 2967 return (-1);
2967 2968
2968 2969 if (type == ZFS_TYPE_VOLUME) {
2969 2970 /*
2970 2971 * If we are creating a volume, the size and block size must
2971 2972 * satisfy a few restraints. First, the blocksize must be a
2972 2973 * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the
2973 2974 * volsize must be a multiple of the block size, and cannot be
2974 2975 * zero.
2975 2976 */
2976 2977 if (props == NULL || nvlist_lookup_uint64(props,
2977 2978 zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2978 2979 nvlist_free(props);
2979 2980 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2980 2981 "missing volume size"));
2981 2982 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2982 2983 }
2983 2984
2984 2985 if ((ret = nvlist_lookup_uint64(props,
2985 2986 zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2986 2987 &blocksize)) != 0) {
2987 2988 if (ret == ENOENT) {
2988 2989 blocksize = zfs_prop_default_numeric(
2989 2990 ZFS_PROP_VOLBLOCKSIZE);
2990 2991 } else {
2991 2992 nvlist_free(props);
2992 2993 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2993 2994 "missing volume block size"));
2994 2995 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2995 2996 }
2996 2997 }
2997 2998
2998 2999 if (size == 0) {
2999 3000 nvlist_free(props);
3000 3001 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3001 3002 "volume size cannot be zero"));
3002 3003 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
3003 3004 }
3004 3005
3005 3006 if (size % blocksize != 0) {
3006 3007 nvlist_free(props);
3007 3008 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3008 3009 "volume size must be a multiple of volume block "
3009 3010 "size"));
3010 3011 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
3011 3012 }
3012 3013 }
3013 3014
3014 3015 /* create the dataset */
3015 3016 ret = lzc_create(path, ost, props);
3016 3017 nvlist_free(props);
3017 3018
3018 3019 /* check for failure */
3019 3020 if (ret != 0) {
3020 3021 char parent[ZFS_MAXNAMELEN];
3021 3022 (void) parent_name(path, parent, sizeof (parent));
3022 3023
3023 3024 switch (errno) {
3024 3025 case ENOENT:
3025 3026 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3026 3027 "no such parent '%s'"), parent);
3027 3028 return (zfs_error(hdl, EZFS_NOENT, errbuf));
3028 3029
3029 3030 case EINVAL:
3030 3031 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3031 3032 "parent '%s' is not a filesystem"), parent);
3032 3033 return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3033 3034
3034 3035 case EDOM:
3035 3036 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3036 3037 "volume block size must be power of 2 from "
3037 3038 "%u to %uk"),
3038 3039 (uint_t)SPA_MINBLOCKSIZE,
3039 3040 (uint_t)SPA_MAXBLOCKSIZE >> 10);
3040 3041
3041 3042 return (zfs_error(hdl, EZFS_BADPROP, errbuf));
3042 3043
3043 3044 case ENOTSUP:
3044 3045 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3045 3046 "pool must be upgraded to set this "
3046 3047 "property or value"));
3047 3048 return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
3048 3049 #ifdef _ILP32
3049 3050 case EOVERFLOW:
3050 3051 /*
3051 3052 * This platform can't address a volume this big.
3052 3053 */
3053 3054 if (type == ZFS_TYPE_VOLUME)
3054 3055 return (zfs_error(hdl, EZFS_VOLTOOBIG,
3055 3056 errbuf));
3056 3057 #endif
3057 3058 /* FALLTHROUGH */
3058 3059 default:
3059 3060 return (zfs_standard_error(hdl, errno, errbuf));
3060 3061 }
3061 3062 }
3062 3063
3063 3064 return (0);
3064 3065 }
3065 3066
3066 3067 /*
3067 3068 * Destroys the given dataset. The caller must make sure that the filesystem
3068 3069 * isn't mounted, and that there are no active dependents. If the file system
3069 3070 * does not exist this function does nothing.
3070 3071 */
3071 3072 int
3072 3073 zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
3073 3074 {
3074 3075 zfs_cmd_t zc = { 0 };
3075 3076
3076 3077 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3077 3078
3078 3079 if (ZFS_IS_VOLUME(zhp)) {
3079 3080 zc.zc_objset_type = DMU_OST_ZVOL;
3080 3081 } else {
3081 3082 zc.zc_objset_type = DMU_OST_ZFS;
3082 3083 }
3083 3084
3084 3085 zc.zc_defer_destroy = defer;
3085 3086 if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 &&
3086 3087 errno != ENOENT) {
3087 3088 return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
3088 3089 dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
3089 3090 zhp->zfs_name));
3090 3091 }
3091 3092
3092 3093 remove_mountpoint(zhp);
3093 3094
3094 3095 return (0);
3095 3096 }
3096 3097
3097 3098 struct destroydata {
3098 3099 nvlist_t *nvl;
3099 3100 const char *snapname;
3100 3101 };
3101 3102
3102 3103 static int
3103 3104 zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
3104 3105 {
3105 3106 struct destroydata *dd = arg;
3106 3107 zfs_handle_t *szhp;
3107 3108 char name[ZFS_MAXNAMELEN];
3108 3109 int rv = 0;
3109 3110
3110 3111 (void) snprintf(name, sizeof (name),
3111 3112 "%s@%s", zhp->zfs_name, dd->snapname);
3112 3113
3113 3114 szhp = make_dataset_handle(zhp->zfs_hdl, name);
3114 3115 if (szhp) {
3115 3116 verify(nvlist_add_boolean(dd->nvl, name) == 0);
3116 3117 zfs_close(szhp);
3117 3118 }
3118 3119
3119 3120 rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
3120 3121 zfs_close(zhp);
3121 3122 return (rv);
3122 3123 }
3123 3124
3124 3125 /*
3125 3126 * Destroys all snapshots with the given name in zhp & descendants.
3126 3127 */
3127 3128 int
3128 3129 zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
3129 3130 {
3130 3131 int ret;
3131 3132 struct destroydata dd = { 0 };
3132 3133
3133 3134 dd.snapname = snapname;
3134 3135 verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
3135 3136 (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
3136 3137
3137 3138 if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
3138 3139 ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
3139 3140 dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
3140 3141 zhp->zfs_name, snapname);
3141 3142 } else {
3142 3143 ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
3143 3144 }
3144 3145 nvlist_free(dd.nvl);
3145 3146 return (ret);
3146 3147 }
3147 3148
3148 3149 /*
3149 3150 * Destroys all the snapshots named in the nvlist.
3150 3151 */
3151 3152 int
3152 3153 zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
3153 3154 {
3154 3155 int ret;
3155 3156 nvlist_t *errlist;
3156 3157
3157 3158 ret = lzc_destroy_snaps(snaps, defer, &errlist);
3158 3159
3159 3160 if (ret == 0)
3160 3161 return (0);
3161 3162
3162 3163 if (nvlist_next_nvpair(errlist, NULL) == NULL) {
3163 3164 char errbuf[1024];
3164 3165 (void) snprintf(errbuf, sizeof (errbuf),
3165 3166 dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
3166 3167
3167 3168 ret = zfs_standard_error(hdl, ret, errbuf);
3168 3169 }
3169 3170 for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
3170 3171 pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
3171 3172 char errbuf[1024];
3172 3173 (void) snprintf(errbuf, sizeof (errbuf),
3173 3174 dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
3174 3175 nvpair_name(pair));
3175 3176
3176 3177 switch (fnvpair_value_int32(pair)) {
3177 3178 case EEXIST:
3178 3179 zfs_error_aux(hdl,
3179 3180 dgettext(TEXT_DOMAIN, "snapshot is cloned"));
3180 3181 ret = zfs_error(hdl, EZFS_EXISTS, errbuf);
3181 3182 break;
3182 3183 default:
3183 3184 ret = zfs_standard_error(hdl, errno, errbuf);
3184 3185 break;
3185 3186 }
3186 3187 }
3187 3188
3188 3189 return (ret);
3189 3190 }
3190 3191
3191 3192 /*
3192 3193 * Clones the given dataset. The target must be of the same type as the source.
3193 3194 */
3194 3195 int
3195 3196 zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
3196 3197 {
3197 3198 char parent[ZFS_MAXNAMELEN];
3198 3199 int ret;
3199 3200 char errbuf[1024];
3200 3201 libzfs_handle_t *hdl = zhp->zfs_hdl;
3201 3202 uint64_t zoned;
3202 3203
3203 3204 assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
3204 3205
3205 3206 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3206 3207 "cannot create '%s'"), target);
3207 3208
3208 3209 /* validate the target/clone name */
3209 3210 if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
3210 3211 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3211 3212
3212 3213 /* validate parents exist */
3213 3214 if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
3214 3215 return (-1);
3215 3216
3216 3217 (void) parent_name(target, parent, sizeof (parent));
3217 3218
3218 3219 /* do the clone */
3219 3220
3220 3221 if (props) {
3221 3222 zfs_type_t type;
3222 3223 if (ZFS_IS_VOLUME(zhp)) {
3223 3224 type = ZFS_TYPE_VOLUME;
3224 3225 } else {
3225 3226 type = ZFS_TYPE_FILESYSTEM;
3226 3227 }
3227 3228 if ((props = zfs_valid_proplist(hdl, type, props, zoned,
3228 3229 zhp, errbuf)) == NULL)
3229 3230 return (-1);
3230 3231 }
3231 3232
3232 3233 ret = lzc_clone(target, zhp->zfs_name, props);
3233 3234 nvlist_free(props);
3234 3235
3235 3236 if (ret != 0) {
3236 3237 switch (errno) {
3237 3238
3238 3239 case ENOENT:
3239 3240 /*
3240 3241 * The parent doesn't exist. We should have caught this
3241 3242 * above, but there may a race condition that has since
3242 3243 * destroyed the parent.
3243 3244 *
3244 3245 * At this point, we don't know whether it's the source
3245 3246 * that doesn't exist anymore, or whether the target
3246 3247 * dataset doesn't exist.
3247 3248 */
3248 3249 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3249 3250 "no such parent '%s'"), parent);
3250 3251 return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
3251 3252
3252 3253 case EXDEV:
3253 3254 zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3254 3255 "source and target pools differ"));
3255 3256 return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
3256 3257 errbuf));
3257 3258
3258 3259 default:
3259 3260 return (zfs_standard_error(zhp->zfs_hdl, errno,
3260 3261 errbuf));
3261 3262 }
3262 3263 }
3263 3264
3264 3265 return (ret);
3265 3266 }
3266 3267
3267 3268 /*
3268 3269 * Promotes the given clone fs to be the clone parent.
3269 3270 */
3270 3271 int
3271 3272 zfs_promote(zfs_handle_t *zhp)
3272 3273 {
3273 3274 libzfs_handle_t *hdl = zhp->zfs_hdl;
3274 3275 zfs_cmd_t zc = { 0 };
3275 3276 char parent[MAXPATHLEN];
3276 3277 int ret;
3277 3278 char errbuf[1024];
3278 3279
3279 3280 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3280 3281 "cannot promote '%s'"), zhp->zfs_name);
3281 3282
3282 3283 if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3283 3284 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3284 3285 "snapshots can not be promoted"));
3285 3286 return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3286 3287 }
3287 3288
3288 3289 (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
3289 3290 if (parent[0] == '\0') {
3290 3291 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3291 3292 "not a cloned filesystem"));
3292 3293 return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3293 3294 }
3294 3295
3295 3296 (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
3296 3297 sizeof (zc.zc_value));
3297 3298 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3298 3299 ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
3299 3300
3300 3301 if (ret != 0) {
3301 3302 int save_errno = errno;
3302 3303
3303 3304 switch (save_errno) {
3304 3305 case EEXIST:
3305 3306 /* There is a conflicting snapshot name. */
3306 3307 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3307 3308 "conflicting snapshot '%s' from parent '%s'"),
3308 3309 zc.zc_string, parent);
3309 3310 return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3310 3311
3311 3312 default:
3312 3313 return (zfs_standard_error(hdl, save_errno, errbuf));
3313 3314 }
3314 3315 }
3315 3316 return (ret);
3316 3317 }
3317 3318
3318 3319 typedef struct snapdata {
3319 3320 nvlist_t *sd_nvl;
3320 3321 const char *sd_snapname;
3321 3322 } snapdata_t;
3322 3323
3323 3324 static int
3324 3325 zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
3325 3326 {
3326 3327 snapdata_t *sd = arg;
3327 3328 char name[ZFS_MAXNAMELEN];
3328 3329 int rv = 0;
3329 3330
3330 3331 (void) snprintf(name, sizeof (name),
3331 3332 "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
3332 3333
3333 3334 fnvlist_add_boolean(sd->sd_nvl, name);
3334 3335
3335 3336 rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
3336 3337 zfs_close(zhp);
3337 3338 return (rv);
3338 3339 }
3339 3340
3340 3341 /*
3341 3342 * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
3342 3343 * created.
3343 3344 */
3344 3345 int
3345 3346 zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
3346 3347 {
3347 3348 int ret;
3348 3349 char errbuf[1024];
3349 3350 nvpair_t *elem;
3350 3351 nvlist_t *errors;
3351 3352
3352 3353 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3353 3354 "cannot create snapshots "));
3354 3355
3355 3356 elem = NULL;
3356 3357 while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
3357 3358 const char *snapname = nvpair_name(elem);
3358 3359
3359 3360 /* validate the target name */
3360 3361 if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
3361 3362 B_TRUE)) {
3362 3363 (void) snprintf(errbuf, sizeof (errbuf),
3363 3364 dgettext(TEXT_DOMAIN,
3364 3365 "cannot create snapshot '%s'"), snapname);
3365 3366 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3366 3367 }
3367 3368 }
3368 3369
3369 3370 if (props != NULL &&
3370 3371 (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3371 3372 props, B_FALSE, NULL, errbuf)) == NULL) {
3372 3373 return (-1);
3373 3374 }
3374 3375
3375 3376 ret = lzc_snapshot(snaps, props, &errors);
3376 3377
3377 3378 if (ret != 0) {
3378 3379 boolean_t printed = B_FALSE;
3379 3380 for (elem = nvlist_next_nvpair(errors, NULL);
3380 3381 elem != NULL;
3381 3382 elem = nvlist_next_nvpair(errors, elem)) {
3382 3383 (void) snprintf(errbuf, sizeof (errbuf),
3383 3384 dgettext(TEXT_DOMAIN,
3384 3385 "cannot create snapshot '%s'"), nvpair_name(elem));
3385 3386 (void) zfs_standard_error(hdl,
3386 3387 fnvpair_value_int32(elem), errbuf);
3387 3388 printed = B_TRUE;
3388 3389 }
3389 3390 if (!printed) {
3390 3391 switch (ret) {
3391 3392 case EXDEV:
3392 3393 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3393 3394 "multiple snapshots of same "
3394 3395 "fs not allowed"));
3395 3396 (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3396 3397
3397 3398 break;
3398 3399 default:
3399 3400 (void) zfs_standard_error(hdl, ret, errbuf);
3400 3401 }
3401 3402 }
3402 3403 }
3403 3404
3404 3405 nvlist_free(props);
3405 3406 nvlist_free(errors);
3406 3407 return (ret);
3407 3408 }
3408 3409
3409 3410 int
3410 3411 zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3411 3412 nvlist_t *props)
3412 3413 {
3413 3414 int ret;
3414 3415 snapdata_t sd = { 0 };
3415 3416 char fsname[ZFS_MAXNAMELEN];
3416 3417 char *cp;
3417 3418 zfs_handle_t *zhp;
3418 3419 char errbuf[1024];
3419 3420
3420 3421 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3421 3422 "cannot snapshot %s"), path);
3422 3423
3423 3424 if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
3424 3425 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3425 3426
3426 3427 (void) strlcpy(fsname, path, sizeof (fsname));
3427 3428 cp = strchr(fsname, '@');
3428 3429 *cp = '\0';
3429 3430 sd.sd_snapname = cp + 1;
3430 3431
3431 3432 if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
3432 3433 ZFS_TYPE_VOLUME)) == NULL) {
3433 3434 return (-1);
3434 3435 }
3435 3436
3436 3437 verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
3437 3438 if (recursive) {
3438 3439 (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
3439 3440 } else {
3440 3441 fnvlist_add_boolean(sd.sd_nvl, path);
3441 3442 }
3442 3443
3443 3444 ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
3444 3445 nvlist_free(sd.sd_nvl);
3445 3446 zfs_close(zhp);
3446 3447 return (ret);
3447 3448 }
3448 3449
3449 3450 /*
3450 3451 * Destroy any more recent snapshots. We invoke this callback on any dependents
3451 3452 * of the snapshot first. If the 'cb_dependent' member is non-zero, then this
3452 3453 * is a dependent and we should just destroy it without checking the transaction
3453 3454 * group.
3454 3455 */
3455 3456 typedef struct rollback_data {
3456 3457 const char *cb_target; /* the snapshot */
3457 3458 uint64_t cb_create; /* creation time reference */
3458 3459 boolean_t cb_error;
3459 3460 boolean_t cb_dependent;
3460 3461 boolean_t cb_force;
3461 3462 } rollback_data_t;
3462 3463
3463 3464 static int
3464 3465 rollback_destroy(zfs_handle_t *zhp, void *data)
3465 3466 {
3466 3467 rollback_data_t *cbp = data;
3467 3468
3468 3469 if (!cbp->cb_dependent) {
3469 3470 if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3470 3471 zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3471 3472 zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3472 3473 cbp->cb_create) {
3473 3474
3474 3475 cbp->cb_dependent = B_TRUE;
3475 3476 cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
3476 3477 rollback_destroy, cbp);
3477 3478 cbp->cb_dependent = B_FALSE;
3478 3479
3479 3480 cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
3480 3481 }
3481 3482 } else {
3482 3483 /* We must destroy this clone; first unmount it */
3483 3484 prop_changelist_t *clp;
3484 3485
3485 3486 clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3486 3487 cbp->cb_force ? MS_FORCE: 0);
3487 3488 if (clp == NULL || changelist_prefix(clp) != 0) {
3488 3489 cbp->cb_error = B_TRUE;
3489 3490 zfs_close(zhp);
3490 3491 return (0);
3491 3492 }
3492 3493 if (zfs_destroy(zhp, B_FALSE) != 0)
3493 3494 cbp->cb_error = B_TRUE;
3494 3495 else
3495 3496 changelist_remove(clp, zhp->zfs_name);
3496 3497 (void) changelist_postfix(clp);
3497 3498 changelist_free(clp);
3498 3499 }
3499 3500
3500 3501 zfs_close(zhp);
3501 3502 return (0);
3502 3503 }
3503 3504
3504 3505 /*
3505 3506 * Given a dataset, rollback to a specific snapshot, discarding any
3506 3507 * data changes since then and making it the active dataset.
3507 3508 *
3508 3509 * Any snapshots more recent than the target are destroyed, along with
3509 3510 * their dependents.
3510 3511 */
3511 3512 int
3512 3513 zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
3513 3514 {
3514 3515 rollback_data_t cb = { 0 };
3515 3516 int err;
3516 3517 zfs_cmd_t zc = { 0 };
3517 3518 boolean_t restore_resv = 0;
3518 3519 uint64_t old_volsize, new_volsize;
3519 3520 zfs_prop_t resv_prop;
3520 3521
3521 3522 assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
3522 3523 zhp->zfs_type == ZFS_TYPE_VOLUME);
3523 3524
3524 3525 /*
3525 3526 * Destroy all recent snapshots and their dependents.
3526 3527 */
3527 3528 cb.cb_force = force;
3528 3529 cb.cb_target = snap->zfs_name;
3529 3530 cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3530 3531 (void) zfs_iter_children(zhp, rollback_destroy, &cb);
3531 3532
3532 3533 if (cb.cb_error)
3533 3534 return (-1);
3534 3535
3535 3536 /*
3536 3537 * Now that we have verified that the snapshot is the latest,
3537 3538 * rollback to the given snapshot.
3538 3539 */
3539 3540
3540 3541 if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
3541 3542 if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
3542 3543 return (-1);
3543 3544 old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3544 3545 restore_resv =
3545 3546 (old_volsize == zfs_prop_get_int(zhp, resv_prop));
3546 3547 }
3547 3548
3548 3549 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3549 3550
3550 3551 if (ZFS_IS_VOLUME(zhp))
3551 3552 zc.zc_objset_type = DMU_OST_ZVOL;
3552 3553 else
3553 3554 zc.zc_objset_type = DMU_OST_ZFS;
3554 3555
3555 3556 /*
3556 3557 * We rely on zfs_iter_children() to verify that there are no
3557 3558 * newer snapshots for the given dataset. Therefore, we can
3558 3559 * simply pass the name on to the ioctl() call. There is still
3559 3560 * an unlikely race condition where the user has taken a
3560 3561 * snapshot since we verified that this was the most recent.
3561 3562 *
3562 3563 */
3563 3564 if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
3564 3565 (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
3565 3566 dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
3566 3567 zhp->zfs_name);
3567 3568 return (err);
3568 3569 }
3569 3570
3570 3571 /*
3571 3572 * For volumes, if the pre-rollback volsize matched the pre-
3572 3573 * rollback reservation and the volsize has changed then set
3573 3574 * the reservation property to the post-rollback volsize.
3574 3575 * Make a new handle since the rollback closed the dataset.
3575 3576 */
3576 3577 if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
3577 3578 (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
3578 3579 if (restore_resv) {
3579 3580 new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3580 3581 if (old_volsize != new_volsize)
3581 3582 err = zfs_prop_set_int(zhp, resv_prop,
3582 3583 new_volsize);
3583 3584 }
3584 3585 zfs_close(zhp);
3585 3586 }
3586 3587 return (err);
3587 3588 }
3588 3589
3589 3590 /*
3590 3591 * Renames the given dataset.
3591 3592 */
3592 3593 int
3593 3594 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
3594 3595 boolean_t force_unmount)
3595 3596 {
3596 3597 int ret;
3597 3598 zfs_cmd_t zc = { 0 };
3598 3599 char *delim;
3599 3600 prop_changelist_t *cl = NULL;
3600 3601 zfs_handle_t *zhrp = NULL;
3601 3602 char *parentname = NULL;
3602 3603 char parent[ZFS_MAXNAMELEN];
3603 3604 libzfs_handle_t *hdl = zhp->zfs_hdl;
3604 3605 char errbuf[1024];
3605 3606
3606 3607 /* if we have the same exact name, just return success */
3607 3608 if (strcmp(zhp->zfs_name, target) == 0)
3608 3609 return (0);
3609 3610
3610 3611 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3611 3612 "cannot rename to '%s'"), target);
3612 3613
3613 3614 /*
3614 3615 * Make sure the target name is valid
3615 3616 */
3616 3617 if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3617 3618 if ((strchr(target, '@') == NULL) ||
3618 3619 *target == '@') {
3619 3620 /*
3620 3621 * Snapshot target name is abbreviated,
3621 3622 * reconstruct full dataset name
3622 3623 */
3623 3624 (void) strlcpy(parent, zhp->zfs_name,
3624 3625 sizeof (parent));
3625 3626 delim = strchr(parent, '@');
3626 3627 if (strchr(target, '@') == NULL)
3627 3628 *(++delim) = '\0';
3628 3629 else
3629 3630 *delim = '\0';
3630 3631 (void) strlcat(parent, target, sizeof (parent));
3631 3632 target = parent;
3632 3633 } else {
3633 3634 /*
3634 3635 * Make sure we're renaming within the same dataset.
3635 3636 */
3636 3637 delim = strchr(target, '@');
3637 3638 if (strncmp(zhp->zfs_name, target, delim - target)
3638 3639 != 0 || zhp->zfs_name[delim - target] != '@') {
3639 3640 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3640 3641 "snapshots must be part of same "
3641 3642 "dataset"));
3642 3643 return (zfs_error(hdl, EZFS_CROSSTARGET,
3643 3644 errbuf));
3644 3645 }
3645 3646 }
3646 3647 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3647 3648 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3648 3649 } else {
3649 3650 if (recursive) {
3650 3651 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3651 3652 "recursive rename must be a snapshot"));
3652 3653 return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3653 3654 }
3654 3655
3655 3656 if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3656 3657 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3657 3658
3658 3659 /* validate parents */
3659 3660 if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)
3660 3661 return (-1);
3661 3662
3662 3663 /* make sure we're in the same pool */
3663 3664 verify((delim = strchr(target, '/')) != NULL);
3664 3665 if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3665 3666 zhp->zfs_name[delim - target] != '/') {
3666 3667 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3667 3668 "datasets must be within same pool"));
3668 3669 return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3669 3670 }
3670 3671
3671 3672 /* new name cannot be a child of the current dataset name */
3672 3673 if (is_descendant(zhp->zfs_name, target)) {
3673 3674 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3674 3675 "New dataset name cannot be a descendant of "
3675 3676 "current dataset name"));
3676 3677 return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3677 3678 }
3678 3679 }
3679 3680
3680 3681 (void) snprintf(errbuf, sizeof (errbuf),
3681 3682 dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
3682 3683
3683 3684 if (getzoneid() == GLOBAL_ZONEID &&
3684 3685 zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
3685 3686 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3686 3687 "dataset is used in a non-global zone"));
3687 3688 return (zfs_error(hdl, EZFS_ZONED, errbuf));
3688 3689 }
3689 3690
3690 3691 if (recursive) {
3691 3692
3692 3693 parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
3693 3694 if (parentname == NULL) {
3694 3695 ret = -1;
3695 3696 goto error;
3696 3697 }
3697 3698 delim = strchr(parentname, '@');
3698 3699 *delim = '\0';
3699 3700 zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
3700 3701 if (zhrp == NULL) {
3701 3702 ret = -1;
3702 3703 goto error;
3703 3704 }
3704 3705
3705 3706 } else {
3706 3707 if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3707 3708 force_unmount ? MS_FORCE : 0)) == NULL)
3708 3709 return (-1);
3709 3710
3710 3711 if (changelist_haszonedchild(cl)) {
3711 3712 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3712 3713 "child dataset with inherited mountpoint is used "
3713 3714 "in a non-global zone"));
3714 3715 (void) zfs_error(hdl, EZFS_ZONED, errbuf);
3715 3716 goto error;
3716 3717 }
3717 3718
3718 3719 if ((ret = changelist_prefix(cl)) != 0)
3719 3720 goto error;
3720 3721 }
3721 3722
3722 3723 if (ZFS_IS_VOLUME(zhp))
3723 3724 zc.zc_objset_type = DMU_OST_ZVOL;
3724 3725 else
3725 3726 zc.zc_objset_type = DMU_OST_ZFS;
3726 3727
3727 3728 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3728 3729 (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
3729 3730
3730 3731 zc.zc_cookie = recursive;
3731 3732
3732 3733 if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
3733 3734 /*
3734 3735 * if it was recursive, the one that actually failed will
3735 3736 * be in zc.zc_name
3736 3737 */
3737 3738 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3738 3739 "cannot rename '%s'"), zc.zc_name);
3739 3740
3740 3741 if (recursive && errno == EEXIST) {
3741 3742 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3742 3743 "a child dataset already has a snapshot "
3743 3744 "with the new name"));
3744 3745 (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3745 3746 } else {
3746 3747 (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3747 3748 }
3748 3749
3749 3750 /*
3750 3751 * On failure, we still want to remount any filesystems that
3751 3752 * were previously mounted, so we don't alter the system state.
3752 3753 */
3753 3754 if (!recursive)
3754 3755 (void) changelist_postfix(cl);
3755 3756 } else {
3756 3757 if (!recursive) {
3757 3758 changelist_rename(cl, zfs_get_name(zhp), target);
3758 3759 ret = changelist_postfix(cl);
3759 3760 }
3760 3761 }
3761 3762
3762 3763 error:
3763 3764 if (parentname) {
3764 3765 free(parentname);
3765 3766 }
3766 3767 if (zhrp) {
3767 3768 zfs_close(zhrp);
3768 3769 }
3769 3770 if (cl) {
3770 3771 changelist_free(cl);
3771 3772 }
3772 3773 return (ret);
3773 3774 }
3774 3775
3775 3776 nvlist_t *
3776 3777 zfs_get_user_props(zfs_handle_t *zhp)
3777 3778 {
3778 3779 return (zhp->zfs_user_props);
3779 3780 }
3780 3781
3781 3782 nvlist_t *
3782 3783 zfs_get_recvd_props(zfs_handle_t *zhp)
3783 3784 {
3784 3785 if (zhp->zfs_recvd_props == NULL)
3785 3786 if (get_recvd_props_ioctl(zhp) != 0)
3786 3787 return (NULL);
3787 3788 return (zhp->zfs_recvd_props);
3788 3789 }
3789 3790
3790 3791 /*
3791 3792 * This function is used by 'zfs list' to determine the exact set of columns to
3792 3793 * display, and their maximum widths. This does two main things:
3793 3794 *
3794 3795 * - If this is a list of all properties, then expand the list to include
3795 3796 * all native properties, and set a flag so that for each dataset we look
3796 3797 * for new unique user properties and add them to the list.
3797 3798 *
3798 3799 * - For non fixed-width properties, keep track of the maximum width seen
3799 3800 * so that we can size the column appropriately. If the user has
3800 3801 * requested received property values, we also need to compute the width
3801 3802 * of the RECEIVED column.
3802 3803 */
3803 3804 int
3804 3805 zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received)
3805 3806 {
3806 3807 libzfs_handle_t *hdl = zhp->zfs_hdl;
3807 3808 zprop_list_t *entry;
3808 3809 zprop_list_t **last, **start;
3809 3810 nvlist_t *userprops, *propval;
3810 3811 nvpair_t *elem;
3811 3812 char *strval;
3812 3813 char buf[ZFS_MAXPROPLEN];
3813 3814
3814 3815 if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
3815 3816 return (-1);
3816 3817
3817 3818 userprops = zfs_get_user_props(zhp);
3818 3819
3819 3820 entry = *plp;
3820 3821 if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3821 3822 /*
3822 3823 * Go through and add any user properties as necessary. We
3823 3824 * start by incrementing our list pointer to the first
3824 3825 * non-native property.
3825 3826 */
3826 3827 start = plp;
3827 3828 while (*start != NULL) {
3828 3829 if ((*start)->pl_prop == ZPROP_INVAL)
3829 3830 break;
3830 3831 start = &(*start)->pl_next;
3831 3832 }
3832 3833
3833 3834 elem = NULL;
3834 3835 while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3835 3836 /*
3836 3837 * See if we've already found this property in our list.
3837 3838 */
3838 3839 for (last = start; *last != NULL;
3839 3840 last = &(*last)->pl_next) {
3840 3841 if (strcmp((*last)->pl_user_prop,
3841 3842 nvpair_name(elem)) == 0)
3842 3843 break;
3843 3844 }
3844 3845
3845 3846 if (*last == NULL) {
3846 3847 if ((entry = zfs_alloc(hdl,
3847 3848 sizeof (zprop_list_t))) == NULL ||
3848 3849 ((entry->pl_user_prop = zfs_strdup(hdl,
3849 3850 nvpair_name(elem)))) == NULL) {
3850 3851 free(entry);
3851 3852 return (-1);
3852 3853 }
3853 3854
3854 3855 entry->pl_prop = ZPROP_INVAL;
3855 3856 entry->pl_width = strlen(nvpair_name(elem));
3856 3857 entry->pl_all = B_TRUE;
3857 3858 *last = entry;
3858 3859 }
3859 3860 }
3860 3861 }
3861 3862
3862 3863 /*
3863 3864 * Now go through and check the width of any non-fixed columns
3864 3865 */
3865 3866 for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3866 3867 if (entry->pl_fixed)
3867 3868 continue;
3868 3869
3869 3870 if (entry->pl_prop != ZPROP_INVAL) {
3870 3871 if (zfs_prop_get(zhp, entry->pl_prop,
3871 3872 buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3872 3873 if (strlen(buf) > entry->pl_width)
3873 3874 entry->pl_width = strlen(buf);
3874 3875 }
3875 3876 if (received && zfs_prop_get_recvd(zhp,
3876 3877 zfs_prop_to_name(entry->pl_prop),
3877 3878 buf, sizeof (buf), B_FALSE) == 0)
3878 3879 if (strlen(buf) > entry->pl_recvd_width)
3879 3880 entry->pl_recvd_width = strlen(buf);
3880 3881 } else {
3881 3882 if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
3882 3883 &propval) == 0) {
3883 3884 verify(nvlist_lookup_string(propval,
3884 3885 ZPROP_VALUE, &strval) == 0);
3885 3886 if (strlen(strval) > entry->pl_width)
3886 3887 entry->pl_width = strlen(strval);
3887 3888 }
3888 3889 if (received && zfs_prop_get_recvd(zhp,
3889 3890 entry->pl_user_prop,
3890 3891 buf, sizeof (buf), B_FALSE) == 0)
3891 3892 if (strlen(buf) > entry->pl_recvd_width)
3892 3893 entry->pl_recvd_width = strlen(buf);
3893 3894 }
3894 3895 }
3895 3896
3896 3897 return (0);
3897 3898 }
3898 3899
3899 3900 int
3900 3901 zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
3901 3902 char *resource, void *export, void *sharetab,
3902 3903 int sharemax, zfs_share_op_t operation)
3903 3904 {
3904 3905 zfs_cmd_t zc = { 0 };
3905 3906 int error;
3906 3907
3907 3908 (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3908 3909 (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3909 3910 if (resource)
3910 3911 (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
3911 3912 zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
3912 3913 zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
3913 3914 zc.zc_share.z_sharetype = operation;
3914 3915 zc.zc_share.z_sharemax = sharemax;
3915 3916 error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
3916 3917 return (error);
3917 3918 }
3918 3919
3919 3920 void
3920 3921 zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
3921 3922 {
3922 3923 nvpair_t *curr;
3923 3924
3924 3925 /*
3925 3926 * Keep a reference to the props-table against which we prune the
3926 3927 * properties.
3927 3928 */
3928 3929 zhp->zfs_props_table = props;
3929 3930
3930 3931 curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
3931 3932
3932 3933 while (curr) {
3933 3934 zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
3934 3935 nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
3935 3936
3936 3937 /*
3937 3938 * User properties will result in ZPROP_INVAL, and since we
3938 3939 * only know how to prune standard ZFS properties, we always
3939 3940 * leave these in the list. This can also happen if we
3940 3941 * encounter an unknown DSL property (when running older
3941 3942 * software, for example).
3942 3943 */
3943 3944 if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
3944 3945 (void) nvlist_remove(zhp->zfs_props,
3945 3946 nvpair_name(curr), nvpair_type(curr));
3946 3947 curr = next;
3947 3948 }
3948 3949 }
3949 3950
3950 3951 static int
3951 3952 zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
3952 3953 zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
3953 3954 {
3954 3955 zfs_cmd_t zc = { 0 };
3955 3956 nvlist_t *nvlist = NULL;
3956 3957 int error;
3957 3958
3958 3959 (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3959 3960 (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3960 3961 zc.zc_cookie = (uint64_t)cmd;
3961 3962
3962 3963 if (cmd == ZFS_SMB_ACL_RENAME) {
3963 3964 if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
3964 3965 (void) no_memory(hdl);
3965 3966 return (NULL);
3966 3967 }
3967 3968 }
3968 3969
3969 3970 switch (cmd) {
3970 3971 case ZFS_SMB_ACL_ADD:
3971 3972 case ZFS_SMB_ACL_REMOVE:
3972 3973 (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
3973 3974 break;
3974 3975 case ZFS_SMB_ACL_RENAME:
3975 3976 if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
3976 3977 resource1) != 0) {
3977 3978 (void) no_memory(hdl);
3978 3979 return (-1);
3979 3980 }
3980 3981 if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
3981 3982 resource2) != 0) {
3982 3983 (void) no_memory(hdl);
3983 3984 return (-1);
3984 3985 }
3985 3986 if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
3986 3987 nvlist_free(nvlist);
3987 3988 return (-1);
3988 3989 }
3989 3990 break;
3990 3991 case ZFS_SMB_ACL_PURGE:
3991 3992 break;
3992 3993 default:
3993 3994 return (-1);
3994 3995 }
3995 3996 error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
3996 3997 if (nvlist)
3997 3998 nvlist_free(nvlist);
3998 3999 return (error);
3999 4000 }
4000 4001
4001 4002 int
4002 4003 zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
4003 4004 char *path, char *resource)
4004 4005 {
4005 4006 return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
4006 4007 resource, NULL));
4007 4008 }
4008 4009
4009 4010 int
4010 4011 zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
4011 4012 char *path, char *resource)
4012 4013 {
4013 4014 return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
4014 4015 resource, NULL));
4015 4016 }
4016 4017
4017 4018 int
4018 4019 zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
4019 4020 {
4020 4021 return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
4021 4022 NULL, NULL));
4022 4023 }
4023 4024
4024 4025 int
4025 4026 zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
4026 4027 char *oldname, char *newname)
4027 4028 {
4028 4029 return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
4029 4030 oldname, newname));
4030 4031 }
4031 4032
4032 4033 int
4033 4034 zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
4034 4035 zfs_userspace_cb_t func, void *arg)
4035 4036 {
4036 4037 zfs_cmd_t zc = { 0 };
4037 4038 zfs_useracct_t buf[100];
4038 4039 libzfs_handle_t *hdl = zhp->zfs_hdl;
4039 4040 int ret;
4040 4041
4041 4042 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4042 4043
4043 4044 zc.zc_objset_type = type;
4044 4045 zc.zc_nvlist_dst = (uintptr_t)buf;
4045 4046
4046 4047 for (;;) {
4047 4048 zfs_useracct_t *zua = buf;
4048 4049
4049 4050 zc.zc_nvlist_dst_size = sizeof (buf);
4050 4051 if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
4051 4052 char errbuf[1024];
4052 4053
4053 4054 (void) snprintf(errbuf, sizeof (errbuf),
4054 4055 dgettext(TEXT_DOMAIN,
4055 4056 "cannot get used/quota for %s"), zc.zc_name);
4056 4057 return (zfs_standard_error_fmt(hdl, errno, errbuf));
4057 4058 }
4058 4059 if (zc.zc_nvlist_dst_size == 0)
4059 4060 break;
4060 4061
4061 4062 while (zc.zc_nvlist_dst_size > 0) {
4062 4063 if ((ret = func(arg, zua->zu_domain, zua->zu_rid,
4063 4064 zua->zu_space)) != 0)
4064 4065 return (ret);
4065 4066 zua++;
4066 4067 zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
4067 4068 }
4068 4069 }
4069 4070
4070 4071 return (0);
4071 4072 }
4072 4073
4073 4074 struct holdarg {
4074 4075 nvlist_t *nvl;
4075 4076 const char *snapname;
4076 4077 const char *tag;
4077 4078 boolean_t recursive;
4078 4079 };
4079 4080
4080 4081 static int
4081 4082 zfs_hold_one(zfs_handle_t *zhp, void *arg)
4082 4083 {
4083 4084 struct holdarg *ha = arg;
4084 4085 zfs_handle_t *szhp;
4085 4086 char name[ZFS_MAXNAMELEN];
4086 4087 int rv = 0;
4087 4088
4088 4089 (void) snprintf(name, sizeof (name),
4089 4090 "%s@%s", zhp->zfs_name, ha->snapname);
4090 4091
4091 4092 szhp = make_dataset_handle(zhp->zfs_hdl, name);
4092 4093 if (szhp) {
4093 4094 fnvlist_add_string(ha->nvl, name, ha->tag);
4094 4095 zfs_close(szhp);
4095 4096 }
4096 4097
4097 4098 if (ha->recursive)
4098 4099 rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
4099 4100 zfs_close(zhp);
4100 4101 return (rv);
4101 4102 }
4102 4103
4103 4104 int
4104 4105 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4105 4106 boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
4106 4107 {
4107 4108 int ret;
4108 4109 struct holdarg ha;
↓ open down ↓ |
4072 lines elided |
↑ open up ↑ |
4109 4110 nvlist_t *errors;
4110 4111 libzfs_handle_t *hdl = zhp->zfs_hdl;
4111 4112 char errbuf[1024];
4112 4113 nvpair_t *elem;
4113 4114
4114 4115 ha.nvl = fnvlist_alloc();
4115 4116 ha.snapname = snapname;
4116 4117 ha.tag = tag;
4117 4118 ha.recursive = recursive;
4118 4119 (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
4120 +
4121 + if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
4122 + fnvlist_free(ha.nvl);
4123 + ret = ENOENT;
4124 + if (!enoent_ok) {
4125 + (void) snprintf(errbuf, sizeof (errbuf),
4126 + dgettext(TEXT_DOMAIN,
4127 + "cannot hold snapshot '%s@%s'"),
4128 + zhp->zfs_name, snapname);
4129 + (void) zfs_standard_error(hdl, ret, errbuf);
4130 + }
4131 + return (ret);
4132 + }
4133 +
4119 4134 ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
4120 4135 fnvlist_free(ha.nvl);
4121 4136
4122 4137 if (ret == 0)
4123 4138 return (0);
4124 4139
4125 4140 if (nvlist_next_nvpair(errors, NULL) == NULL) {
4126 4141 /* no hold-specific errors */
4127 4142 (void) snprintf(errbuf, sizeof (errbuf),
4128 4143 dgettext(TEXT_DOMAIN, "cannot hold"));
4129 4144 switch (ret) {
4130 4145 case ENOTSUP:
4131 4146 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4132 4147 "pool must be upgraded"));
4133 4148 (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4134 4149 break;
4135 4150 case EINVAL:
4136 4151 (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4137 4152 break;
4138 4153 default:
4139 4154 (void) zfs_standard_error(hdl, ret, errbuf);
4140 4155 }
4141 4156 }
4142 4157
4143 4158 for (elem = nvlist_next_nvpair(errors, NULL);
4144 4159 elem != NULL;
4145 4160 elem = nvlist_next_nvpair(errors, elem)) {
4146 4161 (void) snprintf(errbuf, sizeof (errbuf),
4147 4162 dgettext(TEXT_DOMAIN,
4148 4163 "cannot hold snapshot '%s'"), nvpair_name(elem));
4149 4164 switch (fnvpair_value_int32(elem)) {
4150 4165 case E2BIG:
4151 4166 /*
4152 4167 * Temporary tags wind up having the ds object id
4153 4168 * prepended. So even if we passed the length check
4154 4169 * above, it's still possible for the tag to wind
4155 4170 * up being slightly too long.
4156 4171 */
4157 4172 (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
4158 4173 break;
4159 4174 case EINVAL:
4160 4175 (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4161 4176 break;
4162 4177 case EEXIST:
4163 4178 (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
4164 4179 break;
4165 4180 case ENOENT:
4166 4181 if (enoent_ok)
4167 4182 return (ENOENT);
4168 4183 /* FALLTHROUGH */
4169 4184 default:
4170 4185 (void) zfs_standard_error(hdl,
4171 4186 fnvpair_value_int32(elem), errbuf);
4172 4187 }
4173 4188 }
4174 4189
4175 4190 fnvlist_free(errors);
4176 4191 return (ret);
4177 4192 }
4178 4193
4179 4194 struct releasearg {
4180 4195 nvlist_t *nvl;
4181 4196 const char *snapname;
4182 4197 const char *tag;
4183 4198 boolean_t recursive;
4184 4199 };
4185 4200
4186 4201 static int
4187 4202 zfs_release_one(zfs_handle_t *zhp, void *arg)
4188 4203 {
4189 4204 struct holdarg *ha = arg;
4190 4205 zfs_handle_t *szhp;
4191 4206 char name[ZFS_MAXNAMELEN];
4192 4207 int rv = 0;
4193 4208
4194 4209 (void) snprintf(name, sizeof (name),
4195 4210 "%s@%s", zhp->zfs_name, ha->snapname);
4196 4211
4197 4212 szhp = make_dataset_handle(zhp->zfs_hdl, name);
4198 4213 if (szhp) {
4199 4214 nvlist_t *holds = fnvlist_alloc();
4200 4215 fnvlist_add_boolean(holds, ha->tag);
4201 4216 fnvlist_add_nvlist(ha->nvl, name, holds);
4202 4217 zfs_close(szhp);
4203 4218 }
4204 4219
4205 4220 if (ha->recursive)
4206 4221 rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
4207 4222 zfs_close(zhp);
4208 4223 return (rv);
4209 4224 }
↓ open down ↓ |
81 lines elided |
↑ open up ↑ |
4210 4225
4211 4226 int
4212 4227 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4213 4228 boolean_t recursive)
4214 4229 {
4215 4230 int ret;
4216 4231 struct holdarg ha;
4217 4232 nvlist_t *errors;
4218 4233 nvpair_t *elem;
4219 4234 libzfs_handle_t *hdl = zhp->zfs_hdl;
4235 + char errbuf[1024];
4220 4236
4221 4237 ha.nvl = fnvlist_alloc();
4222 4238 ha.snapname = snapname;
4223 4239 ha.tag = tag;
4224 4240 ha.recursive = recursive;
4225 4241 (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
4242 +
4243 + if (nvlist_next_nvpair(ha.nvl, NULL) == NULL) {
4244 + fnvlist_free(ha.nvl);
4245 + ret = ENOENT;
4246 + (void) snprintf(errbuf, sizeof (errbuf),
4247 + dgettext(TEXT_DOMAIN,
4248 + "cannot release hold from snapshot '%s@%s'"),
4249 + zhp->zfs_name, snapname);
4250 + (void) zfs_standard_error(hdl, ret, errbuf);
4251 + return (ret);
4252 + }
4253 +
4226 4254 ret = lzc_release(ha.nvl, &errors);
4227 4255 fnvlist_free(ha.nvl);
4228 4256
4229 4257 if (ret == 0)
4230 4258 return (0);
4231 4259
4232 4260 if (nvlist_next_nvpair(errors, NULL) == NULL) {
4233 4261 /* no hold-specific errors */
4234 - char errbuf[1024];
4235 -
4236 4262 (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4237 4263 "cannot release"));
4238 4264 switch (errno) {
4239 4265 case ENOTSUP:
4240 4266 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4241 4267 "pool must be upgraded"));
4242 4268 (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
4243 4269 break;
4244 4270 default:
4245 4271 (void) zfs_standard_error_fmt(hdl, errno, errbuf);
4246 4272 }
4247 4273 }
4248 4274
4249 4275 for (elem = nvlist_next_nvpair(errors, NULL);
4250 4276 elem != NULL;
4251 4277 elem = nvlist_next_nvpair(errors, elem)) {
4252 - char errbuf[1024];
4253 -
4254 4278 (void) snprintf(errbuf, sizeof (errbuf),
4255 4279 dgettext(TEXT_DOMAIN,
4256 4280 "cannot release hold from snapshot '%s'"),
4257 4281 nvpair_name(elem));
4258 4282 switch (fnvpair_value_int32(elem)) {
4259 4283 case ESRCH:
4260 4284 (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
4261 4285 break;
4262 4286 case EINVAL:
4263 4287 (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
4264 4288 break;
4265 4289 default:
4266 4290 (void) zfs_standard_error_fmt(hdl,
4267 4291 fnvpair_value_int32(elem), errbuf);
4268 4292 }
4269 4293 }
4270 4294
4271 4295 fnvlist_free(errors);
4272 4296 return (ret);
4273 4297 }
4274 4298
4275 4299 int
4276 4300 zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
4277 4301 {
4278 4302 zfs_cmd_t zc = { 0 };
4279 4303 libzfs_handle_t *hdl = zhp->zfs_hdl;
4280 4304 int nvsz = 2048;
4281 4305 void *nvbuf;
4282 4306 int err = 0;
4283 4307 char errbuf[1024];
4284 4308
4285 4309 assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4286 4310 zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4287 4311
4288 4312 tryagain:
4289 4313
4290 4314 nvbuf = malloc(nvsz);
4291 4315 if (nvbuf == NULL) {
4292 4316 err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
4293 4317 goto out;
4294 4318 }
4295 4319
4296 4320 zc.zc_nvlist_dst_size = nvsz;
4297 4321 zc.zc_nvlist_dst = (uintptr_t)nvbuf;
4298 4322
4299 4323 (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
4300 4324
4301 4325 if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
4302 4326 (void) snprintf(errbuf, sizeof (errbuf),
4303 4327 dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
4304 4328 zc.zc_name);
4305 4329 switch (errno) {
4306 4330 case ENOMEM:
4307 4331 free(nvbuf);
4308 4332 nvsz = zc.zc_nvlist_dst_size;
4309 4333 goto tryagain;
4310 4334
4311 4335 case ENOTSUP:
4312 4336 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4313 4337 "pool must be upgraded"));
4314 4338 err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4315 4339 break;
4316 4340 case EINVAL:
4317 4341 err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4318 4342 break;
4319 4343 case ENOENT:
4320 4344 err = zfs_error(hdl, EZFS_NOENT, errbuf);
4321 4345 break;
4322 4346 default:
4323 4347 err = zfs_standard_error_fmt(hdl, errno, errbuf);
4324 4348 break;
4325 4349 }
4326 4350 } else {
4327 4351 /* success */
4328 4352 int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
4329 4353 if (rc) {
4330 4354 (void) snprintf(errbuf, sizeof (errbuf), dgettext(
4331 4355 TEXT_DOMAIN, "cannot get permissions on '%s'"),
4332 4356 zc.zc_name);
4333 4357 err = zfs_standard_error_fmt(hdl, rc, errbuf);
4334 4358 }
4335 4359 }
4336 4360
4337 4361 free(nvbuf);
4338 4362 out:
4339 4363 return (err);
4340 4364 }
4341 4365
4342 4366 int
4343 4367 zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
4344 4368 {
4345 4369 zfs_cmd_t zc = { 0 };
4346 4370 libzfs_handle_t *hdl = zhp->zfs_hdl;
4347 4371 char *nvbuf;
4348 4372 char errbuf[1024];
4349 4373 size_t nvsz;
4350 4374 int err;
4351 4375
4352 4376 assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4353 4377 zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4354 4378
4355 4379 err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
4356 4380 assert(err == 0);
4357 4381
4358 4382 nvbuf = malloc(nvsz);
4359 4383
4360 4384 err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
4361 4385 assert(err == 0);
4362 4386
4363 4387 zc.zc_nvlist_src_size = nvsz;
4364 4388 zc.zc_nvlist_src = (uintptr_t)nvbuf;
4365 4389 zc.zc_perm_action = un;
4366 4390
4367 4391 (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4368 4392
4369 4393 if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
4370 4394 (void) snprintf(errbuf, sizeof (errbuf),
4371 4395 dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
4372 4396 zc.zc_name);
4373 4397 switch (errno) {
4374 4398 case ENOTSUP:
4375 4399 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4376 4400 "pool must be upgraded"));
4377 4401 err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4378 4402 break;
4379 4403 case EINVAL:
4380 4404 err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4381 4405 break;
4382 4406 case ENOENT:
4383 4407 err = zfs_error(hdl, EZFS_NOENT, errbuf);
4384 4408 break;
4385 4409 default:
4386 4410 err = zfs_standard_error_fmt(hdl, errno, errbuf);
4387 4411 break;
4388 4412 }
4389 4413 }
4390 4414
4391 4415 free(nvbuf);
4392 4416
4393 4417 return (err);
4394 4418 }
4395 4419
4396 4420 int
4397 4421 zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
4398 4422 {
4399 4423 int err;
4400 4424 char errbuf[1024];
4401 4425
4402 4426 err = lzc_get_holds(zhp->zfs_name, nvl);
4403 4427
4404 4428 if (err != 0) {
4405 4429 libzfs_handle_t *hdl = zhp->zfs_hdl;
4406 4430
4407 4431 (void) snprintf(errbuf, sizeof (errbuf),
4408 4432 dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
4409 4433 zhp->zfs_name);
4410 4434 switch (err) {
4411 4435 case ENOTSUP:
4412 4436 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4413 4437 "pool must be upgraded"));
4414 4438 err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4415 4439 break;
4416 4440 case EINVAL:
4417 4441 err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4418 4442 break;
4419 4443 case ENOENT:
4420 4444 err = zfs_error(hdl, EZFS_NOENT, errbuf);
4421 4445 break;
4422 4446 default:
4423 4447 err = zfs_standard_error_fmt(hdl, errno, errbuf);
4424 4448 break;
4425 4449 }
4426 4450 }
4427 4451
4428 4452 return (err);
4429 4453 }
4430 4454
4431 4455 uint64_t
4432 4456 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4433 4457 {
4434 4458 uint64_t numdb;
4435 4459 uint64_t nblocks, volblocksize;
4436 4460 int ncopies;
4437 4461 char *strval;
4438 4462
4439 4463 if (nvlist_lookup_string(props,
4440 4464 zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4441 4465 ncopies = atoi(strval);
4442 4466 else
4443 4467 ncopies = 1;
4444 4468 if (nvlist_lookup_uint64(props,
4445 4469 zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4446 4470 &volblocksize) != 0)
4447 4471 volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
4448 4472 nblocks = volsize/volblocksize;
4449 4473 /* start with metadnode L0-L6 */
4450 4474 numdb = 7;
4451 4475 /* calculate number of indirects */
4452 4476 while (nblocks > 1) {
4453 4477 nblocks += DNODES_PER_LEVEL - 1;
4454 4478 nblocks /= DNODES_PER_LEVEL;
4455 4479 numdb += nblocks;
4456 4480 }
4457 4481 numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4458 4482 volsize *= ncopies;
4459 4483 /*
4460 4484 * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4461 4485 * compressed, but in practice they compress down to about
4462 4486 * 1100 bytes
4463 4487 */
4464 4488 numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4465 4489 volsize += numdb;
4466 4490 return (volsize);
4467 4491 }
↓ open down ↓ |
204 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX