xref: /illumos-gate/usr/src/lib/libzfs/common/libzfs_mount.c (revision ea8dc4b6d2251b437950c0056bc626b311c73c27)
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