]>
Commit | Line | Data |
---|---|---|
73cdcc63 MM |
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. | |
c15d36c6 | 25 | * Copyright (c) 2014, 2020 by Delphix. All rights reserved. |
73cdcc63 MM |
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 | ||
501a1511 FD |
53 | #define ZS_COMMENT 0x00000000 /* comment */ |
54 | #define ZS_ZFSUTIL 0x00000001 /* caller is zfs(8) */ | |
55 | ||
56 | typedef struct option_map { | |
57 | const char *name; | |
58 | unsigned long mntmask; | |
59 | unsigned long zfsmask; | |
60 | } option_map_t; | |
61 | ||
62 | static 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 | ||
73cdcc63 | 135 | /* |
501a1511 FD |
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 | */ | |
139 | static int | |
140 | parse_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; | |
171 | out: | |
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 | */ | |
182 | int | |
183 | zfs_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 | ||
239 | out: | |
240 | free(opts); | |
241 | return (error); | |
242 | } | |
243 | ||
244 | static void | |
245 | append_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 | ||
259 | static void | |
260 | zfs_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 | ||
272 | void | |
273 | zfs_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: | |
73cdcc63 MM |
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. | |
73cdcc63 MM |
321 | */ |
322 | int | |
501a1511 | 323 | do_mount(zfs_handle_t *zhp, const char *mntpt, char *opts, int flags) |
73cdcc63 | 324 | { |
501a1511 FD |
325 | const char *src = zfs_get_name(zhp); |
326 | int error = 0; | |
73cdcc63 | 327 | |
501a1511 FD |
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}; | |
73cdcc63 | 332 | |
501a1511 FD |
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); | |
217f4837 RM |
339 | if (mount(src, mntpt, MNTTYPE_ZFS, mntflags, myopts)) { |
340 | return (errno); | |
341 | } | |
501a1511 FD |
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 | } | |
73cdcc63 MM |
371 | } |
372 | ||
501a1511 | 373 | return (error); |
73cdcc63 MM |
374 | } |
375 | ||
376 | int | |
377 | do_unmount(const char *mntpt, int flags) | |
378 | { | |
501a1511 FD |
379 | if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) { |
380 | return (umount2(mntpt, flags)); | |
381 | } | |
382 | ||
73cdcc63 MM |
383 | char force_opt[] = "-f"; |
384 | char lazy_opt[] = "-l"; | |
385 | char *argv[7] = { | |
386 | "/bin/umount", | |
387 | "-t", MNTTYPE_ZFS, | |
388 | NULL, NULL, NULL, NULL }; | |
389 | int rc, count = 3; | |
390 | ||
391 | if (flags & MS_FORCE) { | |
392 | argv[count] = force_opt; | |
393 | count++; | |
394 | } | |
395 | ||
396 | if (flags & MS_DETACH) { | |
397 | argv[count] = lazy_opt; | |
398 | count++; | |
399 | } | |
400 | ||
401 | argv[count] = (char *)mntpt; | |
402 | rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE); | |
403 | ||
404 | return (rc ? EINVAL : 0); | |
405 | } | |
406 | ||
407 | int | |
4bc72196 | 408 | zfs_mount_delegation_check(void) |
73cdcc63 | 409 | { |
4bc72196 | 410 | return ((geteuid() != 0) ? EACCES : 0); |
73cdcc63 | 411 | } |