xref: /titanic_50/usr/src/lib/libzfs/common/libzfs_mount.c (revision 65cd9f2809a015b46790a9c5c2ef992d56177624)
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