1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Routines to manage ZFS mounts. We separate all the nasty routines that have 31 * to deal with the OS. The following functions are the main entry points -- 32 * they are used by mount and unmount and when changing a filesystem's 33 * mountpoint. 34 * 35 * zfs_is_mounted() 36 * zfs_mount() 37 * zfs_unmount() 38 * zfs_unmountall() 39 * 40 * This file also contains the functions used to manage sharing filesystems via 41 * NFS and iSCSI: 42 * 43 * zfs_is_shared() 44 * zfs_share() 45 * zfs_unshare() 46 * 47 * zfs_is_shared_nfs() 48 * zfs_share_nfs() 49 * zfs_unshare_nfs() 50 * zfs_unshareall_nfs() 51 * zfs_is_shared_iscsi() 52 * zfs_share_iscsi() 53 * zfs_unshare_iscsi() 54 * 55 * The following functions are available for pool consumers, and will 56 * mount/unmount and share/unshare all datasets within pool: 57 * 58 * zpool_enable_datasets() 59 * zpool_disable_datasets() 60 */ 61 62 #include <dirent.h> 63 #include <dlfcn.h> 64 #include <errno.h> 65 #include <libgen.h> 66 #include <libintl.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <strings.h> 70 #include <unistd.h> 71 #include <zone.h> 72 #include <sys/mntent.h> 73 #include <sys/mnttab.h> 74 #include <sys/mount.h> 75 #include <sys/stat.h> 76 77 #include <libzfs.h> 78 79 #include "libzfs_impl.h" 80 81 static int (*iscsitgt_zfs_share)(const char *); 82 static int (*iscsitgt_zfs_unshare)(const char *); 83 static int (*iscsitgt_zfs_is_shared)(const char *); 84 85 #pragma init(zfs_iscsi_init) 86 static void 87 zfs_iscsi_init(void) 88 { 89 void *libiscsitgt; 90 91 if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1", 92 RTLD_LAZY | RTLD_GLOBAL)) == NULL || 93 (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt, 94 "iscsitgt_zfs_share")) == NULL || 95 (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt, 96 "iscsitgt_zfs_unshare")) == NULL || 97 (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt, 98 "iscsitgt_zfs_is_shared")) == NULL) { 99 iscsitgt_zfs_share = NULL; 100 iscsitgt_zfs_unshare = NULL; 101 iscsitgt_zfs_is_shared = NULL; 102 } 103 } 104 105 /* 106 * Search the sharetab for the given mountpoint, returning true if it is found. 107 */ 108 static boolean_t 109 is_shared(libzfs_handle_t *hdl, const char *mountpoint) 110 { 111 char buf[MAXPATHLEN], *tab; 112 113 if (hdl->libzfs_sharetab == NULL) 114 return (0); 115 116 (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 117 118 while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 119 120 /* the mountpoint is the first entry on each line */ 121 if ((tab = strchr(buf, '\t')) != NULL) { 122 *tab = '\0'; 123 if (strcmp(buf, mountpoint) == 0) 124 return (B_TRUE); 125 } 126 } 127 128 return (B_FALSE); 129 } 130 131 /* 132 * Returns true if the specified directory is empty. If we can't open the 133 * directory at all, return true so that the mount can fail with a more 134 * informative error message. 135 */ 136 static boolean_t 137 dir_is_empty(const char *dirname) 138 { 139 DIR *dirp; 140 struct dirent64 *dp; 141 142 if ((dirp = opendir(dirname)) == NULL) 143 return (B_TRUE); 144 145 while ((dp = readdir64(dirp)) != NULL) { 146 147 if (strcmp(dp->d_name, ".") == 0 || 148 strcmp(dp->d_name, "..") == 0) 149 continue; 150 151 (void) closedir(dirp); 152 return (B_FALSE); 153 } 154 155 (void) closedir(dirp); 156 return (B_TRUE); 157 } 158 159 /* 160 * Checks to see if the mount is active. If the filesystem is mounted, we fill 161 * in 'where' with the current mountpoint, and return 1. Otherwise, we return 162 * 0. 163 */ 164 boolean_t 165 is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where) 166 { 167 struct mnttab search = { 0 }, entry; 168 169 /* 170 * Search for the entry in /etc/mnttab. We don't bother getting the 171 * mountpoint, as we can just search for the special device. This will 172 * also let us find mounts when the mountpoint is 'legacy'. 173 */ 174 search.mnt_special = (char *)special; 175 search.mnt_fstype = MNTTYPE_ZFS; 176 177 rewind(zfs_hdl->libzfs_mnttab); 178 if (getmntany(zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 179 return (B_FALSE); 180 181 if (where != NULL) 182 *where = zfs_strdup(zfs_hdl, entry.mnt_mountp); 183 184 return (B_TRUE); 185 } 186 187 boolean_t 188 zfs_is_mounted(zfs_handle_t *zhp, char **where) 189 { 190 return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); 191 } 192 193 /* 194 * Returns true if the given dataset is mountable, false otherwise. Returns the 195 * mountpoint in 'buf'. 196 */ 197 static boolean_t 198 zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 199 zfs_source_t *source) 200 { 201 char sourceloc[ZFS_MAXNAMELEN]; 202 zfs_source_t sourcetype; 203 204 if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 205 return (B_FALSE); 206 207 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 208 &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 209 210 if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 211 strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 212 return (B_FALSE); 213 214 if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 215 return (B_FALSE); 216 217 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 218 getzoneid() == GLOBAL_ZONEID) 219 return (B_FALSE); 220 221 if (source) 222 *source = sourcetype; 223 224 return (B_TRUE); 225 } 226 227 /* 228 * Mount the given filesystem. 229 */ 230 int 231 zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 232 { 233 struct stat buf; 234 char mountpoint[ZFS_MAXPROPLEN]; 235 char mntopts[MNT_LINE_MAX]; 236 libzfs_handle_t *hdl = zhp->zfs_hdl; 237 238 if (options == NULL) 239 mntopts[0] = '\0'; 240 else 241 (void) strlcpy(mntopts, options, sizeof (mntopts)); 242 243 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 244 return (0); 245 246 /* Create the directory if it doesn't already exist */ 247 if (lstat(mountpoint, &buf) != 0) { 248 if (mkdirp(mountpoint, 0755) != 0) { 249 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 250 "failed to create mountpoint")); 251 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 252 dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 253 mountpoint)); 254 } 255 } 256 257 /* 258 * Determine if the mountpoint is empty. If so, refuse to perform the 259 * mount. We don't perform this check if MS_OVERLAY is specified, which 260 * would defeat the point. We also avoid this check if 'remount' is 261 * specified. 262 */ 263 if ((flags & MS_OVERLAY) == 0 && 264 strstr(mntopts, MNTOPT_REMOUNT) == NULL && 265 !dir_is_empty(mountpoint)) { 266 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 267 "directory is not empty")); 268 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 269 dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 270 } 271 272 /* perform the mount */ 273 if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 274 MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 275 /* 276 * Generic errors are nasty, but there are just way too many 277 * from mount(), and they're well-understood. We pick a few 278 * common ones to improve upon. 279 */ 280 if (errno == EBUSY) 281 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 282 "mountpoint or dataset is busy")); 283 else 284 zfs_error_aux(hdl, strerror(errno)); 285 286 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 287 dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 288 zhp->zfs_name)); 289 } 290 291 return (0); 292 } 293 294 /* 295 * Unmount a single filesystem. 296 */ 297 static int 298 unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 299 { 300 if (umount2(mountpoint, flags) != 0) { 301 zfs_error_aux(hdl, strerror(errno)); 302 return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, 303 dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 304 mountpoint)); 305 } 306 307 return (0); 308 } 309 310 /* 311 * Unmount the given filesystem. 312 */ 313 int 314 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 315 { 316 struct mnttab search = { 0 }, entry; 317 318 /* check to see if need to unmount the filesystem */ 319 search.mnt_special = zhp->zfs_name; 320 search.mnt_fstype = MNTTYPE_ZFS; 321 rewind(zhp->zfs_hdl->libzfs_mnttab); 322 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 323 getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 324 325 if (mountpoint == NULL) 326 mountpoint = entry.mnt_mountp; 327 328 /* 329 * Unshare and unmount the filesystem 330 */ 331 if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 332 unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 333 return (-1); 334 } 335 336 return (0); 337 } 338 339 /* 340 * Unmount this filesystem and any children inheriting the mountpoint property. 341 * To do this, just act like we're changing the mountpoint property, but don't 342 * remount the filesystems afterwards. 343 */ 344 int 345 zfs_unmountall(zfs_handle_t *zhp, int flags) 346 { 347 prop_changelist_t *clp; 348 int ret; 349 350 clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 351 if (clp == NULL) 352 return (-1); 353 354 ret = changelist_prefix(clp); 355 changelist_free(clp); 356 357 return (ret); 358 } 359 360 boolean_t 361 zfs_is_shared(zfs_handle_t *zhp) 362 { 363 if (ZFS_IS_VOLUME(zhp)) 364 return (zfs_is_shared_iscsi(zhp)); 365 366 return (zfs_is_shared_nfs(zhp, NULL)); 367 } 368 369 int 370 zfs_share(zfs_handle_t *zhp) 371 { 372 if (ZFS_IS_VOLUME(zhp)) 373 return (zfs_share_iscsi(zhp)); 374 375 return (zfs_share_nfs(zhp)); 376 } 377 378 int 379 zfs_unshare(zfs_handle_t *zhp) 380 { 381 if (ZFS_IS_VOLUME(zhp)) 382 return (zfs_unshare_iscsi(zhp)); 383 384 return (zfs_unshare_nfs(zhp, NULL)); 385 } 386 387 /* 388 * Check to see if the filesystem is currently shared. 389 */ 390 boolean_t 391 zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 392 { 393 char *mountpoint; 394 395 if (!zfs_is_mounted(zhp, &mountpoint)) 396 return (B_FALSE); 397 398 if (is_shared(zhp->zfs_hdl, mountpoint)) { 399 if (where != NULL) 400 *where = mountpoint; 401 else 402 free(mountpoint); 403 return (B_TRUE); 404 } else { 405 free(mountpoint); 406 return (B_FALSE); 407 } 408 } 409 410 /* 411 * Share the given filesystem according to the options in 'sharenfs'. We rely 412 * on share(1M) to the dirty work for us. 413 */ 414 int 415 zfs_share_nfs(zfs_handle_t *zhp) 416 { 417 char mountpoint[ZFS_MAXPROPLEN]; 418 char shareopts[ZFS_MAXPROPLEN]; 419 char buf[MAXPATHLEN]; 420 FILE *fp; 421 libzfs_handle_t *hdl = zhp->zfs_hdl; 422 423 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 424 return (0); 425 426 /* 427 * Return success if there are no share options. 428 */ 429 if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 430 NULL, NULL, 0, B_FALSE) != 0 || 431 strcmp(shareopts, "off") == 0) 432 return (0); 433 434 /* 435 * If the 'zoned' property is set, then zfs_is_mountable() will have 436 * already bailed out if we are in the global zone. But local 437 * zones cannot be NFS servers, so we ignore it for local zones as well. 438 */ 439 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 440 return (0); 441 442 /* 443 * Invoke the share(1M) command. We always do this, even if it's 444 * currently shared, as the options may have changed. 445 */ 446 if (strcmp(shareopts, "on") == 0) 447 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 448 "-F nfs \"%s\" 2>&1", mountpoint); 449 else 450 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 451 "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 452 mountpoint); 453 454 if ((fp = popen(buf, "r")) == NULL) 455 return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 456 dgettext(TEXT_DOMAIN, "cannot share '%s'"), 457 zfs_get_name(zhp))); 458 459 /* 460 * share(1M) should only produce output if there is some kind 461 * of error. All output begins with "share_nfs: ", so we trim 462 * this off to get to the real error. 463 */ 464 if (fgets(buf, sizeof (buf), fp) != NULL) { 465 char *colon = strchr(buf, ':'); 466 467 while (buf[strlen(buf) - 1] == '\n') 468 buf[strlen(buf) - 1] = '\0'; 469 470 if (colon != NULL) 471 zfs_error_aux(hdl, colon + 2); 472 473 (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 474 dgettext(TEXT_DOMAIN, "cannot share '%s'"), 475 zfs_get_name(zhp)); 476 477 verify(pclose(fp) != 0); 478 return (-1); 479 } 480 481 verify(pclose(fp) == 0); 482 483 return (0); 484 } 485 486 /* 487 * Unshare a filesystem by mountpoint. 488 */ 489 static int 490 unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 491 { 492 char buf[MAXPATHLEN]; 493 FILE *fp; 494 495 (void) snprintf(buf, sizeof (buf), 496 "/usr/sbin/unshare \"%s\" 2>&1", 497 mountpoint); 498 499 if ((fp = popen(buf, "r")) == NULL) 500 return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 501 dgettext(TEXT_DOMAIN, 502 "cannot unshare '%s'"), name)); 503 504 /* 505 * unshare(1M) should only produce output if there is 506 * some kind of error. All output begins with "unshare 507 * nfs: ", so we trim this off to get to the real error. 508 */ 509 if (fgets(buf, sizeof (buf), fp) != NULL) { 510 char *colon = strchr(buf, ':'); 511 512 while (buf[strlen(buf) - 1] == '\n') 513 buf[strlen(buf) - 1] = '\0'; 514 515 if (colon != NULL) 516 zfs_error_aux(hdl, colon + 2); 517 518 verify(pclose(fp) != 0); 519 520 return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 521 dgettext(TEXT_DOMAIN, 522 "cannot unshare '%s'"), name)); 523 } 524 525 verify(pclose(fp) == 0); 526 527 return (0); 528 } 529 530 /* 531 * Unshare the given filesystem. 532 */ 533 int 534 zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 535 { 536 struct mnttab search = { 0 }, entry; 537 538 /* check to see if need to unmount the filesystem */ 539 search.mnt_special = (char *)zfs_get_name(zhp); 540 search.mnt_fstype = MNTTYPE_ZFS; 541 rewind(zhp->zfs_hdl->libzfs_mnttab); 542 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 543 getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 544 545 if (mountpoint == NULL) 546 mountpoint = entry.mnt_mountp; 547 548 if (is_shared(zhp->zfs_hdl, mountpoint) && 549 unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 550 return (-1); 551 } 552 553 return (0); 554 } 555 556 /* 557 * Same as zfs_unmountall(), but for NFS unshares. 558 */ 559 int 560 zfs_unshareall_nfs(zfs_handle_t *zhp) 561 { 562 prop_changelist_t *clp; 563 int ret; 564 565 clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 566 if (clp == NULL) 567 return (-1); 568 569 ret = changelist_unshare(clp); 570 changelist_free(clp); 571 572 return (ret); 573 } 574 575 /* 576 * Remove the mountpoint associated with the current dataset, if necessary. 577 * We only remove the underlying directory if: 578 * 579 * - The mountpoint is not 'none' or 'legacy' 580 * - The mountpoint is non-empty 581 * - The mountpoint is the default or inherited 582 * - The 'zoned' property is set, or we're in a local zone 583 * 584 * Any other directories we leave alone. 585 */ 586 void 587 remove_mountpoint(zfs_handle_t *zhp) 588 { 589 char mountpoint[ZFS_MAXPROPLEN]; 590 zfs_source_t source; 591 592 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 593 &source)) 594 return; 595 596 if (source == ZFS_SRC_DEFAULT || 597 source == ZFS_SRC_INHERITED) { 598 /* 599 * Try to remove the directory, silently ignoring any errors. 600 * The filesystem may have since been removed or moved around, 601 * and this error isn't really useful to the administrator in 602 * any way. 603 */ 604 (void) rmdir(mountpoint); 605 } 606 } 607 608 boolean_t 609 zfs_is_shared_iscsi(zfs_handle_t *zhp) 610 { 611 return (iscsitgt_zfs_is_shared != NULL && 612 iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 613 } 614 615 int 616 zfs_share_iscsi(zfs_handle_t *zhp) 617 { 618 char shareopts[ZFS_MAXPROPLEN]; 619 const char *dataset = zhp->zfs_name; 620 libzfs_handle_t *hdl = zhp->zfs_hdl; 621 622 /* 623 * Return success if there are no share options. 624 */ 625 if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 626 sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 627 strcmp(shareopts, "off") == 0) 628 return (0); 629 630 if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) 631 return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED, 632 dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 633 634 return (0); 635 } 636 637 int 638 zfs_unshare_iscsi(zfs_handle_t *zhp) 639 { 640 const char *dataset = zfs_get_name(zhp); 641 libzfs_handle_t *hdl = zhp->zfs_hdl; 642 643 /* 644 * Return if the volume is not shared 645 */ 646 if (!zfs_is_shared_iscsi(zhp)) 647 return (0); 648 649 /* 650 * If this fails with ENODEV it indicates that zvol wasn't shared so 651 * we should return success in that case. 652 */ 653 if (iscsitgt_zfs_unshare == NULL || 654 (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) 655 return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED, 656 dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 657 658 return (0); 659 } 660 661 typedef struct mount_cbdata { 662 zfs_handle_t **cb_datasets; 663 int cb_used; 664 int cb_alloc; 665 } mount_cbdata_t; 666 667 static int 668 mount_cb(zfs_handle_t *zhp, void *data) 669 { 670 mount_cbdata_t *cbp = data; 671 672 if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 673 zfs_close(zhp); 674 return (0); 675 } 676 677 if (cbp->cb_alloc == cbp->cb_used) { 678 void *ptr; 679 680 if ((ptr = zfs_realloc(zhp->zfs_hdl, 681 cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 682 cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 683 return (-1); 684 cbp->cb_datasets = ptr; 685 686 cbp->cb_alloc *= 2; 687 } 688 689 cbp->cb_datasets[cbp->cb_used++] = zhp; 690 691 return (zfs_iter_children(zhp, mount_cb, cbp)); 692 } 693 694 static int 695 dataset_cmp(const void *a, const void *b) 696 { 697 zfs_handle_t **za = (zfs_handle_t **)a; 698 zfs_handle_t **zb = (zfs_handle_t **)b; 699 char mounta[MAXPATHLEN]; 700 char mountb[MAXPATHLEN]; 701 boolean_t gota, gotb; 702 703 if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 704 verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 705 sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 706 if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 707 verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 708 sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 709 710 if (gota && gotb) 711 return (strcmp(mounta, mountb)); 712 713 if (gota) 714 return (-1); 715 if (gotb) 716 return (1); 717 718 return (strcmp(zfs_get_name(a), zfs_get_name(b))); 719 } 720 721 /* 722 * Mount and share all datasets within the given pool. This assumes that no 723 * datasets within the pool are currently mounted. Because users can create 724 * complicated nested hierarchies of mountpoints, we first gather all the 725 * datasets and mountpoints within the pool, and sort them by mountpoint. Once 726 * we have the list of all filesystems, we iterate over them in order and mount 727 * and/or share each one. 728 */ 729 #pragma weak zpool_mount_datasets = zpool_enable_datasets 730 int 731 zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 732 { 733 mount_cbdata_t cb = { 0 }; 734 libzfs_handle_t *hdl = zhp->zpool_hdl; 735 zfs_handle_t *zfsp; 736 int i, ret = -1; 737 738 /* 739 * Gather all datasets within the pool. 740 */ 741 if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 742 return (-1); 743 cb.cb_alloc = 4; 744 745 if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 746 goto out; 747 748 cb.cb_datasets[0] = zfsp; 749 cb.cb_used = 1; 750 751 if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 752 goto out; 753 754 /* 755 * Sort the datasets by mountpoint. 756 */ 757 qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 758 759 /* 760 * And mount all the datasets. 761 */ 762 ret = 0; 763 for (i = 0; i < cb.cb_used; i++) { 764 if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 765 zfs_share(cb.cb_datasets[i]) != 0) 766 ret = -1; 767 } 768 769 out: 770 for (i = 0; i < cb.cb_used; i++) 771 zfs_close(cb.cb_datasets[i]); 772 free(cb.cb_datasets); 773 774 return (ret); 775 } 776 777 778 static int 779 zvol_cb(const char *dataset, void *data) 780 { 781 libzfs_handle_t *hdl = data; 782 zfs_handle_t *zhp; 783 784 /* 785 * Ignore snapshots and ignore failures from non-existant datasets. 786 */ 787 if (strchr(dataset, '@') != NULL || 788 (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 789 return (0); 790 791 (void) zfs_unshare_iscsi(zhp); 792 793 zfs_close(zhp); 794 795 return (0); 796 } 797 798 static int 799 mountpoint_compare(const void *a, const void *b) 800 { 801 const char *mounta = *((char **)a); 802 const char *mountb = *((char **)b); 803 804 return (strcmp(mountb, mounta)); 805 } 806 807 /* 808 * Unshare and unmount all datasets within the given pool. We don't want to 809 * rely on traversing the DSL to discover the filesystems within the pool, 810 * because this may be expensive (if not all of them are mounted), and can fail 811 * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 812 * gather all the filesystems that are currently mounted. 813 */ 814 #pragma weak zpool_unmount_datasets = zpool_disable_datasets 815 int 816 zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 817 { 818 int used, alloc; 819 struct mnttab entry; 820 size_t namelen; 821 char **mountpoints = NULL; 822 zfs_handle_t **datasets = NULL; 823 libzfs_handle_t *hdl = zhp->zpool_hdl; 824 int i; 825 int ret = -1; 826 int flags = (force ? MS_FORCE : 0); 827 828 /* 829 * First unshare all zvols. 830 */ 831 if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 832 return (-1); 833 834 namelen = strlen(zhp->zpool_name); 835 836 rewind(hdl->libzfs_mnttab); 837 used = alloc = 0; 838 while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 839 /* 840 * Ignore non-ZFS entries. 841 */ 842 if (entry.mnt_fstype == NULL || 843 strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 844 continue; 845 846 /* 847 * Ignore filesystems not within this pool. 848 */ 849 if (entry.mnt_mountp == NULL || 850 strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 851 (entry.mnt_special[namelen] != '/' && 852 entry.mnt_special[namelen] != '\0')) 853 continue; 854 855 /* 856 * At this point we've found a filesystem within our pool. Add 857 * it to our growing list. 858 */ 859 if (used == alloc) { 860 if (alloc == 0) { 861 if ((mountpoints = zfs_alloc(hdl, 862 8 * sizeof (void *))) == NULL) 863 goto out; 864 865 if ((datasets = zfs_alloc(hdl, 866 8 * sizeof (void *))) == NULL) 867 goto out; 868 869 alloc = 8; 870 } else { 871 void *ptr; 872 873 if ((ptr = zfs_realloc(hdl, mountpoints, 874 alloc * sizeof (void *), 875 alloc * 2 * sizeof (void *))) == NULL) 876 goto out; 877 mountpoints = ptr; 878 879 if ((ptr = zfs_realloc(hdl, datasets, 880 alloc * sizeof (void *), 881 alloc * 2 * sizeof (void *))) == NULL) 882 goto out; 883 datasets = ptr; 884 885 alloc *= 2; 886 } 887 } 888 889 if ((mountpoints[used] = zfs_strdup(hdl, 890 entry.mnt_mountp)) == NULL) 891 goto out; 892 893 /* 894 * This is allowed to fail, in case there is some I/O error. It 895 * is only used to determine if we need to remove the underlying 896 * mountpoint, so failure is not fatal. 897 */ 898 datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 899 900 used++; 901 } 902 903 /* 904 * At this point, we have the entire list of filesystems, so sort it by 905 * mountpoint. 906 */ 907 qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 908 909 /* 910 * Walk through and first unshare everything. 911 */ 912 for (i = 0; i < used; i++) { 913 if (is_shared(hdl, mountpoints[i]) && 914 unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 915 goto out; 916 } 917 918 /* 919 * Now unmount everything, removing the underlying directories as 920 * appropriate. 921 */ 922 for (i = 0; i < used; i++) { 923 if (unmount_one(hdl, mountpoints[i], flags) != 0) 924 goto out; 925 } 926 927 for (i = 0; i < used; i++) { 928 if (datasets[i]) 929 remove_mountpoint(datasets[i]); 930 } 931 932 ret = 0; 933 out: 934 for (i = 0; i < used; i++) { 935 if (datasets[i]) 936 zfs_close(datasets[i]); 937 free(mountpoints[i]); 938 } 939 free(datasets); 940 free(mountpoints); 941 942 return (ret); 943 } 944