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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 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 main entry points are: 32 * 33 * zfs_is_mounted() 34 * zfs_mount() 35 * zfs_unmount() 36 * zfs_unmountall() 37 * 38 * These functions are used by mount and unmount, and when changing a 39 * filesystem's mountpoint. This file also contains the functions used to 40 * manage sharing filesystems via NFS: 41 * 42 * zfs_is_shared() 43 * zfs_share() 44 * zfs_unshare() 45 * zfs_unshareall() 46 */ 47 48 #include <dirent.h> 49 #include <errno.h> 50 #include <libgen.h> 51 #include <libintl.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <strings.h> 55 #include <unistd.h> 56 #include <zone.h> 57 #include <sys/mntent.h> 58 #include <sys/mnttab.h> 59 #include <sys/mount.h> 60 #include <sys/stat.h> 61 62 #include <libzfs.h> 63 64 #include "libzfs_impl.h" 65 66 67 /* 68 * The following two files are opened as part of zfs_init(). It's OK to for 69 * the sharetab to be NULL, but mnttab must always be non-NULL; 70 */ 71 FILE *mnttab_file; 72 FILE *sharetab_file; 73 74 /* 75 * Search the sharetab for the given mountpoint, returning TRUE if it is found. 76 */ 77 static int 78 is_shared(const char *mountpoint) 79 { 80 char buf[MAXPATHLEN], *tab; 81 82 if (sharetab_file == NULL) 83 return (0); 84 85 (void) fseek(sharetab_file, 0, SEEK_SET); 86 87 while (fgets(buf, sizeof (buf), sharetab_file) != NULL) { 88 89 /* the mountpoint is the first entry on each line */ 90 if ((tab = strchr(buf, '\t')) != NULL) { 91 *tab = '\0'; 92 if (strcmp(buf, mountpoint) == 0) 93 return (1); 94 } 95 } 96 97 return (0); 98 } 99 100 /* 101 * Returns TRUE if the specified directory is empty. If we can't open the 102 * directory at all, return TRUE so that the mount can fail with a more 103 * informative error message. 104 */ 105 static int 106 dir_is_empty(const char *dirname) 107 { 108 DIR *dirp; 109 struct dirent64 *dp; 110 111 if ((dirp = opendir(dirname)) == NULL) 112 return (TRUE); 113 114 while ((dp = readdir64(dirp)) != NULL) { 115 116 if (strcmp(dp->d_name, ".") == 0 || 117 strcmp(dp->d_name, "..") == 0) 118 continue; 119 120 (void) closedir(dirp); 121 return (FALSE); 122 } 123 124 (void) closedir(dirp); 125 return (TRUE); 126 } 127 128 /* 129 * Checks to see if the mount is active. If the filesystem is mounted, we fill 130 * in 'where' with the current mountpoint, and return 1. Otherwise, we return 131 * 0. 132 */ 133 int 134 zfs_is_mounted(zfs_handle_t *zhp, char **where) 135 { 136 struct mnttab search = { 0 }, entry; 137 138 /* 139 * Search for the entry in /etc/mnttab. We don't bother getting the 140 * mountpoint, as we can just search for the special device. This will 141 * also let us find mounts when the mountpoint is 'legacy'. 142 */ 143 search.mnt_special = (char *)zfs_get_name(zhp); 144 search.mnt_fstype = MNTTYPE_ZFS; 145 146 rewind(mnttab_file); 147 if (getmntany(mnttab_file, &entry, &search) != 0) 148 return (FALSE); 149 150 if (where != NULL) 151 *where = zfs_strdup(entry.mnt_mountp); 152 153 return (TRUE); 154 } 155 156 /* 157 * Mount the given filesystem. 158 */ 159 int 160 zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 161 { 162 struct stat buf; 163 char mountpoint[ZFS_MAXPROPLEN]; 164 char mntopts[MNT_LINE_MAX]; 165 166 if (options == NULL) 167 mntopts[0] = '\0'; 168 else 169 (void) strlcpy(mntopts, options, sizeof (mntopts)); 170 171 /* ignore non-filesystems */ 172 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 173 sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0) 174 return (0); 175 176 /* return success if there is no mountpoint set */ 177 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 178 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 179 return (0); 180 181 /* 182 * If the 'zoned' property is set, and we're in the global zone, simply 183 * return success. 184 */ 185 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 186 char zonename[ZONENAME_MAX]; 187 if (getzonenamebyid(getzoneid(), zonename, 188 sizeof (zonename)) < 0) { 189 zfs_error(dgettext(TEXT_DOMAIN, "internal error: " 190 "cannot determine current zone")); 191 return (1); 192 } 193 194 if (strcmp(zonename, "global") == 0) 195 return (0); 196 } 197 198 /* Create the directory if it doesn't already exist */ 199 if (lstat(mountpoint, &buf) != 0) { 200 if (mkdirp(mountpoint, 0755) != 0) { 201 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 202 "unable to create mountpoint"), mountpoint); 203 return (1); 204 } 205 } 206 207 /* 208 * Determine if the mountpoint is empty. If so, refuse to perform the 209 * mount. We don't perform this check if MS_OVERLAY is specified, which 210 * would defeat the point. We also avoid this check if 'remount' is 211 * specified. 212 */ 213 if ((flags & MS_OVERLAY) == 0 && 214 strstr(mntopts, MNTOPT_REMOUNT) == NULL && 215 !dir_is_empty(mountpoint)) { 216 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 217 "directory is not empty"), mountpoint); 218 zfs_error(dgettext(TEXT_DOMAIN, "use legacy mountpoint to " 219 "allow this behavior, or use the -O flag")); 220 return (1); 221 } 222 223 /* perform the mount */ 224 if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 225 MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 226 /* 227 * Generic errors are nasty, but there are just way too many 228 * from mount(), and they're well-understood. We pick a few 229 * common ones to improve upon. 230 */ 231 switch (errno) { 232 case EBUSY: 233 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 234 "mountpoint or dataset is busy"), zhp->zfs_name); 235 break; 236 case EPERM: 237 case EACCES: 238 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 239 "permission denied"), zhp->zfs_name, 240 mountpoint); 241 break; 242 default: 243 zfs_error(dgettext(TEXT_DOMAIN, 244 "cannot mount '%s': %s"), 245 mountpoint, strerror(errno)); 246 break; 247 } 248 return (1); 249 } 250 251 return (0); 252 } 253 254 /* 255 * Unmount the given filesystem. 256 */ 257 int 258 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 259 { 260 struct mnttab search = { 0 }, entry; 261 262 /* check to see if need to unmount the filesystem */ 263 search.mnt_special = (char *)zfs_get_name(zhp); 264 search.mnt_fstype = MNTTYPE_ZFS; 265 rewind(mnttab_file); 266 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 267 getmntany(mnttab_file, &entry, &search) == 0)) { 268 269 if (mountpoint == NULL) 270 mountpoint = entry.mnt_mountp; 271 272 /* 273 * Always unshare the filesystem first. 274 */ 275 if (zfs_unshare(zhp, mountpoint) != 0) 276 return (-1); 277 278 /* 279 * Try to unmount the filesystem. There is no reason to try a 280 * forced unmount because the vnodes will still carry a 281 * reference to the underlying dataset, so we can't destroy it 282 * anyway. 283 * 284 * In the unmount case, we print out a slightly more informative 285 * error message, though we'll be relying on the poor error 286 * semantics from the kernel. 287 */ 288 if (umount2(mountpoint, flags) != 0) { 289 zfs_error(dgettext(TEXT_DOMAIN, 290 "cannot unmount '%s': %s"), 291 mountpoint, strerror(errno)); 292 return (-1); 293 } 294 295 /* 296 * Don't actually destroy the underlying directory 297 */ 298 } 299 300 return (0); 301 } 302 303 /* 304 * Unmount this filesystem and any children inheriting the mountpoint property. 305 * To do this, just act like we're changing the mountpoint property, but don't 306 * remount the filesystems afterwards. 307 */ 308 int 309 zfs_unmountall(zfs_handle_t *zhp, int flags) 310 { 311 prop_changelist_t *clp; 312 int ret; 313 314 clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 315 if (clp == NULL) 316 return (-1); 317 318 ret = changelist_prefix(clp); 319 changelist_free(clp); 320 321 return (ret); 322 } 323 324 /* 325 * Check to see if the filesystem is currently shared. 326 */ 327 int 328 zfs_is_shared(zfs_handle_t *zhp, char **where) 329 { 330 char *mountpoint; 331 332 if (!zfs_is_mounted(zhp, &mountpoint)) 333 return (FALSE); 334 335 if (is_shared(mountpoint)) { 336 if (where != NULL) 337 *where = mountpoint; 338 else 339 free(mountpoint); 340 return (TRUE); 341 } else { 342 free(mountpoint); 343 return (FALSE); 344 } 345 } 346 347 /* 348 * Share the given filesystem according to the options in 'sharenfs'. We rely 349 * on share(1M) to the dirty work for us. 350 */ 351 int 352 zfs_share(zfs_handle_t *zhp) 353 { 354 char mountpoint[ZFS_MAXPROPLEN]; 355 char shareopts[ZFS_MAXPROPLEN]; 356 char buf[MAXPATHLEN]; 357 FILE *fp; 358 359 /* ignore non-filesystems */ 360 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) 361 return (0); 362 363 /* return success if there is no mountpoint set */ 364 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 365 mountpoint, sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0 || 366 strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 367 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 368 return (0); 369 370 /* return success if there are no share options */ 371 if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 372 NULL, NULL, 0, FALSE) != 0 || 373 strcmp(shareopts, "off") == 0) 374 return (0); 375 376 /* 377 * If the 'zoned' property is set, simply return success since: 378 * 1. in a global zone, a dataset should not be shared if it's 379 * managed in a local zone. 380 * 2. in a local zone, NFS server is not available. 381 */ 382 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 383 return (0); 384 } 385 386 /* 387 * Invoke the share(1M) command. We always do this, even if it's 388 * currently shared, as the options may have changed. 389 */ 390 if (strcmp(shareopts, "on") == 0) 391 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 392 "-F nfs \"%s\" 2>&1", mountpoint); 393 else 394 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 395 "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 396 mountpoint); 397 398 if ((fp = popen(buf, "r")) == NULL) { 399 zfs_error(dgettext(TEXT_DOMAIN, "cannot share '%s': " 400 "share(1M) failed"), zfs_get_name(zhp)); 401 return (-1); 402 } 403 404 /* 405 * share(1M) should only produce output if there is some kind 406 * of error. All output begins with "share_nfs: ", so we trim 407 * this off to get to the real error. 408 */ 409 if (fgets(buf, sizeof (buf), fp) != NULL) { 410 char *colon = strchr(buf, ':'); 411 412 while (buf[strlen(buf) - 1] == '\n') 413 buf[strlen(buf) - 1] = '\0'; 414 415 if (colon == NULL) 416 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 417 "'%s': share(1M) failed"), 418 zfs_get_name(zhp)); 419 else 420 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 421 "'%s': %s"), zfs_get_name(zhp), 422 colon + 2); 423 424 verify(pclose(fp) != 0); 425 return (-1); 426 } 427 428 verify(pclose(fp) == 0); 429 430 return (0); 431 } 432 433 /* 434 * Unshare the given filesystem. 435 */ 436 int 437 zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 438 { 439 char buf[MAXPATHLEN]; 440 struct mnttab search = { 0 }, entry; 441 442 /* check to see if need to unmount the filesystem */ 443 search.mnt_special = (char *)zfs_get_name(zhp); 444 search.mnt_fstype = MNTTYPE_ZFS; 445 rewind(mnttab_file); 446 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 447 getmntany(mnttab_file, &entry, &search) == 0)) { 448 449 if (mountpoint == NULL) 450 mountpoint = entry.mnt_mountp; 451 452 if (is_shared(mountpoint)) { 453 FILE *fp; 454 455 (void) snprintf(buf, sizeof (buf), 456 "/usr/sbin/unshare \"%s\" 2>&1", 457 mountpoint); 458 459 if ((fp = popen(buf, "r")) == NULL) { 460 zfs_error(dgettext(TEXT_DOMAIN, "cannot " 461 "unshare '%s': unshare(1M) failed"), 462 zfs_get_name(zhp)); 463 return (-1); 464 } 465 466 /* 467 * unshare(1M) should only produce output if there is 468 * some kind of error. All output begins with "unshare 469 * nfs: ", so we trim this off to get to the real error. 470 */ 471 if (fgets(buf, sizeof (buf), fp) != NULL) { 472 char *colon = strchr(buf, ':'); 473 474 while (buf[strlen(buf) - 1] == '\n') 475 buf[strlen(buf) - 1] = '\0'; 476 477 if (colon == NULL) 478 zfs_error(dgettext(TEXT_DOMAIN, 479 "cannot unshare '%s': unshare(1M) " 480 "failed"), zfs_get_name(zhp)); 481 else 482 zfs_error(dgettext(TEXT_DOMAIN, 483 "cannot unshare '%s': %s"), 484 zfs_get_name(zhp), colon + 2); 485 486 verify(pclose(fp) != 0); 487 return (-1); 488 } 489 490 verify(pclose(fp) == 0); 491 } 492 } 493 494 return (0); 495 } 496 497 /* 498 * Same as zfs_unmountall(), but for unshares. 499 */ 500 int 501 zfs_unshareall(zfs_handle_t *zhp) 502 { 503 prop_changelist_t *clp; 504 int ret; 505 506 clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 507 if (clp == NULL) 508 return (-1); 509 510 ret = changelist_unshare(clp); 511 changelist_free(clp); 512 513 return (ret); 514 } 515 516 /* 517 * Remove the mountpoint associated with the current dataset, if necessary. 518 * We only remove the underlying directory if: 519 * 520 * - The mountpoint is not 'none' or 'legacy' 521 * - The mountpoint is non-empty 522 * - The mountpoint is the default or inherited 523 * - The 'zoned' property is set, or we're in a local zone 524 * 525 * Any other directories we leave alone. 526 */ 527 void 528 remove_mountpoint(zfs_handle_t *zhp) 529 { 530 char mountpoint[ZFS_MAXPROPLEN]; 531 char source[ZFS_MAXNAMELEN]; 532 zfs_source_t sourcetype; 533 char zonename[ZONENAME_MAX]; 534 535 /* ignore non-filesystems */ 536 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 537 sizeof (mountpoint), &sourcetype, source, sizeof (source), 538 FALSE) != 0) 539 return; 540 541 if (getzonenamebyid(getzoneid(), zonename, sizeof (zonename)) < 0) 542 zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: " 543 "cannot determine current zone")); 544 545 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && 546 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 547 (sourcetype == ZFS_SRC_DEFAULT || 548 sourcetype == ZFS_SRC_INHERITED) && 549 (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || 550 strcmp(zonename, "global") != 0)) { 551 552 /* 553 * Try to remove the directory, silently ignoring any errors. 554 * The filesystem may have since been removed or moved around, 555 * and this isn't really useful to the administrator in any 556 * way. 557 */ 558 (void) rmdir(mountpoint); 559 } 560 } 561