xref: /freebsd/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_mount_os.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
15  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
16  * Copyright (c) 2014, 2021 by Delphix. All rights reserved.
17  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
18  * Copyright 2017 RackTop Systems.
19  * Copyright (c) 2018 Datto Inc.
20  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
21  */
22 
23 #include <dirent.h>
24 #include <dlfcn.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <libgen.h>
28 #include <libintl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <zone.h>
34 #include <sys/mntent.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/vfs.h>
38 #include <sys/dsl_crypt.h>
39 #include <libzfs.h>
40 
41 #include "../../libzfs_impl.h"
42 
43 #define	ZS_COMMENT	0x00000000	/* comment */
44 #define	ZS_ZFSUTIL	0x00000001	/* caller is zfs(8) */
45 
46 typedef struct option_map {
47 	const char *name;
48 	unsigned long mntmask;
49 	unsigned long zfsmask;
50 } option_map_t;
51 
52 static const option_map_t option_map[] = {
53 	/* Canonicalized filesystem independent options from mount(8) */
54 	{ MNTOPT_NOAUTO,	MS_COMMENT,	ZS_COMMENT	},
55 	{ MNTOPT_DEFAULTS,	MS_COMMENT,	ZS_COMMENT	},
56 	{ MNTOPT_NODEVICES,	MS_NODEV,	ZS_COMMENT	},
57 	{ MNTOPT_DEVICES,	MS_COMMENT,	ZS_COMMENT	},
58 	{ MNTOPT_DIRSYNC,	MS_DIRSYNC,	ZS_COMMENT	},
59 	{ MNTOPT_NOEXEC,	MS_NOEXEC,	ZS_COMMENT	},
60 	{ MNTOPT_EXEC,		MS_COMMENT,	ZS_COMMENT	},
61 	{ MNTOPT_GROUP,		MS_GROUP,	ZS_COMMENT	},
62 	{ MNTOPT_NETDEV,	MS_COMMENT,	ZS_COMMENT	},
63 	{ MNTOPT_NOFAIL,	MS_COMMENT,	ZS_COMMENT	},
64 	{ MNTOPT_NOSUID,	MS_NOSUID,	ZS_COMMENT	},
65 	{ MNTOPT_SUID,		MS_COMMENT,	ZS_COMMENT	},
66 	{ MNTOPT_OWNER,		MS_OWNER,	ZS_COMMENT	},
67 	{ MNTOPT_REMOUNT,	MS_REMOUNT,	ZS_COMMENT	},
68 	{ MNTOPT_RO,		MS_RDONLY,	ZS_COMMENT	},
69 	{ MNTOPT_RW,		MS_COMMENT,	ZS_COMMENT	},
70 	{ MNTOPT_SYNC,		MS_SYNCHRONOUS,	ZS_COMMENT	},
71 	{ MNTOPT_USER,		MS_USERS,	ZS_COMMENT	},
72 	{ MNTOPT_USERS,		MS_USERS,	ZS_COMMENT	},
73 	/* acl flags passed with util-linux-2.24 mount command */
74 	{ MNTOPT_ACL,		MS_POSIXACL,	ZS_COMMENT	},
75 	{ MNTOPT_NOACL,		MS_COMMENT,	ZS_COMMENT	},
76 	{ MNTOPT_POSIXACL,	MS_POSIXACL,	ZS_COMMENT	},
77 	/*
78 	 * Case sensitive options are just listed here to silently
79 	 * ignore the error if passed with zfs mount command.
80 	 */
81 	{ MNTOPT_CASESENSITIVE,		MS_COMMENT,	ZS_COMMENT	},
82 	{ MNTOPT_CASEINSENSITIVE,	MS_COMMENT,	ZS_COMMENT	},
83 	{ MNTOPT_CASEMIXED,		MS_COMMENT,	ZS_COMMENT	},
84 #ifdef MS_NOATIME
85 	{ MNTOPT_NOATIME,	MS_NOATIME,	ZS_COMMENT	},
86 	{ MNTOPT_ATIME,		MS_COMMENT,	ZS_COMMENT	},
87 #endif
88 #ifdef MS_NODIRATIME
89 	{ MNTOPT_NODIRATIME,	MS_NODIRATIME,	ZS_COMMENT	},
90 	{ MNTOPT_DIRATIME,	MS_COMMENT,	ZS_COMMENT	},
91 #endif
92 #ifdef MS_RELATIME
93 	{ MNTOPT_RELATIME,	MS_RELATIME,	ZS_COMMENT	},
94 	{ MNTOPT_NORELATIME,	MS_COMMENT,	ZS_COMMENT	},
95 #endif
96 #ifdef MS_STRICTATIME
97 	{ MNTOPT_STRICTATIME,	MS_STRICTATIME,	ZS_COMMENT	},
98 	{ MNTOPT_NOSTRICTATIME,	MS_COMMENT,	ZS_COMMENT	},
99 #endif
100 #ifdef MS_LAZYTIME
101 	{ MNTOPT_LAZYTIME,	MS_LAZYTIME,	ZS_COMMENT	},
102 #endif
103 	{ MNTOPT_CONTEXT,	MS_COMMENT,	ZS_COMMENT	},
104 	{ MNTOPT_FSCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
105 	{ MNTOPT_DEFCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
106 	{ MNTOPT_ROOTCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
107 #ifdef MS_I_VERSION
108 	{ MNTOPT_IVERSION,	MS_I_VERSION,	ZS_COMMENT	},
109 #endif
110 #ifdef MS_MANDLOCK
111 	{ MNTOPT_NBMAND,	MS_MANDLOCK,	ZS_COMMENT	},
112 	{ MNTOPT_NONBMAND,	MS_COMMENT,	ZS_COMMENT	},
113 #endif
114 	/* Valid options not found in mount(8) */
115 	{ MNTOPT_BIND,		MS_BIND,	ZS_COMMENT	},
116 #ifdef MS_REC
117 	{ MNTOPT_RBIND,		MS_BIND|MS_REC,	ZS_COMMENT	},
118 #endif
119 	{ MNTOPT_COMMENT,	MS_COMMENT,	ZS_COMMENT	},
120 #ifdef MS_NOSUB
121 	{ MNTOPT_NOSUB,		MS_NOSUB,	ZS_COMMENT	},
122 #endif
123 #ifdef MS_SILENT
124 	{ MNTOPT_QUIET,		MS_SILENT,	ZS_COMMENT	},
125 #endif
126 	/* Custom zfs options */
127 	{ MNTOPT_XATTR,		MS_COMMENT,	ZS_COMMENT	},
128 	{ MNTOPT_NOXATTR,	MS_COMMENT,	ZS_COMMENT	},
129 	{ MNTOPT_ZFSUTIL,	MS_COMMENT,	ZS_ZFSUTIL	},
130 	{ NULL,			0,		0		} };
131 
132 /*
133  * Break the mount option in to a name/value pair.  The name is
134  * validated against the option map and mount flags set accordingly.
135  */
136 static int
parse_option(char * mntopt,unsigned long * mntflags,unsigned long * zfsflags,int sloppy)137 parse_option(char *mntopt, unsigned long *mntflags,
138     unsigned long *zfsflags, int sloppy)
139 {
140 	const option_map_t *opt;
141 	char *ptr, *name, *value = NULL;
142 	int error = 0;
143 
144 	name = strdup(mntopt);
145 	if (name == NULL)
146 		return (ENOMEM);
147 
148 	for (ptr = name; ptr && *ptr; ptr++) {
149 		if (*ptr == '=') {
150 			*ptr = '\0';
151 			value = ptr+1;
152 			VERIFY3P(value, !=, NULL);
153 			break;
154 		}
155 	}
156 
157 	for (opt = option_map; opt->name != NULL; opt++) {
158 		if (strncmp(name, opt->name, strlen(name)) == 0) {
159 			*mntflags |= opt->mntmask;
160 			*zfsflags |= opt->zfsmask;
161 			error = 0;
162 			goto out;
163 		}
164 	}
165 
166 	if (!sloppy)
167 		error = ENOENT;
168 out:
169 	/* If required further process on the value may be done here */
170 	free(name);
171 	return (error);
172 }
173 
174 /*
175  * Translate the mount option string in to MS_* mount flags for the
176  * kernel vfs.  When sloppy is non-zero unknown options will be ignored
177  * otherwise they are considered fatal are copied in to badopt.
178  */
179 int
zfs_parse_mount_options(const char * mntopts,unsigned long * mntflags,unsigned long * zfsflags,int sloppy,char * badopt,char * mtabopt)180 zfs_parse_mount_options(const char *mntopts, unsigned long *mntflags,
181     unsigned long *zfsflags, int sloppy, char *badopt, char *mtabopt)
182 {
183 	int error = 0, quote = 0, flag = 0, count = 0;
184 	char *ptr, *opt, *opts;
185 
186 	opts = strdup(mntopts);
187 	if (opts == NULL)
188 		return (ENOMEM);
189 
190 	*mntflags = 0;
191 	opt = NULL;
192 
193 	/*
194 	 * Scan through all mount options which must be comma delimited.
195 	 * We must be careful to notice regions which are double quoted
196 	 * and skip commas in these regions.  Each option is then checked
197 	 * to determine if it is a known option.
198 	 */
199 	for (ptr = opts; ptr && !flag; ptr++) {
200 		if (opt == NULL)
201 			opt = ptr;
202 
203 		if (*ptr == '"')
204 			quote = !quote;
205 
206 		if (quote)
207 			continue;
208 
209 		if (*ptr == '\0')
210 			flag = 1;
211 
212 		if ((*ptr == ',') || (*ptr == '\0')) {
213 			*ptr = '\0';
214 
215 			error = parse_option(opt, mntflags, zfsflags, sloppy);
216 			if (error) {
217 				strcpy(badopt, opt);
218 				goto out;
219 
220 			}
221 
222 			if (!(*mntflags & MS_REMOUNT) &&
223 			    !(*zfsflags & ZS_ZFSUTIL) &&
224 			    mtabopt != NULL) {
225 				if (count > 0)
226 					strlcat(mtabopt, ",", MNT_LINE_MAX);
227 
228 				strlcat(mtabopt, opt, MNT_LINE_MAX);
229 				count++;
230 			}
231 
232 			opt = NULL;
233 		}
234 	}
235 
236 out:
237 	free(opts);
238 	return (error);
239 }
240 
241 static void
append_mntopt(const char * name,const char * val,char * mntopts,char * mtabopt,boolean_t quote)242 append_mntopt(const char *name, const char *val, char *mntopts,
243     char *mtabopt, boolean_t quote)
244 {
245 	char tmp[MNT_LINE_MAX];
246 
247 	snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val);
248 
249 	if (mntopts)
250 		strlcat(mntopts, tmp, MNT_LINE_MAX);
251 
252 	if (mtabopt)
253 		strlcat(mtabopt, tmp, MNT_LINE_MAX);
254 }
255 
256 static void
zfs_selinux_setcontext(zfs_handle_t * zhp,zfs_prop_t zpt,const char * name,char * mntopts,char * mtabopt)257 zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name,
258     char *mntopts, char *mtabopt)
259 {
260 	char context[ZFS_MAXPROPLEN];
261 
262 	if (zfs_prop_get(zhp, zpt, context, sizeof (context),
263 	    NULL, NULL, 0, B_FALSE) == 0) {
264 		if (strcmp(context, "none") != 0)
265 			append_mntopt(name, context, mntopts, mtabopt, B_TRUE);
266 	}
267 }
268 
269 void
zfs_adjust_mount_options(zfs_handle_t * zhp,const char * mntpoint,char * mntopts,char * mtabopt)270 zfs_adjust_mount_options(zfs_handle_t *zhp, const char *mntpoint,
271     char *mntopts, char *mtabopt)
272 {
273 	char prop[ZFS_MAXPROPLEN];
274 
275 	/*
276 	 * Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists
277 	 * if it does, create a tmp variable in case it's needed
278 	 * checks to see if the selinux context is set to the default
279 	 * if it is, allow the setting of the other context properties
280 	 * this is needed because the 'context' property overrides others
281 	 * if it is not the default, set the 'context' property
282 	 */
283 	if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop),
284 	    NULL, NULL, 0, B_FALSE) == 0) {
285 		if (strcmp(prop, "none") == 0) {
286 			zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT,
287 			    MNTOPT_FSCONTEXT, mntopts, mtabopt);
288 			zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT,
289 			    MNTOPT_DEFCONTEXT, mntopts, mtabopt);
290 			zfs_selinux_setcontext(zhp,
291 			    ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
292 			    mntopts, mtabopt);
293 		} else {
294 			append_mntopt(MNTOPT_CONTEXT, prop,
295 			    mntopts, mtabopt, B_TRUE);
296 		}
297 	}
298 
299 	/* A hint used to determine an auto-mounted snapshot mount point */
300 	append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
301 }
302 
303 /*
304  * By default the filesystem by preparing the mount options (i.e. parsing
305  * some flags from the "opts" parameter into the "flags" parameter) and then
306  * directly calling the system call mount(2). We don't need the mount utility
307  * or update /etc/mtab, because this is a symlink on all modern systems.
308  *
309  * If the environment variable ZFS_MOUNT_HELPER is set, we fall back to the
310  * previous behavior:
311  * The filesystem is mounted by invoking the system mount utility rather
312  * than by the system call mount(2).  This ensures that the /etc/mtab
313  * file is correctly locked for the update.  Performing our own locking
314  * and /etc/mtab update requires making an unsafe assumption about how
315  * the mount utility performs its locking.  Unfortunately, this also means
316  * in the case of a mount failure we do not have the exact errno.  We must
317  * make due with return value from the mount process.
318  */
319 int
do_mount(zfs_handle_t * zhp,const char * mntpt,const char * opts,int flags)320 do_mount(zfs_handle_t *zhp, const char *mntpt, const char *opts, int flags)
321 {
322 	const char *src = zfs_get_name(zhp);
323 	int error = 0;
324 
325 	if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
326 		char badopt[MNT_LINE_MAX] = {0};
327 		unsigned long mntflags = flags, zfsflags = 0;
328 		char myopts[MNT_LINE_MAX] = {0};
329 
330 		if (zfs_parse_mount_options(opts, &mntflags,
331 		    &zfsflags, 0, badopt, NULL)) {
332 			return (EINVAL);
333 		}
334 		strlcat(myopts, opts, MNT_LINE_MAX);
335 		zfs_adjust_mount_options(zhp, mntpt, myopts, NULL);
336 		if (mount(src, mntpt, MNTTYPE_ZFS, mntflags, myopts)) {
337 			return (errno);
338 		}
339 	} else {
340 		char *argv[9] = {
341 		    (char *)"/bin/mount",
342 		    (char *)"--no-canonicalize",
343 		    (char *)"-t", (char *)MNTTYPE_ZFS,
344 		    (char *)"-o", (char *)opts,
345 		    (char *)src,
346 		    (char *)mntpt,
347 		    (char *)NULL };
348 
349 		/* Return only the most critical mount error */
350 		error = libzfs_run_process(argv[0], argv,
351 		    STDOUT_VERBOSE|STDERR_VERBOSE);
352 		if (error) {
353 			if (error & MOUNT_FILEIO) {
354 				error = EIO;
355 			} else if (error & MOUNT_USER) {
356 				error = EINTR;
357 			} else if (error & MOUNT_SOFTWARE) {
358 				error = EPIPE;
359 			} else if (error & MOUNT_BUSY) {
360 				error = EBUSY;
361 			} else if (error & MOUNT_SYSERR) {
362 				error = EAGAIN;
363 			} else if (error & MOUNT_USAGE) {
364 				error = EINVAL;
365 			} else
366 				error = ENXIO; /* Generic error */
367 		}
368 	}
369 
370 	return (error);
371 }
372 
373 int
do_unmount(zfs_handle_t * zhp,const char * mntpt,int flags)374 do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags)
375 {
376 	(void) zhp;
377 
378 	/*
379 	 * MS_CRYPT and MS_OVERLAY are libzfs-internal flags; strip them so they
380 	 * are never passed to umount2(2), which rejects unknown flag bits with
381 	 * EINVAL. MS_FORCE/MS_DETACH are real umount2(2) flags and kept.
382 	 */
383 	flags &= ~(MS_CRYPT | MS_OVERLAY);
384 
385 	if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
386 		int rv = umount2(mntpt, flags);
387 
388 		return (rv < 0 ? errno : 0);
389 	}
390 
391 	char *argv[7] = {
392 	    (char *)"/bin/umount",
393 	    (char *)"-t", (char *)MNTTYPE_ZFS,
394 	    NULL, NULL, NULL, NULL };
395 	int rc, count = 3;
396 
397 	if (flags & MS_FORCE)
398 		argv[count++] = (char *)"-f";
399 
400 	if (flags & MS_DETACH)
401 		argv[count++] = (char *)"-l";
402 
403 	argv[count] = (char *)mntpt;
404 	rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
405 
406 	if (rc == 0)
407 		return (0);
408 	if (rc > 0 && geteuid() != 0)
409 		return (EPERM);
410 	return (EINVAL);
411 }
412 
413 #ifdef HAVE_MOUNT_SETATTR
414 /*
415  * Build a struct mount_attr for the changed namespace properties.
416  * Parallel to zfs_add_options() but produces mount_setattr(2) input
417  * instead of a mount options string.
418  */
419 static void
zfs_add_options_setattr(zfs_handle_t * zhp,struct mount_attr * attr,uint32_t nspflags)420 zfs_add_options_setattr(zfs_handle_t *zhp, struct mount_attr *attr,
421     uint32_t nspflags)
422 {
423 	const char *source;
424 
425 	if (nspflags & ZFS_MNT_PROP_READONLY) {
426 		if (getprop_uint64(zhp, ZFS_PROP_READONLY, &source))
427 			attr->attr_set |= MOUNT_ATTR_RDONLY;
428 		else
429 			attr->attr_clr |= MOUNT_ATTR_RDONLY;
430 	}
431 	if (nspflags & ZFS_MNT_PROP_EXEC) {
432 		if (getprop_uint64(zhp, ZFS_PROP_EXEC, &source))
433 			attr->attr_clr |= MOUNT_ATTR_NOEXEC;
434 		else
435 			attr->attr_set |= MOUNT_ATTR_NOEXEC;
436 	}
437 	if (nspflags & ZFS_MNT_PROP_SETUID) {
438 		if (getprop_uint64(zhp, ZFS_PROP_SETUID, &source))
439 			attr->attr_clr |= MOUNT_ATTR_NOSUID;
440 		else
441 			attr->attr_set |= MOUNT_ATTR_NOSUID;
442 	}
443 	if (nspflags & ZFS_MNT_PROP_DEVICES) {
444 		if (getprop_uint64(zhp, ZFS_PROP_DEVICES, &source))
445 			attr->attr_clr |= MOUNT_ATTR_NODEV;
446 		else
447 			attr->attr_set |= MOUNT_ATTR_NODEV;
448 	}
449 	if (nspflags & (ZFS_MNT_PROP_ATIME | ZFS_MNT_PROP_RELATIME)) {
450 		uint64_t atime = getprop_uint64(zhp, ZFS_PROP_ATIME, &source);
451 		uint64_t relatime = getprop_uint64(zhp,
452 		    ZFS_PROP_RELATIME, &source);
453 
454 		attr->attr_clr |= MOUNT_ATTR__ATIME;
455 		if (!atime)
456 			attr->attr_set |= MOUNT_ATTR_NOATIME;
457 		else if (relatime)
458 			attr->attr_set |= MOUNT_ATTR_RELATIME;
459 		else
460 			attr->attr_set |= MOUNT_ATTR_STRICTATIME;
461 	}
462 }
463 #endif /* HAVE_MOUNT_SETATTR */
464 
465 /*
466  * Selectively update per-mount VFS flags for the changed namespace
467  * properties using mount_setattr(2).  Unlike a full remount via mount(2),
468  * this only modifies the specified flags without resetting others --
469  * avoiding clobbering temporary mount flags set by the administrator.
470  *
471  * For non-legacy datasets, the single known mountpoint is used.
472  * For legacy datasets, /proc/mounts is iterated since legacy datasets
473  * can be mounted at multiple locations.
474  *
475  * Falls back to a full remount via zfs_mount() when mount_setattr(2)
476  * is not available (ENOSYS), except for legacy mounts where a full
477  * remount would clobber temporary flags.
478  */
479 int
zfs_mount_setattr(zfs_handle_t * zhp,uint32_t nspflags)480 zfs_mount_setattr(zfs_handle_t *zhp, uint32_t nspflags)
481 {
482 #ifdef HAVE_MOUNT_SETATTR
483 	struct mount_attr attr = { 0 };
484 	char mntpt_prop[ZFS_MAXPROPLEN];
485 	boolean_t legacy = B_FALSE;
486 	libzfs_handle_t *hdl = zhp->zfs_hdl;
487 	FILE *mnttab;
488 	struct mnttab entry;
489 	int ret = 0;
490 
491 	if (!zfs_is_mountable_internal(zhp))
492 		return (0);
493 
494 	zfs_add_options_setattr(zhp, &attr, nspflags);
495 	if (attr.attr_set == 0 && attr.attr_clr == 0)
496 		return (0);
497 
498 	if (zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type,
499 	    B_FALSE)) {
500 		verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mntpt_prop,
501 		    sizeof (mntpt_prop), NULL, NULL, 0, B_FALSE) == 0);
502 		legacy = (strcmp(mntpt_prop, ZFS_MOUNTPOINT_LEGACY) == 0);
503 	}
504 
505 	if (!legacy) {
506 		char *mntpt = NULL;
507 
508 		if (!zfs_is_mounted(zhp, &mntpt))
509 			return (0);
510 
511 		ret = mount_setattr(AT_FDCWD, mntpt, 0,
512 		    &attr, sizeof (attr));
513 		free(mntpt);
514 		if (ret != 0) {
515 			if (errno == ENOSYS)
516 				return (zfs_mount(zhp, MNTOPT_REMOUNT, 0));
517 			return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
518 			    dgettext(TEXT_DOMAIN, "cannot set mount "
519 			    "attributes for '%s'"), zhp->zfs_name));
520 		}
521 		return (0);
522 	}
523 
524 	/* Legacy: iterate /proc/mounts for all mountpoints. */
525 	if ((mnttab = fopen(MNTTAB, "re")) == NULL)
526 		return (0);
527 
528 	while (getmntent(mnttab, &entry) == 0) {
529 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
530 			continue;
531 		if (strcmp(entry.mnt_special, zhp->zfs_name) != 0)
532 			continue;
533 
534 		ret = mount_setattr(AT_FDCWD, entry.mnt_mountp, 0,
535 		    &attr, sizeof (attr));
536 		if (ret != 0) {
537 			if (errno == ENOSYS) {
538 				ret = 0;
539 				break;
540 			}
541 			ret = zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
542 			    dgettext(TEXT_DOMAIN, "cannot set mount "
543 			    "attributes for '%s'"), zhp->zfs_name);
544 			break;
545 		}
546 	}
547 
548 	(void) fclose(mnttab);
549 	return (ret);
550 #else
551 	(void) nspflags;
552 	return (zfs_mount(zhp, MNTOPT_REMOUNT, 0));
553 #endif
554 }
555 
556 int
zfs_mount_delegation_check(void)557 zfs_mount_delegation_check(void)
558 {
559 	return ((geteuid() != 0) ? EACCES : 0);
560 }
561 
562 /* Called from the tail end of zpool_disable_datasets() */
563 void
zpool_disable_datasets_os(zpool_handle_t * zhp,boolean_t force)564 zpool_disable_datasets_os(zpool_handle_t *zhp, boolean_t force)
565 {
566 	(void) zhp, (void) force;
567 }
568 
569 /* Called from the tail end of zfs_unmount() */
570 void
zpool_disable_volume_os(const char * name)571 zpool_disable_volume_os(const char *name)
572 {
573 	(void) name;
574 }
575