]>
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 | |
1d3ba0bf | 9 | * or https://opensource.org/licenses/CDDL-1.0. |
73cdcc63 MM |
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. | |
f5ada653 | 25 | * Copyright (c) 2014, 2021 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> | |
d465fc58 | 40 | #include <string.h> |
73cdcc63 MM |
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 | ||
eefaa55f | 50 | #include "../../libzfs_impl.h" |
73cdcc63 MM |
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 }, | |
f371cc18 AH |
87 | /* |
88 | * Case sensitive options are just listed here to silently | |
89 | * ignore the error if passed with zfs mount command. | |
90 | */ | |
91 | { MNTOPT_CASESENSITIVE, MS_COMMENT, ZS_COMMENT }, | |
92 | { MNTOPT_CASEINSENSITIVE, MS_COMMENT, ZS_COMMENT }, | |
93 | { MNTOPT_CASEMIXED, MS_COMMENT, ZS_COMMENT }, | |
501a1511 FD |
94 | #ifdef MS_NOATIME |
95 | { MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT }, | |
96 | { MNTOPT_ATIME, MS_COMMENT, ZS_COMMENT }, | |
97 | #endif | |
98 | #ifdef MS_NODIRATIME | |
99 | { MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT }, | |
100 | { MNTOPT_DIRATIME, MS_COMMENT, ZS_COMMENT }, | |
101 | #endif | |
102 | #ifdef MS_RELATIME | |
103 | { MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT }, | |
104 | { MNTOPT_NORELATIME, MS_COMMENT, ZS_COMMENT }, | |
105 | #endif | |
106 | #ifdef MS_STRICTATIME | |
107 | { MNTOPT_STRICTATIME, MS_STRICTATIME, ZS_COMMENT }, | |
108 | { MNTOPT_NOSTRICTATIME, MS_COMMENT, ZS_COMMENT }, | |
109 | #endif | |
110 | #ifdef MS_LAZYTIME | |
111 | { MNTOPT_LAZYTIME, MS_LAZYTIME, ZS_COMMENT }, | |
112 | #endif | |
113 | { MNTOPT_CONTEXT, MS_COMMENT, ZS_COMMENT }, | |
114 | { MNTOPT_FSCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
115 | { MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
116 | { MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
117 | #ifdef MS_I_VERSION | |
118 | { MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT }, | |
119 | #endif | |
120 | #ifdef MS_MANDLOCK | |
121 | { MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT }, | |
122 | { MNTOPT_NONBMAND, MS_COMMENT, ZS_COMMENT }, | |
123 | #endif | |
124 | /* Valid options not found in mount(8) */ | |
125 | { MNTOPT_BIND, MS_BIND, ZS_COMMENT }, | |
126 | #ifdef MS_REC | |
127 | { MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT }, | |
128 | #endif | |
129 | { MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT }, | |
130 | #ifdef MS_NOSUB | |
131 | { MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT }, | |
132 | #endif | |
133 | #ifdef MS_SILENT | |
134 | { MNTOPT_QUIET, MS_SILENT, ZS_COMMENT }, | |
135 | #endif | |
136 | /* Custom zfs options */ | |
137 | { MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT }, | |
138 | { MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT }, | |
139 | { MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL }, | |
140 | { NULL, 0, 0 } }; | |
141 | ||
73cdcc63 | 142 | /* |
501a1511 FD |
143 | * Break the mount option in to a name/value pair. The name is |
144 | * validated against the option map and mount flags set accordingly. | |
145 | */ | |
146 | static int | |
147 | parse_option(char *mntopt, unsigned long *mntflags, | |
148 | unsigned long *zfsflags, int sloppy) | |
149 | { | |
150 | const option_map_t *opt; | |
151 | char *ptr, *name, *value = NULL; | |
152 | int error = 0; | |
153 | ||
154 | name = strdup(mntopt); | |
155 | if (name == NULL) | |
156 | return (ENOMEM); | |
157 | ||
158 | for (ptr = name; ptr && *ptr; ptr++) { | |
159 | if (*ptr == '=') { | |
160 | *ptr = '\0'; | |
161 | value = ptr+1; | |
162 | VERIFY3P(value, !=, NULL); | |
163 | break; | |
164 | } | |
165 | } | |
166 | ||
167 | for (opt = option_map; opt->name != NULL; opt++) { | |
168 | if (strncmp(name, opt->name, strlen(name)) == 0) { | |
169 | *mntflags |= opt->mntmask; | |
170 | *zfsflags |= opt->zfsmask; | |
171 | error = 0; | |
172 | goto out; | |
173 | } | |
174 | } | |
175 | ||
176 | if (!sloppy) | |
177 | error = ENOENT; | |
178 | out: | |
179 | /* If required further process on the value may be done here */ | |
180 | free(name); | |
181 | return (error); | |
182 | } | |
183 | ||
184 | /* | |
185 | * Translate the mount option string in to MS_* mount flags for the | |
186 | * kernel vfs. When sloppy is non-zero unknown options will be ignored | |
187 | * otherwise they are considered fatal are copied in to badopt. | |
188 | */ | |
189 | int | |
a926aab9 | 190 | zfs_parse_mount_options(const char *mntopts, unsigned long *mntflags, |
501a1511 FD |
191 | unsigned long *zfsflags, int sloppy, char *badopt, char *mtabopt) |
192 | { | |
193 | int error = 0, quote = 0, flag = 0, count = 0; | |
194 | char *ptr, *opt, *opts; | |
195 | ||
196 | opts = strdup(mntopts); | |
197 | if (opts == NULL) | |
198 | return (ENOMEM); | |
199 | ||
200 | *mntflags = 0; | |
201 | opt = NULL; | |
202 | ||
203 | /* | |
204 | * Scan through all mount options which must be comma delimited. | |
205 | * We must be careful to notice regions which are double quoted | |
206 | * and skip commas in these regions. Each option is then checked | |
207 | * to determine if it is a known option. | |
208 | */ | |
209 | for (ptr = opts; ptr && !flag; ptr++) { | |
210 | if (opt == NULL) | |
211 | opt = ptr; | |
212 | ||
213 | if (*ptr == '"') | |
214 | quote = !quote; | |
215 | ||
216 | if (quote) | |
217 | continue; | |
218 | ||
219 | if (*ptr == '\0') | |
220 | flag = 1; | |
221 | ||
222 | if ((*ptr == ',') || (*ptr == '\0')) { | |
223 | *ptr = '\0'; | |
224 | ||
225 | error = parse_option(opt, mntflags, zfsflags, sloppy); | |
226 | if (error) { | |
227 | strcpy(badopt, opt); | |
228 | goto out; | |
229 | ||
230 | } | |
231 | ||
232 | if (!(*mntflags & MS_REMOUNT) && | |
233 | !(*zfsflags & ZS_ZFSUTIL) && | |
234 | mtabopt != NULL) { | |
235 | if (count > 0) | |
236 | strlcat(mtabopt, ",", MNT_LINE_MAX); | |
237 | ||
238 | strlcat(mtabopt, opt, MNT_LINE_MAX); | |
239 | count++; | |
240 | } | |
241 | ||
242 | opt = NULL; | |
243 | } | |
244 | } | |
245 | ||
246 | out: | |
247 | free(opts); | |
248 | return (error); | |
249 | } | |
250 | ||
251 | static void | |
252 | append_mntopt(const char *name, const char *val, char *mntopts, | |
253 | char *mtabopt, boolean_t quote) | |
254 | { | |
255 | char tmp[MNT_LINE_MAX]; | |
256 | ||
257 | snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val); | |
258 | ||
259 | if (mntopts) | |
260 | strlcat(mntopts, tmp, MNT_LINE_MAX); | |
261 | ||
262 | if (mtabopt) | |
263 | strlcat(mtabopt, tmp, MNT_LINE_MAX); | |
264 | } | |
265 | ||
266 | static void | |
267 | zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name, | |
268 | char *mntopts, char *mtabopt) | |
269 | { | |
270 | char context[ZFS_MAXPROPLEN]; | |
271 | ||
272 | if (zfs_prop_get(zhp, zpt, context, sizeof (context), | |
273 | NULL, NULL, 0, B_FALSE) == 0) { | |
274 | if (strcmp(context, "none") != 0) | |
275 | append_mntopt(name, context, mntopts, mtabopt, B_TRUE); | |
276 | } | |
277 | } | |
278 | ||
279 | void | |
280 | zfs_adjust_mount_options(zfs_handle_t *zhp, const char *mntpoint, | |
281 | char *mntopts, char *mtabopt) | |
282 | { | |
283 | char prop[ZFS_MAXPROPLEN]; | |
284 | ||
285 | /* | |
286 | * Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists | |
287 | * if it does, create a tmp variable in case it's needed | |
288 | * checks to see if the selinux context is set to the default | |
289 | * if it is, allow the setting of the other context properties | |
290 | * this is needed because the 'context' property overrides others | |
291 | * if it is not the default, set the 'context' property | |
292 | */ | |
293 | if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop), | |
294 | NULL, NULL, 0, B_FALSE) == 0) { | |
295 | if (strcmp(prop, "none") == 0) { | |
296 | zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT, | |
297 | MNTOPT_FSCONTEXT, mntopts, mtabopt); | |
298 | zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT, | |
299 | MNTOPT_DEFCONTEXT, mntopts, mtabopt); | |
300 | zfs_selinux_setcontext(zhp, | |
301 | ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT, | |
302 | mntopts, mtabopt); | |
303 | } else { | |
304 | append_mntopt(MNTOPT_CONTEXT, prop, | |
305 | mntopts, mtabopt, B_TRUE); | |
306 | } | |
307 | } | |
308 | ||
309 | /* A hint used to determine an auto-mounted snapshot mount point */ | |
310 | append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE); | |
311 | } | |
312 | ||
313 | /* | |
314 | * By default the filesystem by preparing the mount options (i.e. parsing | |
315 | * some flags from the "opts" parameter into the "flags" parameter) and then | |
316 | * directly calling the system call mount(2). We don't need the mount utility | |
317 | * or update /etc/mtab, because this is a symlink on all modern systems. | |
318 | * | |
319 | * If the environment variable ZFS_MOUNT_HELPER is set, we fall back to the | |
320 | * previous behavior: | |
73cdcc63 MM |
321 | * The filesystem is mounted by invoking the system mount utility rather |
322 | * than by the system call mount(2). This ensures that the /etc/mtab | |
323 | * file is correctly locked for the update. Performing our own locking | |
324 | * and /etc/mtab update requires making an unsafe assumption about how | |
325 | * the mount utility performs its locking. Unfortunately, this also means | |
326 | * in the case of a mount failure we do not have the exact errno. We must | |
327 | * make due with return value from the mount process. | |
73cdcc63 MM |
328 | */ |
329 | int | |
a926aab9 | 330 | do_mount(zfs_handle_t *zhp, const char *mntpt, const char *opts, int flags) |
73cdcc63 | 331 | { |
501a1511 FD |
332 | const char *src = zfs_get_name(zhp); |
333 | int error = 0; | |
73cdcc63 | 334 | |
501a1511 FD |
335 | if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) { |
336 | char badopt[MNT_LINE_MAX] = {0}; | |
4737a9eb | 337 | unsigned long mntflags = flags, zfsflags = 0; |
501a1511 | 338 | char myopts[MNT_LINE_MAX] = {0}; |
73cdcc63 | 339 | |
501a1511 FD |
340 | if (zfs_parse_mount_options(opts, &mntflags, |
341 | &zfsflags, 0, badopt, NULL)) { | |
342 | return (EINVAL); | |
343 | } | |
344 | strlcat(myopts, opts, MNT_LINE_MAX); | |
345 | zfs_adjust_mount_options(zhp, mntpt, myopts, NULL); | |
217f4837 RM |
346 | if (mount(src, mntpt, MNTTYPE_ZFS, mntflags, myopts)) { |
347 | return (errno); | |
348 | } | |
501a1511 FD |
349 | } else { |
350 | char *argv[9] = { | |
a926aab9 AZ |
351 | (char *)"/bin/mount", |
352 | (char *)"--no-canonicalize", | |
353 | (char *)"-t", (char *)MNTTYPE_ZFS, | |
354 | (char *)"-o", (char *)opts, | |
501a1511 FD |
355 | (char *)src, |
356 | (char *)mntpt, | |
357 | (char *)NULL }; | |
358 | ||
359 | /* Return only the most critical mount error */ | |
360 | error = libzfs_run_process(argv[0], argv, | |
361 | STDOUT_VERBOSE|STDERR_VERBOSE); | |
362 | if (error) { | |
363 | if (error & MOUNT_FILEIO) { | |
364 | error = EIO; | |
365 | } else if (error & MOUNT_USER) { | |
366 | error = EINTR; | |
367 | } else if (error & MOUNT_SOFTWARE) { | |
368 | error = EPIPE; | |
369 | } else if (error & MOUNT_BUSY) { | |
370 | error = EBUSY; | |
371 | } else if (error & MOUNT_SYSERR) { | |
372 | error = EAGAIN; | |
373 | } else if (error & MOUNT_USAGE) { | |
374 | error = EINVAL; | |
375 | } else | |
376 | error = ENXIO; /* Generic error */ | |
377 | } | |
73cdcc63 MM |
378 | } |
379 | ||
501a1511 | 380 | return (error); |
73cdcc63 MM |
381 | } |
382 | ||
383 | int | |
41eba770 | 384 | do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags) |
73cdcc63 | 385 | { |
1f182103 AZ |
386 | (void) zhp; |
387 | ||
501a1511 | 388 | if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) { |
f5ada653 DB |
389 | int rv = umount2(mntpt, flags); |
390 | ||
391 | return (rv < 0 ? errno : 0); | |
501a1511 FD |
392 | } |
393 | ||
73cdcc63 | 394 | char *argv[7] = { |
a926aab9 AZ |
395 | (char *)"/bin/umount", |
396 | (char *)"-t", (char *)MNTTYPE_ZFS, | |
73cdcc63 MM |
397 | NULL, NULL, NULL, NULL }; |
398 | int rc, count = 3; | |
399 | ||
a926aab9 AZ |
400 | if (flags & MS_FORCE) |
401 | argv[count++] = (char *)"-f"; | |
73cdcc63 | 402 | |
a926aab9 AZ |
403 | if (flags & MS_DETACH) |
404 | argv[count++] = (char *)"-l"; | |
73cdcc63 MM |
405 | |
406 | argv[count] = (char *)mntpt; | |
407 | rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE); | |
408 | ||
409 | return (rc ? EINVAL : 0); | |
410 | } | |
411 | ||
412 | int | |
4bc72196 | 413 | zfs_mount_delegation_check(void) |
73cdcc63 | 414 | { |
4bc72196 | 415 | return ((geteuid() != 0) ? EACCES : 0); |
73cdcc63 | 416 | } |
3e8d5e4f JL |
417 | |
418 | /* Called from the tail end of zpool_disable_datasets() */ | |
419 | void | |
420 | zpool_disable_datasets_os(zpool_handle_t *zhp, boolean_t force) | |
421 | { | |
1f182103 | 422 | (void) zhp, (void) force; |
3e8d5e4f JL |
423 | } |
424 | ||
425 | /* Called from the tail end of zfs_unmount() */ | |
426 | void | |
427 | zpool_disable_volume_os(const char *name) | |
428 | { | |
1f182103 | 429 | (void) name; |
3e8d5e4f | 430 | } |