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/*
23 * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright (c) 2014, 2021 by Delphix. All rights reserved.
26 * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
27 * Copyright 2017 RackTop Systems.
28 * Copyright (c) 2018 Datto Inc.
29 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
30 */
31
32#include <dirent.h>
33#include <dlfcn.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <libgen.h>
37#include <libintl.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <strings.h>
41#include <unistd.h>
42#include <zone.h>
43#include <sys/mntent.h>
44#include <sys/mount.h>
45#include <sys/stat.h>
46#include <sys/vfs.h>
47#include <sys/dsl_crypt.h>
48#include <libzfs.h>
49
50#include "libzfs_impl.h"
51#include <thread_pool.h>
52
53#define	ZS_COMMENT	0x00000000	/* comment */
54#define	ZS_ZFSUTIL	0x00000001	/* caller is zfs(8) */
55
56typedef struct option_map {
57	const char *name;
58	unsigned long mntmask;
59	unsigned long zfsmask;
60} option_map_t;
61
62static const option_map_t option_map[] = {
63	/* Canonicalized filesystem independent options from mount(8) */
64	{ MNTOPT_NOAUTO,	MS_COMMENT,	ZS_COMMENT	},
65	{ MNTOPT_DEFAULTS,	MS_COMMENT,	ZS_COMMENT	},
66	{ MNTOPT_NODEVICES,	MS_NODEV,	ZS_COMMENT	},
67	{ MNTOPT_DEVICES,	MS_COMMENT,	ZS_COMMENT	},
68	{ MNTOPT_DIRSYNC,	MS_DIRSYNC,	ZS_COMMENT	},
69	{ MNTOPT_NOEXEC,	MS_NOEXEC,	ZS_COMMENT	},
70	{ MNTOPT_EXEC,		MS_COMMENT,	ZS_COMMENT	},
71	{ MNTOPT_GROUP,		MS_GROUP,	ZS_COMMENT	},
72	{ MNTOPT_NETDEV,	MS_COMMENT,	ZS_COMMENT	},
73	{ MNTOPT_NOFAIL,	MS_COMMENT,	ZS_COMMENT	},
74	{ MNTOPT_NOSUID,	MS_NOSUID,	ZS_COMMENT	},
75	{ MNTOPT_SUID,		MS_COMMENT,	ZS_COMMENT	},
76	{ MNTOPT_OWNER,		MS_OWNER,	ZS_COMMENT	},
77	{ MNTOPT_REMOUNT,	MS_REMOUNT,	ZS_COMMENT	},
78	{ MNTOPT_RO,		MS_RDONLY,	ZS_COMMENT	},
79	{ MNTOPT_RW,		MS_COMMENT,	ZS_COMMENT	},
80	{ MNTOPT_SYNC,		MS_SYNCHRONOUS,	ZS_COMMENT	},
81	{ MNTOPT_USER,		MS_USERS,	ZS_COMMENT	},
82	{ MNTOPT_USERS,		MS_USERS,	ZS_COMMENT	},
83	/* acl flags passed with util-linux-2.24 mount command */
84	{ MNTOPT_ACL,		MS_POSIXACL,	ZS_COMMENT	},
85	{ MNTOPT_NOACL,		MS_COMMENT,	ZS_COMMENT	},
86	{ MNTOPT_POSIXACL,	MS_POSIXACL,	ZS_COMMENT	},
87#ifdef MS_NOATIME
88	{ MNTOPT_NOATIME,	MS_NOATIME,	ZS_COMMENT	},
89	{ MNTOPT_ATIME,		MS_COMMENT,	ZS_COMMENT	},
90#endif
91#ifdef MS_NODIRATIME
92	{ MNTOPT_NODIRATIME,	MS_NODIRATIME,	ZS_COMMENT	},
93	{ MNTOPT_DIRATIME,	MS_COMMENT,	ZS_COMMENT	},
94#endif
95#ifdef MS_RELATIME
96	{ MNTOPT_RELATIME,	MS_RELATIME,	ZS_COMMENT	},
97	{ MNTOPT_NORELATIME,	MS_COMMENT,	ZS_COMMENT	},
98#endif
99#ifdef MS_STRICTATIME
100	{ MNTOPT_STRICTATIME,	MS_STRICTATIME,	ZS_COMMENT	},
101	{ MNTOPT_NOSTRICTATIME,	MS_COMMENT,	ZS_COMMENT	},
102#endif
103#ifdef MS_LAZYTIME
104	{ MNTOPT_LAZYTIME,	MS_LAZYTIME,	ZS_COMMENT	},
105#endif
106	{ MNTOPT_CONTEXT,	MS_COMMENT,	ZS_COMMENT	},
107	{ MNTOPT_FSCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
108	{ MNTOPT_DEFCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
109	{ MNTOPT_ROOTCONTEXT,	MS_COMMENT,	ZS_COMMENT	},
110#ifdef MS_I_VERSION
111	{ MNTOPT_IVERSION,	MS_I_VERSION,	ZS_COMMENT	},
112#endif
113#ifdef MS_MANDLOCK
114	{ MNTOPT_NBMAND,	MS_MANDLOCK,	ZS_COMMENT	},
115	{ MNTOPT_NONBMAND,	MS_COMMENT,	ZS_COMMENT	},
116#endif
117	/* Valid options not found in mount(8) */
118	{ MNTOPT_BIND,		MS_BIND,	ZS_COMMENT	},
119#ifdef MS_REC
120	{ MNTOPT_RBIND,		MS_BIND|MS_REC,	ZS_COMMENT	},
121#endif
122	{ MNTOPT_COMMENT,	MS_COMMENT,	ZS_COMMENT	},
123#ifdef MS_NOSUB
124	{ MNTOPT_NOSUB,		MS_NOSUB,	ZS_COMMENT	},
125#endif
126#ifdef MS_SILENT
127	{ MNTOPT_QUIET,		MS_SILENT,	ZS_COMMENT	},
128#endif
129	/* Custom zfs options */
130	{ MNTOPT_XATTR,		MS_COMMENT,	ZS_COMMENT	},
131	{ MNTOPT_NOXATTR,	MS_COMMENT,	ZS_COMMENT	},
132	{ MNTOPT_ZFSUTIL,	MS_COMMENT,	ZS_ZFSUTIL	},
133	{ NULL,			0,		0		} };
134
135/*
136 * Break the mount option in to a name/value pair.  The name is
137 * validated against the option map and mount flags set accordingly.
138 */
139static int
140parse_option(char *mntopt, unsigned long *mntflags,
141    unsigned long *zfsflags, int sloppy)
142{
143	const option_map_t *opt;
144	char *ptr, *name, *value = NULL;
145	int error = 0;
146
147	name = strdup(mntopt);
148	if (name == NULL)
149		return (ENOMEM);
150
151	for (ptr = name; ptr && *ptr; ptr++) {
152		if (*ptr == '=') {
153			*ptr = '\0';
154			value = ptr+1;
155			VERIFY3P(value, !=, NULL);
156			break;
157		}
158	}
159
160	for (opt = option_map; opt->name != NULL; opt++) {
161		if (strncmp(name, opt->name, strlen(name)) == 0) {
162			*mntflags |= opt->mntmask;
163			*zfsflags |= opt->zfsmask;
164			error = 0;
165			goto out;
166		}
167	}
168
169	if (!sloppy)
170		error = ENOENT;
171out:
172	/* If required further process on the value may be done here */
173	free(name);
174	return (error);
175}
176
177/*
178 * Translate the mount option string in to MS_* mount flags for the
179 * kernel vfs.  When sloppy is non-zero unknown options will be ignored
180 * otherwise they are considered fatal are copied in to badopt.
181 */
182int
183zfs_parse_mount_options(char *mntopts, unsigned long *mntflags,
184    unsigned long *zfsflags, int sloppy, char *badopt, char *mtabopt)
185{
186	int error = 0, quote = 0, flag = 0, count = 0;
187	char *ptr, *opt, *opts;
188
189	opts = strdup(mntopts);
190	if (opts == NULL)
191		return (ENOMEM);
192
193	*mntflags = 0;
194	opt = NULL;
195
196	/*
197	 * Scan through all mount options which must be comma delimited.
198	 * We must be careful to notice regions which are double quoted
199	 * and skip commas in these regions.  Each option is then checked
200	 * to determine if it is a known option.
201	 */
202	for (ptr = opts; ptr && !flag; ptr++) {
203		if (opt == NULL)
204			opt = ptr;
205
206		if (*ptr == '"')
207			quote = !quote;
208
209		if (quote)
210			continue;
211
212		if (*ptr == '\0')
213			flag = 1;
214
215		if ((*ptr == ',') || (*ptr == '\0')) {
216			*ptr = '\0';
217
218			error = parse_option(opt, mntflags, zfsflags, sloppy);
219			if (error) {
220				strcpy(badopt, opt);
221				goto out;
222
223			}
224
225			if (!(*mntflags & MS_REMOUNT) &&
226			    !(*zfsflags & ZS_ZFSUTIL) &&
227			    mtabopt != NULL) {
228				if (count > 0)
229					strlcat(mtabopt, ",", MNT_LINE_MAX);
230
231				strlcat(mtabopt, opt, MNT_LINE_MAX);
232				count++;
233			}
234
235			opt = NULL;
236		}
237	}
238
239out:
240	free(opts);
241	return (error);
242}
243
244static void
245append_mntopt(const char *name, const char *val, char *mntopts,
246    char *mtabopt, boolean_t quote)
247{
248	char tmp[MNT_LINE_MAX];
249
250	snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val);
251
252	if (mntopts)
253		strlcat(mntopts, tmp, MNT_LINE_MAX);
254
255	if (mtabopt)
256		strlcat(mtabopt, tmp, MNT_LINE_MAX);
257}
258
259static void
260zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name,
261    char *mntopts, char *mtabopt)
262{
263	char context[ZFS_MAXPROPLEN];
264
265	if (zfs_prop_get(zhp, zpt, context, sizeof (context),
266	    NULL, NULL, 0, B_FALSE) == 0) {
267		if (strcmp(context, "none") != 0)
268			append_mntopt(name, context, mntopts, mtabopt, B_TRUE);
269	}
270}
271
272void
273zfs_adjust_mount_options(zfs_handle_t *zhp, const char *mntpoint,
274    char *mntopts, char *mtabopt)
275{
276	char prop[ZFS_MAXPROPLEN];
277
278	/*
279	 * Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists
280	 * if it does, create a tmp variable in case it's needed
281	 * checks to see if the selinux context is set to the default
282	 * if it is, allow the setting of the other context properties
283	 * this is needed because the 'context' property overrides others
284	 * if it is not the default, set the 'context' property
285	 */
286	if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop),
287	    NULL, NULL, 0, B_FALSE) == 0) {
288		if (strcmp(prop, "none") == 0) {
289			zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT,
290			    MNTOPT_FSCONTEXT, mntopts, mtabopt);
291			zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT,
292			    MNTOPT_DEFCONTEXT, mntopts, mtabopt);
293			zfs_selinux_setcontext(zhp,
294			    ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
295			    mntopts, mtabopt);
296		} else {
297			append_mntopt(MNTOPT_CONTEXT, prop,
298			    mntopts, mtabopt, B_TRUE);
299		}
300	}
301
302	/* A hint used to determine an auto-mounted snapshot mount point */
303	append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
304}
305
306/*
307 * By default the filesystem by preparing the mount options (i.e. parsing
308 * some flags from the "opts" parameter into the "flags" parameter) and then
309 * directly calling the system call mount(2). We don't need the mount utility
310 * or update /etc/mtab, because this is a symlink on all modern systems.
311 *
312 * If the environment variable ZFS_MOUNT_HELPER is set, we fall back to the
313 * previous behavior:
314 * The filesystem is mounted by invoking the system mount utility rather
315 * than by the system call mount(2).  This ensures that the /etc/mtab
316 * file is correctly locked for the update.  Performing our own locking
317 * and /etc/mtab update requires making an unsafe assumption about how
318 * the mount utility performs its locking.  Unfortunately, this also means
319 * in the case of a mount failure we do not have the exact errno.  We must
320 * make due with return value from the mount process.
321 */
322int
323do_mount(zfs_handle_t *zhp, const char *mntpt, char *opts, int flags)
324{
325	const char *src = zfs_get_name(zhp);
326	int error = 0;
327
328	if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
329		char badopt[MNT_LINE_MAX] = {0};
330		unsigned long mntflags = flags, zfsflags;
331		char myopts[MNT_LINE_MAX] = {0};
332
333		if (zfs_parse_mount_options(opts, &mntflags,
334		    &zfsflags, 0, badopt, NULL)) {
335			return (EINVAL);
336		}
337		strlcat(myopts, opts, MNT_LINE_MAX);
338		zfs_adjust_mount_options(zhp, mntpt, myopts, NULL);
339		if (mount(src, mntpt, MNTTYPE_ZFS, mntflags, myopts)) {
340			return (errno);
341		}
342	} else {
343		char *argv[9] = {
344		    "/bin/mount",
345		    "--no-canonicalize",
346		    "-t", MNTTYPE_ZFS,
347		    "-o", opts,
348		    (char *)src,
349		    (char *)mntpt,
350		    (char *)NULL };
351
352		/* Return only the most critical mount error */
353		error = libzfs_run_process(argv[0], argv,
354		    STDOUT_VERBOSE|STDERR_VERBOSE);
355		if (error) {
356			if (error & MOUNT_FILEIO) {
357				error = EIO;
358			} else if (error & MOUNT_USER) {
359				error = EINTR;
360			} else if (error & MOUNT_SOFTWARE) {
361				error = EPIPE;
362			} else if (error & MOUNT_BUSY) {
363				error = EBUSY;
364			} else if (error & MOUNT_SYSERR) {
365				error = EAGAIN;
366			} else if (error & MOUNT_USAGE) {
367				error = EINVAL;
368			} else
369				error = ENXIO; /* Generic error */
370		}
371	}
372
373	return (error);
374}
375
376int
377do_unmount(const char *mntpt, int flags)
378{
379	if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
380		int rv = umount2(mntpt, flags);
381
382		return (rv < 0 ? errno : 0);
383	}
384
385	char force_opt[] = "-f";
386	char lazy_opt[] = "-l";
387	char *argv[7] = {
388	    "/bin/umount",
389	    "-t", MNTTYPE_ZFS,
390	    NULL, NULL, NULL, NULL };
391	int rc, count = 3;
392
393	if (flags & MS_FORCE) {
394		argv[count] = force_opt;
395		count++;
396	}
397
398	if (flags & MS_DETACH) {
399		argv[count] = lazy_opt;
400		count++;
401	}
402
403	argv[count] = (char *)mntpt;
404	rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
405
406	return (rc ? EINVAL : 0);
407}
408
409int
410zfs_mount_delegation_check(void)
411{
412	return ((geteuid() != 0) ? EACCES : 0);
413}
414