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 2005 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 '%s' is busy"), zhp->zfs_name, 234 mountpoint); 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 rewind(mnttab_file); 265 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 266 getmntany(mnttab_file, &entry, &search) == 0)) { 267 268 if (mountpoint == NULL) 269 mountpoint = entry.mnt_mountp; 270 271 /* 272 * Always unshare the filesystem first. 273 */ 274 if (zfs_unshare(zhp, mountpoint) != 0) 275 return (-1); 276 277 /* 278 * Try to unmount the filesystem. There is no reason to try a 279 * forced unmount because the vnodes will still carry a 280 * reference to the underlying dataset, so we can't destroy it 281 * anyway. 282 * 283 * In the unmount case, we print out a slightly more informative 284 * error message, though we'll be relying on the poor error 285 * semantics from the kernel. 286 */ 287 if (umount2(mountpoint, flags) != 0) { 288 zfs_error(dgettext(TEXT_DOMAIN, 289 "cannot unmount '%s': %s"), 290 mountpoint, strerror(errno)); 291 return (-1); 292 } 293 294 /* 295 * Don't actually destroy the underlying directory 296 */ 297 } 298 299 return (0); 300 } 301 302 /* 303 * Unmount this filesystem and any children inheriting the mountpoint property. 304 * To do this, just act like we're changing the mountpoint property, but don't 305 * remount the filesystems afterwards. 306 */ 307 int 308 zfs_unmountall(zfs_handle_t *zhp, int flags) 309 { 310 prop_changelist_t *clp; 311 int ret; 312 313 clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 314 if (clp == NULL) 315 return (-1); 316 317 ret = changelist_prefix(clp); 318 changelist_free(clp); 319 320 return (ret); 321 } 322 323 /* 324 * Check to see if the filesystem is currently shared. 325 */ 326 int 327 zfs_is_shared(zfs_handle_t *zhp, char **where) 328 { 329 char *mountpoint; 330 331 if (!zfs_is_mounted(zhp, &mountpoint)) 332 return (FALSE); 333 334 if (is_shared(mountpoint)) { 335 if (where != NULL) 336 *where = mountpoint; 337 else 338 free(mountpoint); 339 return (TRUE); 340 } else { 341 free(mountpoint); 342 return (FALSE); 343 } 344 } 345 346 /* 347 * Share the given filesystem according to the options in 'sharenfs'. We rely 348 * on share(1M) to the dirty work for us. 349 */ 350 int 351 zfs_share(zfs_handle_t *zhp) 352 { 353 char mountpoint[ZFS_MAXPROPLEN]; 354 char shareopts[ZFS_MAXPROPLEN]; 355 char buf[MAXPATHLEN]; 356 FILE *fp; 357 358 /* ignore non-filesystems */ 359 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) 360 return (0); 361 362 /* return success if there is no mountpoint set */ 363 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 364 mountpoint, sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0 || 365 strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 366 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 367 return (0); 368 369 /* return success if there are no share options */ 370 if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 371 NULL, NULL, 0, FALSE) != 0 || 372 strcmp(shareopts, "off") == 0) 373 return (0); 374 375 /* 376 * If the 'zoned' property is set, simply return success since: 377 * 1. in a global zone, a dataset should not be shared if it's 378 * managed in a local zone. 379 * 2. in a local zone, NFS server is not available. 380 */ 381 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 382 return (0); 383 } 384 385 /* 386 * Invoke the share(1M) command. We always do this, even if it's 387 * currently shared, as the options may have changed. 388 */ 389 if (strcmp(shareopts, "on") == 0) 390 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 391 "-F nfs \"%s\" 2>&1", mountpoint); 392 else 393 (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 394 "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 395 mountpoint); 396 397 if ((fp = popen(buf, "r")) == NULL) { 398 zfs_error(dgettext(TEXT_DOMAIN, "cannot share '%s': " 399 "share(1M) failed"), zfs_get_name(zhp)); 400 return (-1); 401 } 402 403 /* 404 * share(1M) should only produce output if there is some kind 405 * of error. All output begins with "share_nfs: ", so we trim 406 * this off to get to the real error. 407 */ 408 if (fgets(buf, sizeof (buf), fp) != NULL) { 409 char *colon = strchr(buf, ':'); 410 411 while (buf[strlen(buf) - 1] == '\n') 412 buf[strlen(buf) - 1] = '\0'; 413 414 if (colon == NULL) 415 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 416 "'%s': share(1M) failed"), 417 zfs_get_name(zhp)); 418 else 419 zfs_error(dgettext(TEXT_DOMAIN, "cannot share " 420 "'%s': %s"), zfs_get_name(zhp), 421 colon + 2); 422 423 verify(pclose(fp) != 0); 424 return (-1); 425 } 426 427 verify(pclose(fp) == 0); 428 429 return (0); 430 } 431 432 /* 433 * Unshare the given filesystem. 434 */ 435 int 436 zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 437 { 438 char buf[MAXPATHLEN]; 439 struct mnttab search = { 0 }, entry; 440 441 /* check to see if need to unmount the filesystem */ 442 search.mnt_special = (char *)zfs_get_name(zhp); 443 rewind(mnttab_file); 444 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 445 getmntany(mnttab_file, &entry, &search) == 0)) { 446 447 if (mountpoint == NULL) 448 mountpoint = entry.mnt_mountp; 449 450 if (is_shared(mountpoint)) { 451 FILE *fp; 452 453 (void) snprintf(buf, sizeof (buf), 454 "/usr/sbin/unshare \"%s\" 2>&1", 455 mountpoint); 456 457 if ((fp = popen(buf, "r")) == NULL) { 458 zfs_error(dgettext(TEXT_DOMAIN, "cannot " 459 "unshare '%s': unshare(1M) failed"), 460 zfs_get_name(zhp)); 461 return (-1); 462 } 463 464 /* 465 * unshare(1M) should only produce output if there is 466 * some kind of error. All output begins with "unshare 467 * nfs: ", so we trim this off to get to the real error. 468 */ 469 if (fgets(buf, sizeof (buf), fp) != NULL) { 470 char *colon = strchr(buf, ':'); 471 472 while (buf[strlen(buf) - 1] == '\n') 473 buf[strlen(buf) - 1] = '\0'; 474 475 if (colon == NULL) 476 zfs_error(dgettext(TEXT_DOMAIN, 477 "cannot unshare '%s': unshare(1M) " 478 "failed"), zfs_get_name(zhp)); 479 else 480 zfs_error(dgettext(TEXT_DOMAIN, 481 "cannot unshare '%s': %s"), 482 zfs_get_name(zhp), colon + 2); 483 484 verify(pclose(fp) != 0); 485 return (-1); 486 } 487 488 verify(pclose(fp) == 0); 489 } 490 } 491 492 return (0); 493 } 494 495 /* 496 * Same as zfs_unmountall(), but for unshares. 497 */ 498 int 499 zfs_unshareall(zfs_handle_t *zhp) 500 { 501 prop_changelist_t *clp; 502 int ret; 503 504 clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 505 if (clp == NULL) 506 return (-1); 507 508 ret = changelist_unshare(clp); 509 changelist_free(clp); 510 511 return (ret); 512 } 513 514 /* 515 * Remove the mountpoint associated with the current dataset, if necessary. 516 * We only remove the underlying directory if: 517 * 518 * - The mountpoint is not 'none' or 'legacy' 519 * - The mountpoint is non-empty 520 * - The mountpoint is the default or inherited 521 * - The 'zoned' property is set, or we're in a local zone 522 * 523 * Any other directories we leave alone. 524 */ 525 void 526 remove_mountpoint(zfs_handle_t *zhp) 527 { 528 char mountpoint[ZFS_MAXPROPLEN]; 529 char source[ZFS_MAXNAMELEN]; 530 zfs_source_t sourcetype; 531 char zonename[ZONENAME_MAX]; 532 533 /* ignore non-filesystems */ 534 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 535 sizeof (mountpoint), &sourcetype, source, sizeof (source), 536 FALSE) != 0) 537 return; 538 539 if (getzonenamebyid(getzoneid(), zonename, sizeof (zonename)) < 0) 540 zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: " 541 "cannot determine current zone")); 542 543 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && 544 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 545 (sourcetype == ZFS_SRC_DEFAULT || 546 sourcetype == ZFS_SRC_INHERITED) && 547 (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || 548 strcmp(zonename, "global") != 0)) { 549 550 /* 551 * Try to remove the directory, silently ignoring any errors. 552 * The filesystem may have since been removed or moved around, 553 * and this isn't really useful to the administrator in any 554 * way. 555 */ 556 (void) rmdir(mountpoint); 557 } 558 } 559