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 145 rewind(mnttab_file); 146 if (getmntany(mnttab_file, &entry, &search) != 0) 147 return (FALSE); 148 149 if (where != NULL) 150 *where = zfs_strdup(entry.mnt_mountp); 151 152 return (TRUE); 153 } 154 155 /* 156 * Mount the given filesystem. 157 */ 158 int 159 zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 160 { 161 struct stat buf; 162 char mountpoint[ZFS_MAXPROPLEN]; 163 char mntopts[MNT_LINE_MAX]; 164 165 if (options == NULL) 166 mntopts[0] = '\0'; 167 else 168 (void) strlcpy(mntopts, options, sizeof (mntopts)); 169 170 /* ignore non-filesystems */ 171 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 172 sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0) 173 return (0); 174 175 /* return success if there is no mountpoint set */ 176 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 177 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 178 return (0); 179 180 /* 181 * If the 'zoned' property is set, and we're in the global zone, simply 182 * return success. 183 */ 184 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 185 char zonename[ZONENAME_MAX]; 186 if (getzonenamebyid(getzoneid(), zonename, 187 sizeof (zonename)) < 0) { 188 zfs_error(dgettext(TEXT_DOMAIN, "internal error: " 189 "cannot determine current zone")); 190 return (1); 191 } 192 193 if (strcmp(zonename, "global") == 0) 194 return (0); 195 } 196 197 /* Create the directory if it doesn't already exist */ 198 if (lstat(mountpoint, &buf) != 0) { 199 if (mkdirp(mountpoint, 0755) != 0) { 200 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 201 "unable to create mountpoint"), mountpoint); 202 return (1); 203 } 204 } 205 206 /* 207 * Determine if the mountpoint is empty. If so, refuse to perform the 208 * mount. We don't perform this check if MS_OVERLAY is specified, which 209 * would defeat the point. We also avoid this check if 'remount' is 210 * specified. 211 */ 212 if ((flags & MS_OVERLAY) == 0 && 213 strstr(mntopts, MNTOPT_REMOUNT) == NULL && 214 !dir_is_empty(mountpoint)) { 215 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 216 "directory is not empty"), mountpoint); 217 zfs_error(dgettext(TEXT_DOMAIN, "use legacy mountpoint to " 218 "allow this behavior, or use the -O flag")); 219 return (1); 220 } 221 222 /* perform the mount */ 223 if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 224 MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 225 /* 226 * Generic errors are nasty, but there are just way too many 227 * from mount(), and they're well-understood. We pick a few 228 * common ones to improve upon. 229 */ 230 switch (errno) { 231 case EBUSY: 232 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 233 "mountpoint or dataset is busy"), zhp->zfs_name); 234 break; 235 case EPERM: 236 case EACCES: 237 zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': " 238 "permission denied"), zhp->zfs_name, 239 mountpoint); 240 break; 241 default: 242 zfs_error(dgettext(TEXT_DOMAIN, 243 "cannot mount '%s': %s"), 244 mountpoint, strerror(errno)); 245 break; 246 } 247 return (1); 248 } 249 250 return (0); 251 } 252 253 /* 254 * Unmount the given filesystem. 255 */ 256 int 257 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 258 { 259 struct mnttab search = { 0 }, entry; 260 261 /* check to see if need to unmount the filesystem */ 262 search.mnt_special = (char *)zfs_get_name(zhp); 263 rewind(mnttab_file); 264 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 265 getmntany(mnttab_file, &entry, &search) == 0)) { 266 267 if (mountpoint == NULL) 268 mountpoint = entry.mnt_mountp; 269 270 /* 271 * Always unshare the filesystem first. 272 */ 273 if (zfs_unshare(zhp, mountpoint) != 0) 274 return (-1); 275 276 /* 277 * Try to unmount the filesystem. There is no reason to try a 278 * forced unmount because the vnodes will still carry a 279 * reference to the underlying dataset, so we can't destroy it 280 * anyway. 281 * 282 * In the unmount case, we print out a slightly more informative 283 * error message, though we'll be relying on the poor error 284 * semantics from the kernel. 285 */ 286 if (umount2(mountpoint, flags) != 0) { 287 zfs_error(dgettext(TEXT_DOMAIN, 288 "cannot unmount '%s': %s"), 289 mountpoint, strerror(errno)); 290 return (-1); 291 } 292 293 /* 294 * Don't actually destroy the underlying directory 295 */ 296 } 297 298 return (0); 299 } 300 301 /* 302 * Unmount this filesystem and any children inheriting the mountpoint property. 303 * To do this, just act like we're changing the mountpoint property, but don't 304 * remount the filesystems afterwards. 305 */ 306 int 307 zfs_unmountall(zfs_handle_t *zhp, int flags) 308 { 309 prop_changelist_t *clp; 310 int ret; 311 312 clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 313 if (clp == NULL) 314 return (-1); 315 316 ret = changelist_prefix(clp); 317 changelist_free(clp); 318 319 return (ret); 320 } 321 322 /* 323 * Check to see if the filesystem is currently shared. 324 */ 325 int 326 zfs_is_shared(zfs_handle_t *zhp, char **where) 327 { 328 char *mountpoint; 329 330 if (!zfs_is_mounted(zhp, &mountpoint)) 331 return (FALSE); 332 333 if (is_shared(mountpoint)) { 334 if (where != NULL) 335 *where = mountpoint; 336 else 337 free(mountpoint); 338 return (TRUE); 339 } else { 340 free(mountpoint); 341 return (FALSE); 342 } 343 } 344 345 /* 346 * Share the given filesystem according to the options in 'sharenfs'. We rely 347 * on share(1M) to the dirty work for us. 348 */ 349 int 350 zfs_share(zfs_handle_t *zhp) 351 { 352 char mountpoint[ZFS_MAXPROPLEN]; 353 char shareopts[ZFS_MAXPROPLEN]; 354 char buf[MAXPATHLEN]; 355 FILE *fp; 356 357 /* ignore non-filesystems */ 358 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) 359 return (0); 360 361 /* return success if there is no mountpoint set */ 362 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 363 mountpoint, sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0 || 364 strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 365 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 366 return (0); 367 368 /* return success if there are no share options */ 369 if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 370 NULL, NULL, 0, FALSE) != 0 || 371 strcmp(shareopts, "off") == 0) 372 return (0); 373 374 /* 375 * If the 'zoned' property is set, simply return success since: 376 * 1. in a global zone, a dataset should not be shared if it's 377 * managed in a local zone. 378 * 2. in a local zone, NFS server is not available. 379 */ 380 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 381 return (0); 382 } 383 384 /* 385 * Invoke the share(1M) command. We always do this, even if it's 386 * currently shared, as the options may have changed. 387 */ 388 if (strcmp(shareopts, "on") == 0) 389 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 390 "-F nfs \"%s\" 2>&1", mountpoint); 391 else 392 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 393 "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 394 mountpoint); 395 396 if ((fp = popen(buf, "r")) == NULL) { 397 zfs_error(dgettext(TEXT_DOMAIN, "cannot share '%s': " 398 "share(1M) failed"), zfs_get_name(zhp)); 399 return (-1); 400 } 401 402 /* 403 * share(1M) should only produce output if there is some kind 404 * of error. All output begins with "share_nfs: ", so we trim 405 * this off to get to the real error. 406 */ 407 if (fgets(buf, sizeof (buf), fp) != NULL) { 408 char *colon = strchr(buf, ':'); 409 410 while (buf[strlen(buf) - 1] == '\n') 411 buf[strlen(buf) - 1] = '\0'; 412 413 if (colon == NULL) 414 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 415 "'%s': share(1M) failed"), 416 zfs_get_name(zhp)); 417 else 418 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 419 "'%s': %s"), zfs_get_name(zhp), 420 colon + 2); 421 422 verify(pclose(fp) != 0); 423 return (-1); 424 } 425 426 verify(pclose(fp) == 0); 427 428 return (0); 429 } 430 431 /* 432 * Unshare the given filesystem. 433 */ 434 int 435 zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 436 { 437 char buf[MAXPATHLEN]; 438 struct mnttab search = { 0 }, entry; 439 440 /* check to see if need to unmount the filesystem */ 441 search.mnt_special = (char *)zfs_get_name(zhp); 442 rewind(mnttab_file); 443 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 444 getmntany(mnttab_file, &entry, &search) == 0)) { 445 446 if (mountpoint == NULL) 447 mountpoint = entry.mnt_mountp; 448 449 if (is_shared(mountpoint)) { 450 FILE *fp; 451 452 (void) snprintf(buf, sizeof (buf), 453 "/usr/sbin/unshare \"%s\" 2>&1", 454 mountpoint); 455 456 if ((fp = popen(buf, "r")) == NULL) { 457 zfs_error(dgettext(TEXT_DOMAIN, "cannot " 458 "unshare '%s': unshare(1M) failed"), 459 zfs_get_name(zhp)); 460 return (-1); 461 } 462 463 /* 464 * unshare(1M) should only produce output if there is 465 * some kind of error. All output begins with "unshare 466 * nfs: ", so we trim this off to get to the real error. 467 */ 468 if (fgets(buf, sizeof (buf), fp) != NULL) { 469 char *colon = strchr(buf, ':'); 470 471 while (buf[strlen(buf) - 1] == '\n') 472 buf[strlen(buf) - 1] = '\0'; 473 474 if (colon == NULL) 475 zfs_error(dgettext(TEXT_DOMAIN, 476 "cannot unshare '%s': unshare(1M) " 477 "failed"), zfs_get_name(zhp)); 478 else 479 zfs_error(dgettext(TEXT_DOMAIN, 480 "cannot unshare '%s': %s"), 481 zfs_get_name(zhp), colon + 2); 482 483 verify(pclose(fp) != 0); 484 return (-1); 485 } 486 487 verify(pclose(fp) == 0); 488 } 489 } 490 491 return (0); 492 } 493 494 /* 495 * Same as zfs_unmountall(), but for unshares. 496 */ 497 int 498 zfs_unshareall(zfs_handle_t *zhp) 499 { 500 prop_changelist_t *clp; 501 int ret; 502 503 clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 504 if (clp == NULL) 505 return (-1); 506 507 ret = changelist_unshare(clp); 508 changelist_free(clp); 509 510 return (ret); 511 } 512 513 /* 514 * Remove the mountpoint associated with the current dataset, if necessary. 515 * We only remove the underlying directory if: 516 * 517 * - The mountpoint is not 'none' or 'legacy' 518 * - The mountpoint is non-empty 519 * - The mountpoint is the default or inherited 520 * - The 'zoned' property is set, or we're in a local zone 521 * 522 * Any other directories we leave alone. 523 */ 524 void 525 remove_mountpoint(zfs_handle_t *zhp) 526 { 527 char mountpoint[ZFS_MAXPROPLEN]; 528 char source[ZFS_MAXNAMELEN]; 529 zfs_source_t sourcetype; 530 char zonename[ZONENAME_MAX]; 531 532 /* ignore non-filesystems */ 533 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 534 sizeof (mountpoint), &sourcetype, source, sizeof (source), 535 FALSE) != 0) 536 return; 537 538 if (getzonenamebyid(getzoneid(), zonename, sizeof (zonename)) < 0) 539 zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: " 540 "cannot determine current zone")); 541 542 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && 543 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 544 (sourcetype == ZFS_SRC_DEFAULT || 545 sourcetype == ZFS_SRC_INHERITED) && 546 (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || 547 strcmp(zonename, "global") != 0)) { 548 549 /* 550 * Try to remove the directory, silently ignoring any errors. 551 * The filesystem may have since been removed or moved around, 552 * and this isn't really useful to the administrator in any 553 * way. 554 */ 555 (void) rmdir(mountpoint); 556 } 557 } 558