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