]>
Commit | Line | Data |
---|---|---|
34dc7c2f BB |
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 | /* | |
7ea4f88f | 23 | * Copyright 2015 Nexenta Systems, Inc. All rights reserved. |
428870ff | 24 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
f5ada653 | 25 | * Copyright (c) 2014, 2021 by Delphix. All rights reserved. |
23d70cde | 26 | * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> |
7e35ea78 | 27 | * Copyright 2017 RackTop Systems. |
50a343d8 | 28 | * Copyright (c) 2018 Datto Inc. |
e63ac16d | 29 | * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. |
34dc7c2f BB |
30 | */ |
31 | ||
34dc7c2f BB |
32 | /* |
33 | * Routines to manage ZFS mounts. We separate all the nasty routines that have | |
34 | * to deal with the OS. The following functions are the main entry points -- | |
35 | * they are used by mount and unmount and when changing a filesystem's | |
36 | * mountpoint. | |
37 | * | |
a10d50f9 SR |
38 | * zfs_is_mounted() |
39 | * zfs_mount() | |
68a192e4 | 40 | * zfs_mount_at() |
a10d50f9 SR |
41 | * zfs_unmount() |
42 | * zfs_unmountall() | |
34dc7c2f BB |
43 | * |
44 | * This file also contains the functions used to manage sharing filesystems via | |
45 | * NFS and iSCSI: | |
46 | * | |
a10d50f9 SR |
47 | * zfs_is_shared() |
48 | * zfs_share() | |
49 | * zfs_unshare() | |
34dc7c2f | 50 | * |
a10d50f9 SR |
51 | * zfs_is_shared_nfs() |
52 | * zfs_is_shared_smb() | |
53 | * zfs_share_proto() | |
54 | * zfs_shareall(); | |
55 | * zfs_unshare_nfs() | |
56 | * zfs_unshare_smb() | |
57 | * zfs_unshareall_nfs() | |
34dc7c2f BB |
58 | * zfs_unshareall_smb() |
59 | * zfs_unshareall() | |
60 | * zfs_unshareall_bypath() | |
34dc7c2f BB |
61 | * |
62 | * The following functions are available for pool consumers, and will | |
63 | * mount/unmount and share/unshare all datasets within pool: | |
64 | * | |
a10d50f9 SR |
65 | * zpool_enable_datasets() |
66 | * zpool_disable_datasets() | |
34dc7c2f BB |
67 | */ |
68 | ||
69 | #include <dirent.h> | |
70 | #include <dlfcn.h> | |
71 | #include <errno.h> | |
3cbe6b29 | 72 | #include <fcntl.h> |
34dc7c2f BB |
73 | #include <libgen.h> |
74 | #include <libintl.h> | |
75 | #include <stdio.h> | |
76 | #include <stdlib.h> | |
d465fc58 | 77 | #include <string.h> |
34dc7c2f BB |
78 | #include <unistd.h> |
79 | #include <zone.h> | |
80 | #include <sys/mntent.h> | |
34dc7c2f BB |
81 | #include <sys/mount.h> |
82 | #include <sys/stat.h> | |
774ee3c7 | 83 | #include <sys/vfs.h> |
b5256303 | 84 | #include <sys/dsl_crypt.h> |
34dc7c2f BB |
85 | |
86 | #include <libzfs.h> | |
87 | ||
88 | #include "libzfs_impl.h" | |
a10d50f9 | 89 | #include <thread_pool.h> |
34dc7c2f BB |
90 | |
91 | #include <libshare.h> | |
92 | #include <sys/systeminfo.h> | |
93 | #define MAXISALEN 257 /* based on sysinfo(2) man page */ | |
94 | ||
a10d50f9 SR |
95 | static int mount_tp_nthr = 512; /* tpool threads for multi-threaded mounting */ |
96 | ||
97 | static void zfs_mount_task(void *); | |
739cfb96 | 98 | static zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **, |
34dc7c2f BB |
99 | zfs_share_proto_t); |
100 | ||
34dc7c2f | 101 | /* |
7e35ea78 | 102 | * The share protocols table must be in the same order as the zfs_share_proto_t |
34dc7c2f BB |
103 | * enum in libzfs_impl.h |
104 | */ | |
0481eabf | 105 | static const proto_table_t proto_table[PROTO_END] = { |
34dc7c2f BB |
106 | {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED}, |
107 | {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED}, | |
108 | }; | |
109 | ||
0481eabf | 110 | static const zfs_share_proto_t nfs_only[] = { |
34dc7c2f BB |
111 | PROTO_NFS, |
112 | PROTO_END | |
113 | }; | |
114 | ||
0481eabf | 115 | static const zfs_share_proto_t smb_only[] = { |
34dc7c2f BB |
116 | PROTO_SMB, |
117 | PROTO_END | |
118 | }; | |
0481eabf | 119 | static const zfs_share_proto_t share_all_proto[] = { |
34dc7c2f BB |
120 | PROTO_NFS, |
121 | PROTO_SMB, | |
122 | PROTO_END | |
123 | }; | |
124 | ||
34dc7c2f | 125 | |
34dc7c2f | 126 | |
34dc7c2f | 127 | static boolean_t |
774ee3c7 GM |
128 | dir_is_empty_stat(const char *dirname) |
129 | { | |
130 | struct stat st; | |
131 | ||
132 | /* | |
133 | * We only want to return false if the given path is a non empty | |
134 | * directory, all other errors are handled elsewhere. | |
135 | */ | |
136 | if (stat(dirname, &st) < 0 || !S_ISDIR(st.st_mode)) { | |
137 | return (B_TRUE); | |
138 | } | |
139 | ||
140 | /* | |
141 | * An empty directory will still have two entries in it, one | |
142 | * entry for each of "." and "..". | |
143 | */ | |
144 | if (st.st_size > 2) { | |
145 | return (B_FALSE); | |
146 | } | |
147 | ||
148 | return (B_TRUE); | |
149 | } | |
150 | ||
151 | static boolean_t | |
152 | dir_is_empty_readdir(const char *dirname) | |
34dc7c2f BB |
153 | { |
154 | DIR *dirp; | |
155 | struct dirent64 *dp; | |
3cbe6b29 | 156 | int dirfd; |
34dc7c2f | 157 | |
3cbe6b29 GM |
158 | if ((dirfd = openat(AT_FDCWD, dirname, |
159 | O_RDONLY | O_NDELAY | O_LARGEFILE | O_CLOEXEC, 0)) < 0) { | |
34dc7c2f | 160 | return (B_TRUE); |
3cbe6b29 GM |
161 | } |
162 | ||
163 | if ((dirp = fdopendir(dirfd)) == NULL) { | |
aa6e82a6 | 164 | (void) close(dirfd); |
3cbe6b29 GM |
165 | return (B_TRUE); |
166 | } | |
34dc7c2f BB |
167 | |
168 | while ((dp = readdir64(dirp)) != NULL) { | |
169 | ||
170 | if (strcmp(dp->d_name, ".") == 0 || | |
171 | strcmp(dp->d_name, "..") == 0) | |
172 | continue; | |
173 | ||
174 | (void) closedir(dirp); | |
175 | return (B_FALSE); | |
176 | } | |
177 | ||
178 | (void) closedir(dirp); | |
179 | return (B_TRUE); | |
180 | } | |
181 | ||
774ee3c7 GM |
182 | /* |
183 | * Returns true if the specified directory is empty. If we can't open the | |
184 | * directory at all, return true so that the mount can fail with a more | |
185 | * informative error message. | |
186 | */ | |
187 | static boolean_t | |
188 | dir_is_empty(const char *dirname) | |
189 | { | |
190 | struct statfs64 st; | |
191 | ||
192 | /* | |
193 | * If the statvfs call fails or the filesystem is not a ZFS | |
194 | * filesystem, fall back to the slow path which uses readdir. | |
195 | */ | |
196 | if ((statfs64(dirname, &st) != 0) || | |
197 | (st.f_type != ZFS_SUPER_MAGIC)) { | |
198 | return (dir_is_empty_readdir(dirname)); | |
199 | } | |
200 | ||
201 | /* | |
202 | * At this point, we know the provided path is on a ZFS | |
203 | * filesystem, so we can use stat instead of readdir to | |
204 | * determine if the directory is empty or not. We try to avoid | |
205 | * using readdir because that requires opening "dirname"; this | |
206 | * open file descriptor can potentially end up in a child | |
207 | * process if there's a concurrent fork, thus preventing the | |
208 | * zfs_mount() from otherwise succeeding (the open file | |
209 | * descriptor inherited by the child process will cause the | |
210 | * parent's mount to fail with EBUSY). The performance | |
211 | * implications of replacing the open, read, and close with a | |
212 | * single stat is nice; but is not the main motivation for the | |
213 | * added complexity. | |
214 | */ | |
215 | return (dir_is_empty_stat(dirname)); | |
216 | } | |
217 | ||
34dc7c2f BB |
218 | /* |
219 | * Checks to see if the mount is active. If the filesystem is mounted, we fill | |
220 | * in 'where' with the current mountpoint, and return 1. Otherwise, we return | |
221 | * 0. | |
222 | */ | |
223 | boolean_t | |
224 | is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where) | |
225 | { | |
fb5f0bc8 | 226 | struct mnttab entry; |
34dc7c2f | 227 | |
fb5f0bc8 | 228 | if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0) |
34dc7c2f BB |
229 | return (B_FALSE); |
230 | ||
231 | if (where != NULL) | |
232 | *where = zfs_strdup(zfs_hdl, entry.mnt_mountp); | |
233 | ||
234 | return (B_TRUE); | |
235 | } | |
236 | ||
237 | boolean_t | |
238 | zfs_is_mounted(zfs_handle_t *zhp, char **where) | |
239 | { | |
240 | return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); | |
241 | } | |
242 | ||
68a192e4 KE |
243 | /* |
244 | * Checks any higher order concerns about whether the given dataset is | |
245 | * mountable, false otherwise. zfs_is_mountable_internal specifically assumes | |
246 | * that the caller has verified the sanity of mounting the dataset at | |
1f182103 | 247 | * its mountpoint to the extent the caller wants. |
68a192e4 KE |
248 | */ |
249 | static boolean_t | |
1f182103 | 250 | zfs_is_mountable_internal(zfs_handle_t *zhp) |
68a192e4 | 251 | { |
68a192e4 KE |
252 | if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && |
253 | getzoneid() == GLOBAL_ZONEID) | |
254 | return (B_FALSE); | |
255 | ||
256 | return (B_TRUE); | |
257 | } | |
258 | ||
34dc7c2f BB |
259 | /* |
260 | * Returns true if the given dataset is mountable, false otherwise. Returns the | |
261 | * mountpoint in 'buf'. | |
262 | */ | |
73cdcc63 | 263 | boolean_t |
34dc7c2f | 264 | zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, |
30af21b0 | 265 | zprop_source_t *source, int flags) |
34dc7c2f | 266 | { |
eca7b760 | 267 | char sourceloc[MAXNAMELEN]; |
610cb4fb | 268 | zprop_source_t sourcetype; |
34dc7c2f | 269 | |
962d5242 TC |
270 | if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type, |
271 | B_FALSE)) | |
34dc7c2f BB |
272 | return (B_FALSE); |
273 | ||
274 | verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, | |
275 | &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); | |
276 | ||
277 | if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || | |
278 | strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) | |
279 | return (B_FALSE); | |
280 | ||
281 | if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF) | |
282 | return (B_FALSE); | |
283 | ||
1f182103 | 284 | if (!zfs_is_mountable_internal(zhp)) |
34dc7c2f BB |
285 | return (B_FALSE); |
286 | ||
30af21b0 PD |
287 | if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) |
288 | return (B_FALSE); | |
289 | ||
34dc7c2f BB |
290 | if (source) |
291 | *source = sourcetype; | |
292 | ||
293 | return (B_TRUE); | |
294 | } | |
295 | ||
3fb1fcde BB |
296 | /* |
297 | * The filesystem is mounted by invoking the system mount utility rather | |
298 | * than by the system call mount(2). This ensures that the /etc/mtab | |
299 | * file is correctly locked for the update. Performing our own locking | |
300 | * and /etc/mtab update requires making an unsafe assumption about how | |
301 | * the mount utility performs its locking. Unfortunately, this also means | |
302 | * in the case of a mount failure we do not have the exact errno. We must | |
303 | * make due with return value from the mount process. | |
304 | * | |
305 | * In the long term a shared library called libmount is under development | |
306 | * which provides a common API to address the locking and errno issues. | |
307 | * Once the standard mount utility has been updated to use this library | |
308 | * we can add an autoconf check to conditionally use it. | |
309 | * | |
310 | * http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html | |
311 | */ | |
312 | ||
2cf7f52b BB |
313 | static int |
314 | zfs_add_option(zfs_handle_t *zhp, char *options, int len, | |
315 | zfs_prop_t prop, char *on, char *off) | |
316 | { | |
317 | char *source; | |
318 | uint64_t value; | |
319 | ||
320 | /* Skip adding duplicate default options */ | |
321 | if ((strstr(options, on) != NULL) || (strstr(options, off) != NULL)) | |
322 | return (0); | |
323 | ||
324 | /* | |
79251738 | 325 | * zfs_prop_get_int() is not used to ensure our mount options |
326 | * are not influenced by the current /proc/self/mounts contents. | |
2cf7f52b BB |
327 | */ |
328 | value = getprop_uint64(zhp, prop, &source); | |
329 | ||
330 | (void) strlcat(options, ",", len); | |
331 | (void) strlcat(options, value ? on : off, len); | |
332 | ||
333 | return (0); | |
334 | } | |
335 | ||
336 | static int | |
337 | zfs_add_options(zfs_handle_t *zhp, char *options, int len) | |
338 | { | |
339 | int error = 0; | |
340 | ||
341 | error = zfs_add_option(zhp, options, len, | |
342 | ZFS_PROP_ATIME, MNTOPT_ATIME, MNTOPT_NOATIME); | |
67600771 CC |
343 | /* |
344 | * don't add relatime/strictatime when atime=off, otherwise strictatime | |
345 | * will force atime=on | |
346 | */ | |
347 | if (strstr(options, MNTOPT_NOATIME) == NULL) { | |
348 | error = zfs_add_option(zhp, options, len, | |
349 | ZFS_PROP_RELATIME, MNTOPT_RELATIME, MNTOPT_STRICTATIME); | |
350 | } | |
2cf7f52b BB |
351 | error = error ? error : zfs_add_option(zhp, options, len, |
352 | ZFS_PROP_DEVICES, MNTOPT_DEVICES, MNTOPT_NODEVICES); | |
353 | error = error ? error : zfs_add_option(zhp, options, len, | |
354 | ZFS_PROP_EXEC, MNTOPT_EXEC, MNTOPT_NOEXEC); | |
355 | error = error ? error : zfs_add_option(zhp, options, len, | |
356 | ZFS_PROP_READONLY, MNTOPT_RO, MNTOPT_RW); | |
357 | error = error ? error : zfs_add_option(zhp, options, len, | |
358 | ZFS_PROP_SETUID, MNTOPT_SETUID, MNTOPT_NOSETUID); | |
2cf7f52b BB |
359 | error = error ? error : zfs_add_option(zhp, options, len, |
360 | ZFS_PROP_NBMAND, MNTOPT_NBMAND, MNTOPT_NONBMAND); | |
361 | ||
362 | return (error); | |
363 | } | |
364 | ||
68a192e4 KE |
365 | int |
366 | zfs_mount(zfs_handle_t *zhp, const char *options, int flags) | |
367 | { | |
368 | char mountpoint[ZFS_MAXPROPLEN]; | |
369 | ||
370 | if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, | |
371 | flags)) | |
372 | return (0); | |
373 | ||
374 | return (zfs_mount_at(zhp, options, flags, mountpoint)); | |
375 | } | |
376 | ||
34dc7c2f BB |
377 | /* |
378 | * Mount the given filesystem. | |
379 | */ | |
380 | int | |
68a192e4 KE |
381 | zfs_mount_at(zfs_handle_t *zhp, const char *options, int flags, |
382 | const char *mountpoint) | |
34dc7c2f BB |
383 | { |
384 | struct stat buf; | |
34dc7c2f | 385 | char mntopts[MNT_LINE_MAX]; |
9540be9b | 386 | char overlay[ZFS_MAXPROPLEN]; |
b3530c42 AZ |
387 | char prop_encroot[MAXNAMELEN]; |
388 | boolean_t is_encroot; | |
389 | zfs_handle_t *encroot_hp = zhp; | |
34dc7c2f | 390 | libzfs_handle_t *hdl = zhp->zfs_hdl; |
b5256303 | 391 | uint64_t keystatus; |
2cf7f52b | 392 | int remount = 0, rc; |
34dc7c2f | 393 | |
2cf7f52b | 394 | if (options == NULL) { |
3fb1fcde | 395 | (void) strlcpy(mntopts, MNTOPT_DEFAULTS, sizeof (mntopts)); |
2cf7f52b | 396 | } else { |
34dc7c2f | 397 | (void) strlcpy(mntopts, options, sizeof (mntopts)); |
2cf7f52b BB |
398 | } |
399 | ||
400 | if (strstr(mntopts, MNTOPT_REMOUNT) != NULL) | |
401 | remount = 1; | |
34dc7c2f | 402 | |
68a192e4 | 403 | /* Potentially duplicates some checks if invoked by zfs_mount(). */ |
1f182103 | 404 | if (!zfs_is_mountable_internal(zhp)) |
68a192e4 KE |
405 | return (0); |
406 | ||
572e2857 BB |
407 | /* |
408 | * If the pool is imported read-only then all mounts must be read-only | |
409 | */ | |
410 | if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) | |
3fb1fcde BB |
411 | (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts)); |
412 | ||
2cf7f52b BB |
413 | /* |
414 | * Append default mount options which apply to the mount point. | |
415 | * This is done because under Linux (unlike Solaris) multiple mount | |
416 | * points may reference a single super block. This means that just | |
417 | * given a super block there is no back reference to update the per | |
418 | * mount point options. | |
419 | */ | |
420 | rc = zfs_add_options(zhp, mntopts, sizeof (mntopts)); | |
421 | if (rc) { | |
422 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, | |
423 | "default options unavailable")); | |
424 | return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, | |
425 | dgettext(TEXT_DOMAIN, "cannot mount '%s'"), | |
426 | mountpoint)); | |
427 | } | |
428 | ||
b5256303 TC |
429 | /* |
430 | * If the filesystem is encrypted the key must be loaded in order to | |
431 | * mount. If the key isn't loaded, the MS_CRYPT flag decides whether | |
432 | * or not we attempt to load the keys. Note: we must call | |
433 | * zfs_refresh_properties() here since some callers of this function | |
434 | * (most notably zpool_enable_datasets()) may implicitly load our key | |
435 | * by loading the parent's key first. | |
436 | */ | |
437 | if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) { | |
438 | zfs_refresh_properties(zhp); | |
439 | keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); | |
440 | ||
441 | /* | |
442 | * If the key is unavailable and MS_CRYPT is set give the | |
443 | * user a chance to enter the key. Otherwise just fail | |
444 | * immediately. | |
445 | */ | |
446 | if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) { | |
447 | if (flags & MS_CRYPT) { | |
b3530c42 AZ |
448 | rc = zfs_crypto_get_encryption_root(zhp, |
449 | &is_encroot, prop_encroot); | |
450 | if (rc) { | |
451 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, | |
452 | "Failed to get encryption root for " | |
453 | "'%s'."), zfs_get_name(zhp)); | |
454 | return (rc); | |
455 | } | |
456 | ||
457 | if (!is_encroot) { | |
458 | encroot_hp = zfs_open(hdl, prop_encroot, | |
459 | ZFS_TYPE_DATASET); | |
460 | if (encroot_hp == NULL) | |
461 | return (hdl->libzfs_error); | |
462 | } | |
463 | ||
464 | rc = zfs_crypto_load_key(encroot_hp, | |
465 | B_FALSE, NULL); | |
466 | ||
467 | if (!is_encroot) | |
468 | zfs_close(encroot_hp); | |
b5256303 TC |
469 | if (rc) |
470 | return (rc); | |
471 | } else { | |
472 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, | |
473 | "encryption key not loaded")); | |
474 | return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, | |
475 | dgettext(TEXT_DOMAIN, "cannot mount '%s'"), | |
476 | mountpoint)); | |
477 | } | |
478 | } | |
479 | ||
480 | } | |
481 | ||
3fb1fcde BB |
482 | /* |
483 | * Append zfsutil option so the mount helper allow the mount | |
484 | */ | |
485 | strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts)); | |
572e2857 | 486 | |
34dc7c2f BB |
487 | /* Create the directory if it doesn't already exist */ |
488 | if (lstat(mountpoint, &buf) != 0) { | |
489 | if (mkdirp(mountpoint, 0755) != 0) { | |
490 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, | |
217f4837 RM |
491 | "failed to create mountpoint: %s"), |
492 | strerror(errno)); | |
34dc7c2f BB |
493 | return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, |
494 | dgettext(TEXT_DOMAIN, "cannot mount '%s'"), | |
495 | mountpoint)); | |
496 | } | |
497 | } | |
498 | ||
9540be9b | 499 | /* |
f5f6fb03 RM |
500 | * Overlay mounts are enabled by default but may be disabled |
501 | * via the 'overlay' property. The -O flag remains for compatibility. | |
9540be9b NB |
502 | */ |
503 | if (!(flags & MS_OVERLAY)) { | |
504 | if (zfs_prop_get(zhp, ZFS_PROP_OVERLAY, overlay, | |
02730c33 | 505 | sizeof (overlay), NULL, NULL, 0, B_FALSE) == 0) { |
9540be9b NB |
506 | if (strcmp(overlay, "on") == 0) { |
507 | flags |= MS_OVERLAY; | |
508 | } | |
509 | } | |
510 | } | |
511 | ||
34dc7c2f BB |
512 | /* |
513 | * Determine if the mountpoint is empty. If so, refuse to perform the | |
e18be9a6 | 514 | * mount. We don't perform this check if 'remount' is |
f5f6fb03 | 515 | * specified or if overlay option (-O) is given |
34dc7c2f | 516 | */ |
e18be9a6 SC |
517 | if ((flags & MS_OVERLAY) == 0 && !remount && |
518 | !dir_is_empty(mountpoint)) { | |
34dc7c2f BB |
519 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, |
520 | "directory is not empty")); | |
521 | return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, | |
522 | dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); | |
523 | } | |
524 | ||
525 | /* perform the mount */ | |
501a1511 | 526 | rc = do_mount(zhp, mountpoint, mntopts, flags); |
3fb1fcde | 527 | if (rc) { |
34dc7c2f BB |
528 | /* |
529 | * Generic errors are nasty, but there are just way too many | |
530 | * from mount(), and they're well-understood. We pick a few | |
531 | * common ones to improve upon. | |
532 | */ | |
3fb1fcde | 533 | if (rc == EBUSY) { |
34dc7c2f BB |
534 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, |
535 | "mountpoint or dataset is busy")); | |
3fb1fcde | 536 | } else if (rc == EPERM) { |
34dc7c2f BB |
537 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, |
538 | "Insufficient privileges")); | |
3fb1fcde | 539 | } else if (rc == ENOTSUP) { |
428870ff BB |
540 | int spa_version; |
541 | ||
542 | VERIFY(zfs_spa_version(zhp, &spa_version) == 0); | |
f00f4690 AZ |
543 | zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, |
544 | "Can't mount a version %llu " | |
428870ff BB |
545 | "file system on a version %d pool. Pool must be" |
546 | " upgraded to mount this file system."), | |
547 | (u_longlong_t)zfs_prop_get_int(zhp, | |
548 | ZFS_PROP_VERSION), spa_version); | |
34dc7c2f | 549 | } else { |
f00f4690 | 550 | zfs_error_aux(hdl, "%s", strerror(rc)); |
34dc7c2f | 551 | } |
34dc7c2f BB |
552 | return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, |
553 | dgettext(TEXT_DOMAIN, "cannot mount '%s'"), | |
554 | zhp->zfs_name)); | |
555 | } | |
556 | ||
2cf7f52b BB |
557 | /* remove the mounted entry before re-adding on remount */ |
558 | if (remount) | |
559 | libzfs_mnttab_remove(hdl, zhp->zfs_name); | |
560 | ||
fb5f0bc8 | 561 | /* add the mounted entry into our cache */ |
3fb1fcde | 562 | libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts); |
34dc7c2f BB |
563 | return (0); |
564 | } | |
565 | ||
566 | /* | |
567 | * Unmount a single filesystem. | |
568 | */ | |
569 | static int | |
41eba770 | 570 | unmount_one(zfs_handle_t *zhp, const char *mountpoint, int flags) |
34dc7c2f | 571 | { |
9ac97c2a BB |
572 | int error; |
573 | ||
41eba770 | 574 | error = do_unmount(zhp, mountpoint, flags); |
9ac97c2a | 575 | if (error != 0) { |
f5ada653 DB |
576 | int libzfs_err; |
577 | ||
578 | switch (error) { | |
579 | case EBUSY: | |
580 | libzfs_err = EZFS_BUSY; | |
581 | break; | |
582 | case EIO: | |
583 | libzfs_err = EZFS_IO; | |
584 | break; | |
585 | case ENOENT: | |
586 | libzfs_err = EZFS_NOENT; | |
587 | break; | |
588 | case ENOMEM: | |
589 | libzfs_err = EZFS_NOMEM; | |
590 | break; | |
591 | case EPERM: | |
592 | libzfs_err = EZFS_PERM; | |
593 | break; | |
594 | default: | |
595 | libzfs_err = EZFS_UMOUNTFAILED; | |
596 | } | |
5dc6fc2b RE |
597 | if (zhp) { |
598 | return (zfs_error_fmt(zhp->zfs_hdl, libzfs_err, | |
599 | dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), | |
600 | mountpoint)); | |
601 | } else { | |
602 | return (-1); | |
603 | } | |
34dc7c2f BB |
604 | } |
605 | ||
606 | return (0); | |
607 | } | |
608 | ||
609 | /* | |
610 | * Unmount the given filesystem. | |
611 | */ | |
612 | int | |
613 | zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) | |
614 | { | |
fb5f0bc8 BB |
615 | libzfs_handle_t *hdl = zhp->zfs_hdl; |
616 | struct mnttab entry; | |
34dc7c2f | 617 | char *mntpt = NULL; |
765d1f06 | 618 | boolean_t encroot, unmounted = B_FALSE; |
34dc7c2f | 619 | |
fb5f0bc8 | 620 | /* check to see if we need to unmount the filesystem */ |
34dc7c2f | 621 | if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && |
fb5f0bc8 | 622 | libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) { |
34dc7c2f BB |
623 | /* |
624 | * mountpoint may have come from a call to | |
625 | * getmnt/getmntany if it isn't NULL. If it is NULL, | |
fb5f0bc8 BB |
626 | * we know it comes from libzfs_mnttab_find which can |
627 | * then get freed later. We strdup it to play it safe. | |
34dc7c2f BB |
628 | */ |
629 | if (mountpoint == NULL) | |
1bf490ba | 630 | mntpt = zfs_strdup(hdl, entry.mnt_mountp); |
34dc7c2f | 631 | else |
fb5f0bc8 | 632 | mntpt = zfs_strdup(hdl, mountpoint); |
34dc7c2f BB |
633 | |
634 | /* | |
635 | * Unshare and unmount the filesystem | |
636 | */ | |
2d96d7aa | 637 | if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0) { |
638 | free(mntpt); | |
34dc7c2f | 639 | return (-1); |
2d96d7aa | 640 | } |
c15d36c6 | 641 | zfs_commit_all_shares(); |
34dc7c2f | 642 | |
41eba770 | 643 | if (unmount_one(zhp, mntpt, flags) != 0) { |
34dc7c2f BB |
644 | free(mntpt); |
645 | (void) zfs_shareall(zhp); | |
c15d36c6 | 646 | zfs_commit_all_shares(); |
34dc7c2f BB |
647 | return (-1); |
648 | } | |
765d1f06 | 649 | |
fb5f0bc8 | 650 | libzfs_mnttab_remove(hdl, zhp->zfs_name); |
34dc7c2f | 651 | free(mntpt); |
765d1f06 TC |
652 | unmounted = B_TRUE; |
653 | } | |
654 | ||
655 | /* | |
656 | * If the MS_CRYPT flag is provided we must ensure we attempt to | |
657 | * unload the dataset's key regardless of whether we did any work | |
658 | * to unmount it. We only do this for encryption roots. | |
659 | */ | |
660 | if ((flags & MS_CRYPT) != 0 && | |
661 | zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) { | |
662 | zfs_refresh_properties(zhp); | |
663 | ||
664 | if (zfs_crypto_get_encryption_root(zhp, &encroot, NULL) != 0 && | |
665 | unmounted) { | |
666 | (void) zfs_mount(zhp, NULL, 0); | |
667 | return (-1); | |
668 | } | |
669 | ||
670 | if (encroot && zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) == | |
671 | ZFS_KEYSTATUS_AVAILABLE && | |
672 | zfs_crypto_unload_key(zhp) != 0) { | |
673 | (void) zfs_mount(zhp, NULL, 0); | |
674 | return (-1); | |
675 | } | |
34dc7c2f BB |
676 | } |
677 | ||
3e8d5e4f JL |
678 | zpool_disable_volume_os(zhp->zfs_name); |
679 | ||
34dc7c2f BB |
680 | return (0); |
681 | } | |
682 | ||
683 | /* | |
684 | * Unmount this filesystem and any children inheriting the mountpoint property. | |
685 | * To do this, just act like we're changing the mountpoint property, but don't | |
686 | * remount the filesystems afterwards. | |
687 | */ | |
688 | int | |
689 | zfs_unmountall(zfs_handle_t *zhp, int flags) | |
690 | { | |
691 | prop_changelist_t *clp; | |
692 | int ret; | |
693 | ||
50a343d8 | 694 | clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, |
765d1f06 | 695 | CL_GATHER_ITER_MOUNTED, flags); |
34dc7c2f BB |
696 | if (clp == NULL) |
697 | return (-1); | |
698 | ||
699 | ret = changelist_prefix(clp); | |
700 | changelist_free(clp); | |
701 | ||
702 | return (ret); | |
703 | } | |
704 | ||
705 | boolean_t | |
706 | zfs_is_shared(zfs_handle_t *zhp) | |
707 | { | |
708 | zfs_share_type_t rc = 0; | |
0481eabf | 709 | const zfs_share_proto_t *curr_proto; |
34dc7c2f BB |
710 | |
711 | if (ZFS_IS_VOLUME(zhp)) | |
428870ff | 712 | return (B_FALSE); |
34dc7c2f BB |
713 | |
714 | for (curr_proto = share_all_proto; *curr_proto != PROTO_END; | |
715 | curr_proto++) | |
716 | rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto); | |
717 | ||
718 | return (rc ? B_TRUE : B_FALSE); | |
719 | } | |
720 | ||
c15d36c6 GW |
721 | /* |
722 | * Unshare a filesystem by mountpoint. | |
723 | */ | |
724 | int | |
725 | unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint, | |
726 | zfs_share_proto_t proto) | |
727 | { | |
728 | int err; | |
729 | ||
730 | err = sa_disable_share(mountpoint, proto_table[proto].p_name); | |
731 | if (err != SA_OK) { | |
732 | return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err, | |
733 | dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"), | |
734 | name, sa_errorstr(err))); | |
735 | } | |
736 | return (0); | |
737 | } | |
738 | ||
739 | /* | |
740 | * Query libshare for the given mountpoint and protocol, returning | |
741 | * a zfs_share_type_t value. | |
742 | */ | |
743 | zfs_share_type_t | |
744 | is_shared(const char *mountpoint, zfs_share_proto_t proto) | |
745 | { | |
746 | if (sa_is_shared(mountpoint, proto_table[proto].p_name)) { | |
747 | switch (proto) { | |
748 | case PROTO_NFS: | |
749 | return (SHARED_NFS); | |
750 | case PROTO_SMB: | |
751 | return (SHARED_SMB); | |
752 | default: | |
753 | return (SHARED_NOT_SHARED); | |
754 | } | |
755 | } | |
756 | return (SHARED_NOT_SHARED); | |
757 | } | |
758 | ||
759 | /* | |
760 | * Share the given filesystem according to the options in the specified | |
761 | * protocol specific properties (sharenfs, sharesmb). We rely | |
762 | * on "libshare" to do the dirty work for us. | |
763 | */ | |
764 | int | |
0481eabf | 765 | zfs_share_proto(zfs_handle_t *zhp, const zfs_share_proto_t *proto) |
c15d36c6 GW |
766 | { |
767 | char mountpoint[ZFS_MAXPROPLEN]; | |
768 | char shareopts[ZFS_MAXPROPLEN]; | |
769 | char sourcestr[ZFS_MAXPROPLEN]; | |
0481eabf | 770 | const zfs_share_proto_t *curr_proto; |
610cb4fb | 771 | zprop_source_t sourcetype; |
c15d36c6 GW |
772 | int err = 0; |
773 | ||
774 | if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, 0)) | |
775 | return (0); | |
776 | ||
777 | for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) { | |
778 | /* | |
779 | * Return success if there are no share options. | |
780 | */ | |
781 | if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop, | |
782 | shareopts, sizeof (shareopts), &sourcetype, sourcestr, | |
783 | ZFS_MAXPROPLEN, B_FALSE) != 0 || | |
784 | strcmp(shareopts, "off") == 0) | |
785 | continue; | |
786 | ||
787 | /* | |
788 | * If the 'zoned' property is set, then zfs_is_mountable() | |
789 | * will have already bailed out if we are in the global zone. | |
790 | * But local zones cannot be NFS servers, so we ignore it for | |
791 | * local zones as well. | |
792 | */ | |
793 | if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) | |
794 | continue; | |
795 | ||
796 | err = sa_enable_share(zfs_get_name(zhp), mountpoint, shareopts, | |
797 | proto_table[*curr_proto].p_name); | |
798 | if (err != SA_OK) { | |
799 | return (zfs_error_fmt(zhp->zfs_hdl, | |
800 | proto_table[*curr_proto].p_share_err, | |
801 | dgettext(TEXT_DOMAIN, "cannot share '%s: %s'"), | |
802 | zfs_get_name(zhp), sa_errorstr(err))); | |
803 | } | |
804 | ||
805 | } | |
806 | return (0); | |
807 | } | |
808 | ||
34dc7c2f BB |
809 | int |
810 | zfs_share(zfs_handle_t *zhp) | |
811 | { | |
572e2857 | 812 | assert(!ZFS_IS_VOLUME(zhp)); |
34dc7c2f BB |
813 | return (zfs_share_proto(zhp, share_all_proto)); |
814 | } | |
815 | ||
816 | int | |
817 | zfs_unshare(zfs_handle_t *zhp) | |
818 | { | |
572e2857 | 819 | assert(!ZFS_IS_VOLUME(zhp)); |
34dc7c2f BB |
820 | return (zfs_unshareall(zhp)); |
821 | } | |
822 | ||
823 | /* | |
824 | * Check to see if the filesystem is currently shared. | |
825 | */ | |
739cfb96 | 826 | static zfs_share_type_t |
34dc7c2f BB |
827 | zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto) |
828 | { | |
829 | char *mountpoint; | |
830 | zfs_share_type_t rc; | |
831 | ||
832 | if (!zfs_is_mounted(zhp, &mountpoint)) | |
833 | return (SHARED_NOT_SHARED); | |
834 | ||
c15d36c6 | 835 | if ((rc = is_shared(mountpoint, proto)) |
23d70cde | 836 | != SHARED_NOT_SHARED) { |
34dc7c2f BB |
837 | if (where != NULL) |
838 | *where = mountpoint; | |
839 | else | |
840 | free(mountpoint); | |
841 | return (rc); | |
842 | } else { | |
843 | free(mountpoint); | |
844 | return (SHARED_NOT_SHARED); | |
845 | } | |
846 | } | |
847 | ||
848 | boolean_t | |
849 | zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) | |
850 | { | |
851 | return (zfs_is_shared_proto(zhp, where, | |
852 | PROTO_NFS) != SHARED_NOT_SHARED); | |
853 | } | |
854 | ||
855 | boolean_t | |
856 | zfs_is_shared_smb(zfs_handle_t *zhp, char **where) | |
857 | { | |
858 | return (zfs_is_shared_proto(zhp, where, | |
859 | PROTO_SMB) != SHARED_NOT_SHARED); | |
860 | } | |
861 | ||
34dc7c2f BB |
862 | /* |
863 | * zfs_parse_options(options, proto) | |
864 | * | |
865 | * Call the legacy parse interface to get the protocol specific | |
866 | * options using the NULL arg to indicate that this is a "parse" only. | |
867 | */ | |
868 | int | |
869 | zfs_parse_options(char *options, zfs_share_proto_t proto) | |
870 | { | |
c15d36c6 GW |
871 | return (sa_validate_shareopts(options, proto_table[proto].p_name)); |
872 | } | |
873 | ||
874 | void | |
0481eabf | 875 | zfs_commit_proto(const zfs_share_proto_t *proto) |
c15d36c6 | 876 | { |
0481eabf AZ |
877 | const zfs_share_proto_t *curr_proto; |
878 | for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) | |
c15d36c6 | 879 | sa_commit_shares(proto_table[*curr_proto].p_name); |
c15d36c6 GW |
880 | } |
881 | ||
882 | void | |
883 | zfs_commit_nfs_shares(void) | |
884 | { | |
885 | zfs_commit_proto(nfs_only); | |
886 | } | |
887 | ||
888 | void | |
889 | zfs_commit_smb_shares(void) | |
890 | { | |
891 | zfs_commit_proto(smb_only); | |
892 | } | |
893 | ||
894 | void | |
895 | zfs_commit_all_shares(void) | |
896 | { | |
897 | zfs_commit_proto(share_all_proto); | |
898 | } | |
899 | ||
900 | void | |
901 | zfs_commit_shares(const char *proto) | |
902 | { | |
903 | if (proto == NULL) | |
904 | zfs_commit_proto(share_all_proto); | |
905 | else if (strcmp(proto, "nfs") == 0) | |
906 | zfs_commit_proto(nfs_only); | |
907 | else if (strcmp(proto, "smb") == 0) | |
908 | zfs_commit_proto(smb_only); | |
34dc7c2f BB |
909 | } |
910 | ||
34dc7c2f BB |
911 | int |
912 | zfs_share_nfs(zfs_handle_t *zhp) | |
913 | { | |
914 | return (zfs_share_proto(zhp, nfs_only)); | |
915 | } | |
916 | ||
917 | int | |
918 | zfs_share_smb(zfs_handle_t *zhp) | |
919 | { | |
920 | return (zfs_share_proto(zhp, smb_only)); | |
921 | } | |
922 | ||
923 | int | |
924 | zfs_shareall(zfs_handle_t *zhp) | |
925 | { | |
926 | return (zfs_share_proto(zhp, share_all_proto)); | |
927 | } | |
928 | ||
34dc7c2f BB |
929 | /* |
930 | * Unshare the given filesystem. | |
931 | */ | |
932 | int | |
933 | zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint, | |
0481eabf | 934 | const zfs_share_proto_t *proto) |
34dc7c2f | 935 | { |
fb5f0bc8 BB |
936 | libzfs_handle_t *hdl = zhp->zfs_hdl; |
937 | struct mnttab entry; | |
34dc7c2f BB |
938 | char *mntpt = NULL; |
939 | ||
940 | /* check to see if need to unmount the filesystem */ | |
34dc7c2f | 941 | if (mountpoint != NULL) |
2d96d7aa | 942 | mntpt = zfs_strdup(hdl, mountpoint); |
34dc7c2f BB |
943 | |
944 | if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && | |
fb5f0bc8 | 945 | libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) { |
0481eabf | 946 | const zfs_share_proto_t *curr_proto; |
34dc7c2f BB |
947 | |
948 | if (mountpoint == NULL) | |
949 | mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); | |
950 | ||
951 | for (curr_proto = proto; *curr_proto != PROTO_END; | |
d1d7e268 | 952 | curr_proto++) { |
34dc7c2f | 953 | |
c15d36c6 GW |
954 | if (is_shared(mntpt, *curr_proto)) { |
955 | if (unshare_one(hdl, zhp->zfs_name, | |
956 | mntpt, *curr_proto) != 0) { | |
957 | if (mntpt != NULL) | |
958 | free(mntpt); | |
959 | return (-1); | |
960 | } | |
34dc7c2f BB |
961 | } |
962 | } | |
963 | } | |
964 | if (mntpt != NULL) | |
965 | free(mntpt); | |
966 | ||
967 | return (0); | |
968 | } | |
969 | ||
970 | int | |
971 | zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) | |
972 | { | |
973 | return (zfs_unshare_proto(zhp, mountpoint, nfs_only)); | |
974 | } | |
975 | ||
976 | int | |
977 | zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint) | |
978 | { | |
979 | return (zfs_unshare_proto(zhp, mountpoint, smb_only)); | |
980 | } | |
981 | ||
982 | /* | |
983 | * Same as zfs_unmountall(), but for NFS and SMB unshares. | |
984 | */ | |
65c7cc49 | 985 | static int |
0481eabf | 986 | zfs_unshareall_proto(zfs_handle_t *zhp, const zfs_share_proto_t *proto) |
34dc7c2f BB |
987 | { |
988 | prop_changelist_t *clp; | |
989 | int ret; | |
990 | ||
b128c09f | 991 | clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0); |
34dc7c2f BB |
992 | if (clp == NULL) |
993 | return (-1); | |
994 | ||
995 | ret = changelist_unshare(clp, proto); | |
996 | changelist_free(clp); | |
997 | ||
998 | return (ret); | |
999 | } | |
1000 | ||
1001 | int | |
1002 | zfs_unshareall_nfs(zfs_handle_t *zhp) | |
1003 | { | |
1004 | return (zfs_unshareall_proto(zhp, nfs_only)); | |
1005 | } | |
1006 | ||
1007 | int | |
1008 | zfs_unshareall_smb(zfs_handle_t *zhp) | |
1009 | { | |
1010 | return (zfs_unshareall_proto(zhp, smb_only)); | |
1011 | } | |
1012 | ||
1013 | int | |
1014 | zfs_unshareall(zfs_handle_t *zhp) | |
1015 | { | |
1016 | return (zfs_unshareall_proto(zhp, share_all_proto)); | |
1017 | } | |
1018 | ||
1019 | int | |
1020 | zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint) | |
1021 | { | |
1022 | return (zfs_unshare_proto(zhp, mountpoint, share_all_proto)); | |
1023 | } | |
1024 | ||
2f71caf2 | 1025 | int |
1026 | zfs_unshareall_bytype(zfs_handle_t *zhp, const char *mountpoint, | |
1027 | const char *proto) | |
1028 | { | |
1029 | if (proto == NULL) | |
1030 | return (zfs_unshare_proto(zhp, mountpoint, share_all_proto)); | |
1031 | if (strcmp(proto, "nfs") == 0) | |
1032 | return (zfs_unshare_proto(zhp, mountpoint, nfs_only)); | |
1033 | else if (strcmp(proto, "smb") == 0) | |
1034 | return (zfs_unshare_proto(zhp, mountpoint, smb_only)); | |
1035 | else | |
1036 | return (1); | |
1037 | } | |
1038 | ||
34dc7c2f BB |
1039 | /* |
1040 | * Remove the mountpoint associated with the current dataset, if necessary. | |
1041 | * We only remove the underlying directory if: | |
1042 | * | |
1043 | * - The mountpoint is not 'none' or 'legacy' | |
1044 | * - The mountpoint is non-empty | |
1045 | * - The mountpoint is the default or inherited | |
1046 | * - The 'zoned' property is set, or we're in a local zone | |
1047 | * | |
1048 | * Any other directories we leave alone. | |
1049 | */ | |
1050 | void | |
1051 | remove_mountpoint(zfs_handle_t *zhp) | |
1052 | { | |
1053 | char mountpoint[ZFS_MAXPROPLEN]; | |
1054 | zprop_source_t source; | |
1055 | ||
73cdcc63 MM |
1056 | if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), |
1057 | &source, 0)) | |
34dc7c2f BB |
1058 | return; |
1059 | ||
1060 | if (source == ZPROP_SRC_DEFAULT || | |
1061 | source == ZPROP_SRC_INHERITED) { | |
1062 | /* | |
1063 | * Try to remove the directory, silently ignoring any errors. | |
1064 | * The filesystem may have since been removed or moved around, | |
1065 | * and this error isn't really useful to the administrator in | |
1066 | * any way. | |
1067 | */ | |
1068 | (void) rmdir(mountpoint); | |
1069 | } | |
1070 | } | |
1071 | ||
a10d50f9 SR |
1072 | /* |
1073 | * Add the given zfs handle to the cb_handles array, dynamically reallocating | |
1074 | * the array if it is out of space. | |
1075 | */ | |
572e2857 BB |
1076 | void |
1077 | libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp) | |
1078 | { | |
1079 | if (cbp->cb_alloc == cbp->cb_used) { | |
1080 | size_t newsz; | |
a10d50f9 | 1081 | zfs_handle_t **newhandles; |
572e2857 | 1082 | |
a10d50f9 SR |
1083 | newsz = cbp->cb_alloc != 0 ? cbp->cb_alloc * 2 : 64; |
1084 | newhandles = zfs_realloc(zhp->zfs_hdl, | |
1085 | cbp->cb_handles, cbp->cb_alloc * sizeof (zfs_handle_t *), | |
1086 | newsz * sizeof (zfs_handle_t *)); | |
1087 | cbp->cb_handles = newhandles; | |
572e2857 BB |
1088 | cbp->cb_alloc = newsz; |
1089 | } | |
1090 | cbp->cb_handles[cbp->cb_used++] = zhp; | |
1091 | } | |
34dc7c2f | 1092 | |
a10d50f9 SR |
1093 | /* |
1094 | * Recursive helper function used during file system enumeration | |
1095 | */ | |
34dc7c2f | 1096 | static int |
a10d50f9 | 1097 | zfs_iter_cb(zfs_handle_t *zhp, void *data) |
34dc7c2f | 1098 | { |
572e2857 | 1099 | get_all_cb_t *cbp = data; |
34dc7c2f | 1100 | |
572e2857 | 1101 | if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) { |
34dc7c2f BB |
1102 | zfs_close(zhp); |
1103 | return (0); | |
1104 | } | |
1105 | ||
1106 | if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) { | |
1107 | zfs_close(zhp); | |
1108 | return (0); | |
1109 | } | |
1110 | ||
b5256303 TC |
1111 | if (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) == |
1112 | ZFS_KEYSTATUS_UNAVAILABLE) { | |
1113 | zfs_close(zhp); | |
1114 | return (0); | |
1115 | } | |
1116 | ||
47dfff3b MA |
1117 | /* |
1118 | * If this filesystem is inconsistent and has a receive resume | |
1119 | * token, we can not mount it. | |
1120 | */ | |
1121 | if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && | |
1122 | zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, | |
1123 | NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { | |
1124 | zfs_close(zhp); | |
1125 | return (0); | |
1126 | } | |
1127 | ||
572e2857 | 1128 | libzfs_add_handle(cbp, zhp); |
399b9819 | 1129 | if (zfs_iter_filesystems(zhp, zfs_iter_cb, cbp) != 0) { |
572e2857 BB |
1130 | zfs_close(zhp); |
1131 | return (-1); | |
34dc7c2f | 1132 | } |
572e2857 | 1133 | return (0); |
34dc7c2f BB |
1134 | } |
1135 | ||
a10d50f9 SR |
1136 | /* |
1137 | * Sort comparator that compares two mountpoint paths. We sort these paths so | |
1138 | * that subdirectories immediately follow their parents. This means that we | |
e63ac16d AF |
1139 | * effectively treat the '/' character as the lowest value non-nul char. |
1140 | * Since filesystems from non-global zones can have the same mountpoint | |
1141 | * as other filesystems, the comparator sorts global zone filesystems to | |
1142 | * the top of the list. This means that the global zone will traverse the | |
1143 | * filesystem list in the correct order and can stop when it sees the | |
1144 | * first zoned filesystem. In a non-global zone, only the delegated | |
1145 | * filesystems are seen. | |
1146 | * | |
1147 | * An example sorted list using this comparator would look like: | |
a10d50f9 SR |
1148 | * |
1149 | * /foo | |
1150 | * /foo/bar | |
1151 | * /foo/bar/baz | |
1152 | * /foo/baz | |
1153 | * /foo.bar | |
e63ac16d AF |
1154 | * /foo (NGZ1) |
1155 | * /foo (NGZ2) | |
a10d50f9 SR |
1156 | * |
1157 | * The mounting code depends on this ordering to deterministically iterate | |
1158 | * over filesystems in order to spawn parallel mount tasks. | |
1159 | */ | |
e63ac16d | 1160 | static int |
a10d50f9 | 1161 | mountpoint_cmp(const void *arga, const void *argb) |
34dc7c2f | 1162 | { |
a10d50f9 SR |
1163 | zfs_handle_t *const *zap = arga; |
1164 | zfs_handle_t *za = *zap; | |
1165 | zfs_handle_t *const *zbp = argb; | |
1166 | zfs_handle_t *zb = *zbp; | |
34dc7c2f BB |
1167 | char mounta[MAXPATHLEN]; |
1168 | char mountb[MAXPATHLEN]; | |
a10d50f9 SR |
1169 | const char *a = mounta; |
1170 | const char *b = mountb; | |
34dc7c2f | 1171 | boolean_t gota, gotb; |
e63ac16d AF |
1172 | uint64_t zoneda, zonedb; |
1173 | ||
1174 | zoneda = zfs_prop_get_int(za, ZFS_PROP_ZONED); | |
1175 | zonedb = zfs_prop_get_int(zb, ZFS_PROP_ZONED); | |
1176 | if (zoneda && !zonedb) | |
1177 | return (1); | |
1178 | if (!zoneda && zonedb) | |
1179 | return (-1); | |
34dc7c2f | 1180 | |
a10d50f9 SR |
1181 | gota = (zfs_get_type(za) == ZFS_TYPE_FILESYSTEM); |
1182 | if (gota) { | |
1183 | verify(zfs_prop_get(za, ZFS_PROP_MOUNTPOINT, mounta, | |
34dc7c2f | 1184 | sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); |
a10d50f9 SR |
1185 | } |
1186 | gotb = (zfs_get_type(zb) == ZFS_TYPE_FILESYSTEM); | |
1187 | if (gotb) { | |
1188 | verify(zfs_prop_get(zb, ZFS_PROP_MOUNTPOINT, mountb, | |
34dc7c2f | 1189 | sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); |
a10d50f9 | 1190 | } |
34dc7c2f | 1191 | |
a10d50f9 SR |
1192 | if (gota && gotb) { |
1193 | while (*a != '\0' && (*a == *b)) { | |
1194 | a++; | |
1195 | b++; | |
1196 | } | |
1197 | if (*a == *b) | |
1198 | return (0); | |
1199 | if (*a == '\0') | |
1200 | return (-1); | |
1201 | if (*b == '\0') | |
1202 | return (1); | |
1203 | if (*a == '/') | |
1204 | return (-1); | |
1205 | if (*b == '/') | |
1206 | return (1); | |
1207 | return (*a < *b ? -1 : *a > *b); | |
1208 | } | |
34dc7c2f BB |
1209 | |
1210 | if (gota) | |
1211 | return (-1); | |
1212 | if (gotb) | |
1213 | return (1); | |
1214 | ||
a10d50f9 SR |
1215 | /* |
1216 | * If neither filesystem has a mountpoint, revert to sorting by | |
1217 | * dataset name. | |
1218 | */ | |
1219 | return (strcmp(zfs_get_name(za), zfs_get_name(zb))); | |
34dc7c2f BB |
1220 | } |
1221 | ||
1222 | /* | |
ab5036df TK |
1223 | * Return true if path2 is a child of path1 or path2 equals path1 or |
1224 | * path1 is "/" (path2 is always a child of "/"). | |
34dc7c2f | 1225 | */ |
a10d50f9 SR |
1226 | static boolean_t |
1227 | libzfs_path_contains(const char *path1, const char *path2) | |
34dc7c2f | 1228 | { |
ab5036df TK |
1229 | return (strcmp(path1, path2) == 0 || strcmp(path1, "/") == 0 || |
1230 | (strstr(path2, path1) == path2 && path2[strlen(path1)] == '/')); | |
a10d50f9 SR |
1231 | } |
1232 | ||
1233 | /* | |
1234 | * Given a mountpoint specified by idx in the handles array, find the first | |
1235 | * non-descendent of that mountpoint and return its index. Descendant paths | |
1236 | * start with the parent's path. This function relies on the ordering | |
1237 | * enforced by mountpoint_cmp(). | |
1238 | */ | |
1239 | static int | |
1240 | non_descendant_idx(zfs_handle_t **handles, size_t num_handles, int idx) | |
1241 | { | |
1242 | char parent[ZFS_MAXPROPLEN]; | |
1243 | char child[ZFS_MAXPROPLEN]; | |
1244 | int i; | |
1245 | ||
1246 | verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, parent, | |
1247 | sizeof (parent), NULL, NULL, 0, B_FALSE) == 0); | |
1248 | ||
1249 | for (i = idx + 1; i < num_handles; i++) { | |
1250 | verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT, child, | |
1251 | sizeof (child), NULL, NULL, 0, B_FALSE) == 0); | |
1252 | if (!libzfs_path_contains(parent, child)) | |
1253 | break; | |
1254 | } | |
1255 | return (i); | |
1256 | } | |
1257 | ||
1258 | typedef struct mnt_param { | |
1259 | libzfs_handle_t *mnt_hdl; | |
1260 | tpool_t *mnt_tp; | |
1261 | zfs_handle_t **mnt_zhps; /* filesystems to mount */ | |
1262 | size_t mnt_num_handles; | |
1263 | int mnt_idx; /* Index of selected entry to mount */ | |
1264 | zfs_iter_f mnt_func; | |
1265 | void *mnt_data; | |
1266 | } mnt_param_t; | |
1267 | ||
1268 | /* | |
1269 | * Allocate and populate the parameter struct for mount function, and | |
1270 | * schedule mounting of the entry selected by idx. | |
1271 | */ | |
1272 | static void | |
1273 | zfs_dispatch_mount(libzfs_handle_t *hdl, zfs_handle_t **handles, | |
1274 | size_t num_handles, int idx, zfs_iter_f func, void *data, tpool_t *tp) | |
1275 | { | |
1276 | mnt_param_t *mnt_param = zfs_alloc(hdl, sizeof (mnt_param_t)); | |
34dc7c2f | 1277 | |
a10d50f9 SR |
1278 | mnt_param->mnt_hdl = hdl; |
1279 | mnt_param->mnt_tp = tp; | |
1280 | mnt_param->mnt_zhps = handles; | |
1281 | mnt_param->mnt_num_handles = num_handles; | |
1282 | mnt_param->mnt_idx = idx; | |
1283 | mnt_param->mnt_func = func; | |
1284 | mnt_param->mnt_data = data; | |
1285 | ||
1286 | (void) tpool_dispatch(tp, zfs_mount_task, (void*)mnt_param); | |
1287 | } | |
1288 | ||
1289 | /* | |
1290 | * This is the structure used to keep state of mounting or sharing operations | |
1291 | * during a call to zpool_enable_datasets(). | |
1292 | */ | |
1293 | typedef struct mount_state { | |
34dc7c2f | 1294 | /* |
a10d50f9 SR |
1295 | * ms_mntstatus is set to -1 if any mount fails. While multiple threads |
1296 | * could update this variable concurrently, no synchronization is | |
1297 | * needed as it's only ever set to -1. | |
34dc7c2f | 1298 | */ |
a10d50f9 SR |
1299 | int ms_mntstatus; |
1300 | int ms_mntflags; | |
1301 | const char *ms_mntopts; | |
1302 | } mount_state_t; | |
1303 | ||
1304 | static int | |
1305 | zfs_mount_one(zfs_handle_t *zhp, void *arg) | |
1306 | { | |
1307 | mount_state_t *ms = arg; | |
1308 | int ret = 0; | |
34dc7c2f | 1309 | |
34dc7c2f | 1310 | /* |
a10d50f9 SR |
1311 | * don't attempt to mount encrypted datasets with |
1312 | * unloaded keys | |
34dc7c2f | 1313 | */ |
a10d50f9 SR |
1314 | if (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) == |
1315 | ZFS_KEYSTATUS_UNAVAILABLE) | |
1316 | return (0); | |
1317 | ||
1318 | if (zfs_mount(zhp, ms->ms_mntopts, ms->ms_mntflags) != 0) | |
1319 | ret = ms->ms_mntstatus = -1; | |
1320 | return (ret); | |
1321 | } | |
1322 | ||
1323 | static int | |
1324 | zfs_share_one(zfs_handle_t *zhp, void *arg) | |
1325 | { | |
1326 | mount_state_t *ms = arg; | |
1327 | int ret = 0; | |
1328 | ||
1329 | if (zfs_share(zhp) != 0) | |
1330 | ret = ms->ms_mntstatus = -1; | |
1331 | return (ret); | |
1332 | } | |
1333 | ||
1334 | /* | |
1335 | * Thread pool function to mount one file system. On completion, it finds and | |
1336 | * schedules its children to be mounted. This depends on the sorting done in | |
1337 | * zfs_foreach_mountpoint(). Note that the degenerate case (chain of entries | |
1338 | * each descending from the previous) will have no parallelism since we always | |
1339 | * have to wait for the parent to finish mounting before we can schedule | |
1340 | * its children. | |
1341 | */ | |
1342 | static void | |
1343 | zfs_mount_task(void *arg) | |
1344 | { | |
1345 | mnt_param_t *mp = arg; | |
1346 | int idx = mp->mnt_idx; | |
1347 | zfs_handle_t **handles = mp->mnt_zhps; | |
1348 | size_t num_handles = mp->mnt_num_handles; | |
1349 | char mountpoint[ZFS_MAXPROPLEN]; | |
1350 | ||
1351 | verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, mountpoint, | |
1352 | sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); | |
1353 | ||
1354 | if (mp->mnt_func(handles[idx], mp->mnt_data) != 0) | |
3c617c79 | 1355 | goto out; |
34dc7c2f BB |
1356 | |
1357 | /* | |
a10d50f9 SR |
1358 | * We dispatch tasks to mount filesystems with mountpoints underneath |
1359 | * this one. We do this by dispatching the next filesystem with a | |
1360 | * descendant mountpoint of the one we just mounted, then skip all of | |
1361 | * its descendants, dispatch the next descendant mountpoint, and so on. | |
1362 | * The non_descendant_idx() function skips over filesystems that are | |
1363 | * descendants of the filesystem we just dispatched. | |
34dc7c2f | 1364 | */ |
a10d50f9 SR |
1365 | for (int i = idx + 1; i < num_handles; |
1366 | i = non_descendant_idx(handles, num_handles, i)) { | |
1367 | char child[ZFS_MAXPROPLEN]; | |
1368 | verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT, | |
1369 | child, sizeof (child), NULL, NULL, 0, B_FALSE) == 0); | |
1370 | ||
1371 | if (!libzfs_path_contains(mountpoint, child)) | |
1372 | break; /* not a descendant, return */ | |
1373 | zfs_dispatch_mount(mp->mnt_hdl, handles, num_handles, i, | |
1374 | mp->mnt_func, mp->mnt_data, mp->mnt_tp); | |
1375 | } | |
3c617c79 AZ |
1376 | |
1377 | out: | |
a10d50f9 SR |
1378 | free(mp); |
1379 | } | |
d164b209 | 1380 | |
a10d50f9 SR |
1381 | /* |
1382 | * Issue the func callback for each ZFS handle contained in the handles | |
1383 | * array. This function is used to mount all datasets, and so this function | |
1384 | * guarantees that filesystems for parent mountpoints are called before their | |
1385 | * children. As such, before issuing any callbacks, we first sort the array | |
1386 | * of handles by mountpoint. | |
1387 | * | |
1388 | * Callbacks are issued in one of two ways: | |
1389 | * | |
1390 | * 1. Sequentially: If the parallel argument is B_FALSE or the ZFS_SERIAL_MOUNT | |
1391 | * environment variable is set, then we issue callbacks sequentially. | |
1392 | * | |
1393 | * 2. In parallel: If the parallel argument is B_TRUE and the ZFS_SERIAL_MOUNT | |
1394 | * environment variable is not set, then we use a tpool to dispatch threads | |
1395 | * to mount filesystems in parallel. This function dispatches tasks to mount | |
1396 | * the filesystems at the top-level mountpoints, and these tasks in turn | |
1397 | * are responsible for recursively mounting filesystems in their children | |
1398 | * mountpoints. | |
1399 | */ | |
1400 | void | |
1401 | zfs_foreach_mountpoint(libzfs_handle_t *hdl, zfs_handle_t **handles, | |
1402 | size_t num_handles, zfs_iter_f func, void *data, boolean_t parallel) | |
1403 | { | |
e63ac16d AF |
1404 | zoneid_t zoneid = getzoneid(); |
1405 | ||
a10d50f9 SR |
1406 | /* |
1407 | * The ZFS_SERIAL_MOUNT environment variable is an undocumented | |
1408 | * variable that can be used as a convenience to do a/b comparison | |
1409 | * of serial vs. parallel mounting. | |
1410 | */ | |
1411 | boolean_t serial_mount = !parallel || | |
1412 | (getenv("ZFS_SERIAL_MOUNT") != NULL); | |
b5256303 | 1413 | |
a10d50f9 SR |
1414 | /* |
1415 | * Sort the datasets by mountpoint. See mountpoint_cmp for details | |
1416 | * of how these are sorted. | |
1417 | */ | |
1418 | qsort(handles, num_handles, sizeof (zfs_handle_t *), mountpoint_cmp); | |
1419 | ||
1420 | if (serial_mount) { | |
1421 | for (int i = 0; i < num_handles; i++) { | |
1422 | func(handles[i], data); | |
1423 | } | |
1424 | return; | |
34dc7c2f BB |
1425 | } |
1426 | ||
1427 | /* | |
a10d50f9 SR |
1428 | * Issue the callback function for each dataset using a parallel |
1429 | * algorithm that uses a thread pool to manage threads. | |
1430 | */ | |
1431 | tpool_t *tp = tpool_create(1, mount_tp_nthr, 0, NULL); | |
1432 | ||
1433 | /* | |
1434 | * There may be multiple "top level" mountpoints outside of the pool's | |
1435 | * root mountpoint, e.g.: /foo /bar. Dispatch a mount task for each of | |
1436 | * these. | |
34dc7c2f | 1437 | */ |
a10d50f9 SR |
1438 | for (int i = 0; i < num_handles; |
1439 | i = non_descendant_idx(handles, num_handles, i)) { | |
e63ac16d AF |
1440 | /* |
1441 | * Since the mountpoints have been sorted so that the zoned | |
1442 | * filesystems are at the end, a zoned filesystem seen from | |
1443 | * the global zone means that we're done. | |
1444 | */ | |
1445 | if (zoneid == GLOBAL_ZONEID && | |
1446 | zfs_prop_get_int(handles[i], ZFS_PROP_ZONED)) | |
1447 | break; | |
a10d50f9 SR |
1448 | zfs_dispatch_mount(hdl, handles, num_handles, i, func, data, |
1449 | tp); | |
34dc7c2f BB |
1450 | } |
1451 | ||
a10d50f9 SR |
1452 | tpool_wait(tp); /* wait for all scheduled mounts to complete */ |
1453 | tpool_destroy(tp); | |
1454 | } | |
1455 | ||
1456 | /* | |
1457 | * Mount and share all datasets within the given pool. This assumes that no | |
1458 | * datasets within the pool are currently mounted. | |
1459 | */ | |
a10d50f9 SR |
1460 | int |
1461 | zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) | |
1462 | { | |
1463 | get_all_cb_t cb = { 0 }; | |
1464 | mount_state_t ms = { 0 }; | |
1465 | zfs_handle_t *zfsp; | |
1466 | int ret = 0; | |
1467 | ||
1468 | if ((zfsp = zfs_open(zhp->zpool_hdl, zhp->zpool_name, | |
1469 | ZFS_TYPE_DATASET)) == NULL) | |
1470 | goto out; | |
1471 | ||
1472 | /* | |
1473 | * Gather all non-snapshot datasets within the pool. Start by adding | |
1474 | * the root filesystem for this pool to the list, and then iterate | |
1475 | * over all child filesystems. | |
1476 | */ | |
1477 | libzfs_add_handle(&cb, zfsp); | |
399b9819 | 1478 | if (zfs_iter_filesystems(zfsp, zfs_iter_cb, &cb) != 0) |
a10d50f9 SR |
1479 | goto out; |
1480 | ||
1481 | /* | |
1482 | * Mount all filesystems | |
1483 | */ | |
1484 | ms.ms_mntopts = mntopts; | |
1485 | ms.ms_mntflags = flags; | |
1486 | zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used, | |
1487 | zfs_mount_one, &ms, B_TRUE); | |
1488 | if (ms.ms_mntstatus != 0) | |
1489 | ret = ms.ms_mntstatus; | |
1490 | ||
1491 | /* | |
1492 | * Share all filesystems that need to be shared. This needs to be | |
1493 | * a separate pass because libshare is not mt-safe, and so we need | |
1494 | * to share serially. | |
1495 | */ | |
1496 | ms.ms_mntstatus = 0; | |
1497 | zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used, | |
1498 | zfs_share_one, &ms, B_FALSE); | |
1499 | if (ms.ms_mntstatus != 0) | |
1500 | ret = ms.ms_mntstatus; | |
c15d36c6 GW |
1501 | else |
1502 | zfs_commit_all_shares(); | |
34dc7c2f BB |
1503 | |
1504 | out: | |
a10d50f9 | 1505 | for (int i = 0; i < cb.cb_used; i++) |
572e2857 BB |
1506 | zfs_close(cb.cb_handles[i]); |
1507 | free(cb.cb_handles); | |
34dc7c2f BB |
1508 | |
1509 | return (ret); | |
1510 | } | |
1511 | ||
41eba770 JL |
1512 | struct sets_s { |
1513 | char *mountpoint; | |
1514 | zfs_handle_t *dataset; | |
1515 | }; | |
1516 | ||
34dc7c2f BB |
1517 | static int |
1518 | mountpoint_compare(const void *a, const void *b) | |
1519 | { | |
41eba770 JL |
1520 | const struct sets_s *mounta = (struct sets_s *)a; |
1521 | const struct sets_s *mountb = (struct sets_s *)b; | |
34dc7c2f | 1522 | |
41eba770 | 1523 | return (strcmp(mountb->mountpoint, mounta->mountpoint)); |
34dc7c2f BB |
1524 | } |
1525 | ||
1526 | /* | |
1527 | * Unshare and unmount all datasets within the given pool. We don't want to | |
1528 | * rely on traversing the DSL to discover the filesystems within the pool, | |
1529 | * because this may be expensive (if not all of them are mounted), and can fail | |
79251738 | 1530 | * arbitrarily (on I/O error, for example). Instead, we walk /proc/self/mounts |
1531 | * and gather all the filesystems that are currently mounted. | |
34dc7c2f | 1532 | */ |
34dc7c2f BB |
1533 | int |
1534 | zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) | |
1535 | { | |
1536 | int used, alloc; | |
53352772 | 1537 | FILE *mnttab; |
34dc7c2f BB |
1538 | struct mnttab entry; |
1539 | size_t namelen; | |
41eba770 | 1540 | struct sets_s *sets = NULL; |
34dc7c2f BB |
1541 | libzfs_handle_t *hdl = zhp->zpool_hdl; |
1542 | int i; | |
1543 | int ret = -1; | |
1544 | int flags = (force ? MS_FORCE : 0); | |
1545 | ||
34dc7c2f BB |
1546 | namelen = strlen(zhp->zpool_name); |
1547 | ||
53352772 | 1548 | if ((mnttab = fopen(MNTTAB, "re")) == NULL) |
cbca6076 JL |
1549 | return (ENOENT); |
1550 | ||
34dc7c2f | 1551 | used = alloc = 0; |
53352772 | 1552 | while (getmntent(mnttab, &entry) == 0) { |
34dc7c2f BB |
1553 | /* |
1554 | * Ignore non-ZFS entries. | |
1555 | */ | |
1556 | if (entry.mnt_fstype == NULL || | |
1557 | strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) | |
1558 | continue; | |
1559 | ||
1560 | /* | |
1561 | * Ignore filesystems not within this pool. | |
1562 | */ | |
1563 | if (entry.mnt_mountp == NULL || | |
1564 | strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || | |
1565 | (entry.mnt_special[namelen] != '/' && | |
1566 | entry.mnt_special[namelen] != '\0')) | |
1567 | continue; | |
1568 | ||
1569 | /* | |
1570 | * At this point we've found a filesystem within our pool. Add | |
1571 | * it to our growing list. | |
1572 | */ | |
1573 | if (used == alloc) { | |
1574 | if (alloc == 0) { | |
34dc7c2f | 1575 | |
41eba770 JL |
1576 | if ((sets = zfs_alloc(hdl, |
1577 | 8 * sizeof (struct sets_s))) == NULL) | |
34dc7c2f BB |
1578 | goto out; |
1579 | ||
1580 | alloc = 8; | |
1581 | } else { | |
1582 | void *ptr; | |
1583 | ||
41eba770 JL |
1584 | if ((ptr = zfs_realloc(hdl, sets, |
1585 | alloc * sizeof (struct sets_s), | |
1586 | alloc * 2 * sizeof (struct sets_s))) | |
1587 | == NULL) | |
34dc7c2f | 1588 | goto out; |
41eba770 | 1589 | sets = ptr; |
34dc7c2f BB |
1590 | |
1591 | alloc *= 2; | |
1592 | } | |
1593 | } | |
1594 | ||
41eba770 | 1595 | if ((sets[used].mountpoint = zfs_strdup(hdl, |
34dc7c2f BB |
1596 | entry.mnt_mountp)) == NULL) |
1597 | goto out; | |
1598 | ||
1599 | /* | |
1600 | * This is allowed to fail, in case there is some I/O error. It | |
1601 | * is only used to determine if we need to remove the underlying | |
1602 | * mountpoint, so failure is not fatal. | |
1603 | */ | |
41eba770 JL |
1604 | sets[used].dataset = make_dataset_handle(hdl, |
1605 | entry.mnt_special); | |
34dc7c2f BB |
1606 | |
1607 | used++; | |
1608 | } | |
1609 | ||
1610 | /* | |
1611 | * At this point, we have the entire list of filesystems, so sort it by | |
1612 | * mountpoint. | |
1613 | */ | |
63652e15 DS |
1614 | if (used != 0) |
1615 | qsort(sets, used, sizeof (struct sets_s), mountpoint_compare); | |
34dc7c2f BB |
1616 | |
1617 | /* | |
1618 | * Walk through and first unshare everything. | |
1619 | */ | |
1620 | for (i = 0; i < used; i++) { | |
0481eabf | 1621 | const zfs_share_proto_t *curr_proto; |
34dc7c2f BB |
1622 | for (curr_proto = share_all_proto; *curr_proto != PROTO_END; |
1623 | curr_proto++) { | |
41eba770 JL |
1624 | if (is_shared(sets[i].mountpoint, *curr_proto) && |
1625 | unshare_one(hdl, sets[i].mountpoint, | |
1626 | sets[i].mountpoint, *curr_proto) != 0) | |
34dc7c2f BB |
1627 | goto out; |
1628 | } | |
1629 | } | |
c15d36c6 | 1630 | zfs_commit_all_shares(); |
34dc7c2f BB |
1631 | |
1632 | /* | |
1633 | * Now unmount everything, removing the underlying directories as | |
1634 | * appropriate. | |
1635 | */ | |
1636 | for (i = 0; i < used; i++) { | |
41eba770 JL |
1637 | if (unmount_one(sets[i].dataset, sets[i].mountpoint, |
1638 | flags) != 0) | |
34dc7c2f BB |
1639 | goto out; |
1640 | } | |
1641 | ||
1642 | for (i = 0; i < used; i++) { | |
41eba770 JL |
1643 | if (sets[i].dataset) |
1644 | remove_mountpoint(sets[i].dataset); | |
34dc7c2f BB |
1645 | } |
1646 | ||
3e8d5e4f JL |
1647 | zpool_disable_datasets_os(zhp, force); |
1648 | ||
34dc7c2f BB |
1649 | ret = 0; |
1650 | out: | |
53352772 | 1651 | (void) fclose(mnttab); |
34dc7c2f | 1652 | for (i = 0; i < used; i++) { |
41eba770 JL |
1653 | if (sets[i].dataset) |
1654 | zfs_close(sets[i].dataset); | |
1655 | free(sets[i].mountpoint); | |
34dc7c2f | 1656 | } |
41eba770 | 1657 | free(sets); |
34dc7c2f BB |
1658 | |
1659 | return (ret); | |
1660 | } |