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