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