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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2015 EveryCity Ltd.
26 * Copyright (c) 2015 by Delphix. All rights reserved.
27 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
28 */
29
30 /*
31 * System includes
32 */
33 #include <assert.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/mntent.h>
43 #include <sys/mnttab.h>
44 #include <sys/mount.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/vfstab.h>
48 #include <sys/zone.h>
49 #include <sys/mkdev.h>
50 #include <unistd.h>
51
52 #include <libbe.h>
53 #include <libbe_priv.h>
54
55 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
56
57 typedef struct dir_data {
58 char *dir;
59 char *ds;
60 } dir_data_t;
61
62 /* Private function prototypes */
63 static int be_mount_callback(zfs_handle_t *, void *);
64 static int be_unmount_callback(zfs_handle_t *, void *);
65 static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
66 static int fix_mountpoint(zfs_handle_t *);
67 static int fix_mountpoint_callback(zfs_handle_t *, void *);
68 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
69 boolean_t);
70 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
71 static int loopback_mount_zonepath(const char *, be_mount_data_t *);
72 static int iter_shared_fs_callback(zfs_handle_t *, void *);
73 static int zpool_shared_fs_callback(zpool_handle_t *, void *);
74 static int unmount_shared_fs(be_unmount_data_t *);
75 static int add_to_fs_list(be_fs_list_data_t *, const char *);
76 static int be_mount_root(zfs_handle_t *, char *);
77 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
78 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
79 static int be_unmount_zones(be_unmount_data_t *);
80 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
81 char *);
82 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
83 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
84 static int mount_zfs(zfs_handle_t *, char *);
85
86
87 /* ******************************************************************** */
88 /* Public Functions */
89 /* ******************************************************************** */
90
91 /*
92 * Function: be_mount
93 * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
94 * Parameters:
95 * be_attrs - pointer to nvlist_t of attributes being passed in.
96 * The following attributes are used by this function:
97 *
98 * BE_ATTR_ORIG_BE_NAME *required
99 * BE_ATTR_MOUNTPOINT *required
100 * BE_ATTR_MOUNT_FLAGS *optional
101 * Return:
102 * BE_SUCCESS - Success
103 * be_errno_t - Failure
104 * Scope:
105 * Public
106 */
107 int
be_mount(nvlist_t * be_attrs)108 be_mount(nvlist_t *be_attrs)
109 {
110 char *be_name = NULL;
111 char *mountpoint = NULL;
112 uint16_t flags = 0;
113 int ret = BE_SUCCESS;
114
115 /* Initialize libzfs handle */
116 if (!be_zfs_init())
117 return (BE_ERR_INIT);
118
119 /* Get original BE name */
120 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
121 != 0) {
122 be_print_err(gettext("be_mount: failed to lookup "
123 "BE_ATTR_ORIG_BE_NAME attribute\n"));
124 return (BE_ERR_INVAL);
125 }
126
127 /* Validate original BE name */
128 if (!be_valid_be_name(be_name)) {
129 be_print_err(gettext("be_mount: invalid BE name %s\n"),
130 be_name);
131 return (BE_ERR_INVAL);
132 }
133
134 /* Get mountpoint */
135 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
136 != 0) {
137 be_print_err(gettext("be_mount: failed to lookup "
138 "BE_ATTR_MOUNTPOINT attribute\n"));
139 return (BE_ERR_INVAL);
140 }
141
142 /* Get flags */
143 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
144 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
145 be_print_err(gettext("be_mount: failed to lookup "
146 "BE_ATTR_MOUNT_FLAGS attribute\n"));
147 return (BE_ERR_INVAL);
148 }
149
150 ret = _be_mount(be_name, &mountpoint, flags);
151
152 be_zfs_fini();
153
154 return (ret);
155 }
156
157 /*
158 * Function: be_unmount
159 * Description: Unmounts a BE and its subordinate datasets.
160 * Parameters:
161 * be_attrs - pointer to nvlist_t of attributes being passed in.
162 * The following attributes are used by this function:
163 *
164 * BE_ATTR_ORIG_BE_NAME *required
165 * BE_ATTR_UNMOUNT_FLAGS *optional
166 * Return:
167 * BE_SUCCESS - Success
168 * be_errno_t - Failure
169 * Scope:
170 * Public
171 */
172 int
be_unmount(nvlist_t * be_attrs)173 be_unmount(nvlist_t *be_attrs)
174 {
175 char *be_name = NULL;
176 char *be_name_mnt = NULL;
177 char *ds = NULL;
178 uint16_t flags = 0;
179 int ret = BE_SUCCESS;
180
181 /* Initialize libzfs handle */
182 if (!be_zfs_init())
183 return (BE_ERR_INIT);
184
185 /* Get original BE name */
186 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
187 != 0) {
188 be_print_err(gettext("be_unmount: failed to lookup "
189 "BE_ATTR_ORIG_BE_NAME attribute\n"));
190 return (BE_ERR_INVAL);
191 }
192
193 /* Check if we have mountpoint argument instead of BE name */
194 if (be_name[0] == '/') {
195 if ((ds = be_get_ds_from_dir(be_name)) != NULL) {
196 if ((be_name_mnt = strrchr(ds, '/')) != NULL) {
197 be_name = be_name_mnt + 1;
198 }
199 } else {
200 be_print_err(gettext("be_unmount: no datasets mounted "
201 "at '%s'\n"), be_name);
202 return (BE_ERR_INVAL);
203 }
204 }
205
206 /* Validate original BE name */
207 if (!be_valid_be_name(be_name)) {
208 be_print_err(gettext("be_unmount: invalid BE name %s\n"),
209 be_name);
210 return (BE_ERR_INVAL);
211 }
212
213 /* Get unmount flags */
214 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
215 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
216 be_print_err(gettext("be_unmount: failed to loookup "
217 "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
218 return (BE_ERR_INVAL);
219 }
220
221 ret = _be_unmount(be_name, flags);
222
223 be_zfs_fini();
224
225 return (ret);
226 }
227
228 /* ******************************************************************** */
229 /* Semi-Private Functions */
230 /* ******************************************************************** */
231
232 /*
233 * Function: _be_mount
234 * Description: Mounts a BE. If the altroot is not provided, this function
235 * will generate a temporary mountpoint to mount the BE at. It
236 * will return this temporary mountpoint to the caller via the
237 * altroot reference pointer passed in. This returned value is
238 * allocated on heap storage and is the repsonsibility of the
239 * caller to free.
240 * Parameters:
241 * be_name - pointer to name of BE to mount.
242 * altroot - reference pointer to altroot of where to mount BE.
243 * flags - flag indicating special handling for mounting the BE
244 * Return:
245 * BE_SUCCESS - Success
246 * be_errno_t - Failure
247 * Scope:
248 * Semi-private (library wide use only)
249 */
250 int
_be_mount(char * be_name,char ** altroot,int flags)251 _be_mount(char *be_name, char **altroot, int flags)
252 {
253 be_transaction_data_t bt = { 0 };
254 be_mount_data_t md = { 0 };
255 zfs_handle_t *zhp;
256 char obe_root_ds[MAXPATHLEN];
257 char *mp = NULL;
258 char *tmp_altroot = NULL;
259 int ret = BE_SUCCESS, err = 0;
260 uuid_t uu = { 0 };
261 boolean_t gen_tmp_altroot = B_FALSE;
262
263 if (be_name == NULL || altroot == NULL)
264 return (BE_ERR_INVAL);
265
266 /* Set be_name as obe_name in bt structure */
267 bt.obe_name = be_name;
268
269 /* Find which zpool obe_name lives in */
270 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
271 be_print_err(gettext("be_mount: failed to "
272 "find zpool for BE (%s)\n"), bt.obe_name);
273 return (BE_ERR_BE_NOENT);
274 } else if (err < 0) {
275 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
276 libzfs_error_description(g_zfs));
277 return (zfs_err_to_be_err(g_zfs));
278 }
279
280 /* Generate string for obe_name's root dataset */
281 if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
282 sizeof (obe_root_ds))) != BE_SUCCESS) {
283 be_print_err(gettext("%s: failed to get BE container dataset "
284 "for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name);
285 return (ret);
286 }
287 bt.obe_root_ds = obe_root_ds;
288
289 /* Get handle to BE's root dataset */
290 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
291 NULL) {
292 be_print_err(gettext("be_mount: failed to "
293 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
294 libzfs_error_description(g_zfs));
295 return (zfs_err_to_be_err(g_zfs));
296 }
297
298 /* Make sure BE's root dataset isn't already mounted somewhere */
299 if (zfs_is_mounted(zhp, &mp)) {
300 ZFS_CLOSE(zhp);
301 be_print_err(gettext("be_mount: %s is already mounted "
302 "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
303 free(mp);
304 return (BE_ERR_MOUNTED);
305 }
306
307 /*
308 * Fix this BE's mountpoint if its root dataset isn't set to
309 * either 'legacy' or '/'.
310 */
311 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
312 be_print_err(gettext("be_mount: mountpoint check "
313 "failed for %s\n"), bt.obe_root_ds);
314 ZFS_CLOSE(zhp);
315 return (ret);
316 }
317
318 /*
319 * If altroot not provided, create a temporary alternate root
320 * to mount on
321 */
322 if (*altroot == NULL) {
323 if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
324 != BE_SUCCESS) {
325 be_print_err(gettext("be_mount: failed to "
326 "make temporary mountpoint\n"));
327 ZFS_CLOSE(zhp);
328 return (ret);
329 }
330 gen_tmp_altroot = B_TRUE;
331 } else {
332 tmp_altroot = *altroot;
333 }
334
335 md.altroot = tmp_altroot;
336 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
337 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
338
339 /* Mount the BE's root file system */
340 if (getzoneid() == GLOBAL_ZONEID) {
341 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
342 be_print_err(gettext("be_mount: failed to "
343 "mount BE root file system\n"));
344 if (gen_tmp_altroot)
345 free(tmp_altroot);
346 ZFS_CLOSE(zhp);
347 return (ret);
348 }
349 } else {
350 /* Legacy mount the zone root dataset */
351 if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) {
352 be_print_err(gettext("be_mount: failed to "
353 "mount BE zone root file system\n"));
354 free(md.altroot);
355 ZFS_CLOSE(zhp);
356 return (ret);
357 }
358 }
359
360 /* Iterate through BE's children filesystems */
361 if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
362 tmp_altroot)) != 0) {
363 be_print_err(gettext("be_mount: failed to "
364 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
365 if (gen_tmp_altroot)
366 free(tmp_altroot);
367 ZFS_CLOSE(zhp);
368 return (err);
369 }
370
371 /*
372 * Mount shared file systems if mount flag says so.
373 */
374 if (md.shared_fs) {
375 /*
376 * Mount all ZFS file systems not under the BE's root dataset
377 */
378 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
379
380 /* TODO: Mount all non-ZFS file systems - Not supported yet */
381 }
382
383 /*
384 * If we're in the global zone and the global zone has a valid uuid,
385 * mount all supported non-global zones.
386 */
387 if (getzoneid() == GLOBAL_ZONEID &&
388 !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
389 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
390 if (be_mount_zones(zhp, &md) != BE_SUCCESS) {
391 ret = BE_ERR_NO_MOUNTED_ZONE;
392 }
393 }
394
395 ZFS_CLOSE(zhp);
396
397 /*
398 * If a NULL altroot was passed in, pass the generated altroot
399 * back to the caller in altroot.
400 */
401 if (gen_tmp_altroot) {
402 if (ret == BE_SUCCESS || ret == BE_ERR_NO_MOUNTED_ZONE)
403 *altroot = tmp_altroot;
404 else
405 free(tmp_altroot);
406 }
407
408 return (ret);
409 }
410
411 /*
412 * Function: _be_unmount
413 * Description: Unmount a BE.
414 * Parameters:
415 * be_name - pointer to name of BE to unmount.
416 * flags - flags for unmounting the BE.
417 * Returns:
418 * BE_SUCCESS - Success
419 * be_errno_t - Failure
420 * Scope:
421 * Semi-private (library wide use only)
422 */
423 int
_be_unmount(char * be_name,int flags)424 _be_unmount(char *be_name, int flags)
425 {
426 be_transaction_data_t bt = { 0 };
427 be_unmount_data_t ud = { 0 };
428 zfs_handle_t *zhp;
429 uuid_t uu = { 0 };
430 char obe_root_ds[MAXPATHLEN];
431 char mountpoint[MAXPATHLEN];
432 char *mp = NULL;
433 int ret = BE_SUCCESS;
434 int zret = 0;
435
436 if (be_name == NULL)
437 return (BE_ERR_INVAL);
438
439 /* Set be_name as obe_name in bt structure */
440 bt.obe_name = be_name;
441
442 /* Find which zpool obe_name lives in */
443 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
444 be_print_err(gettext("be_unmount: failed to "
445 "find zpool for BE (%s)\n"), bt.obe_name);
446 return (BE_ERR_BE_NOENT);
447 } else if (zret < 0) {
448 be_print_err(gettext("be_unmount: "
449 "zpool_iter failed: %s\n"),
450 libzfs_error_description(g_zfs));
451 ret = zfs_err_to_be_err(g_zfs);
452 return (ret);
453 }
454
455 /* Generate string for obe_name's root dataset */
456 if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
457 sizeof (obe_root_ds))) != BE_SUCCESS) {
458 be_print_err(gettext("%s: failed to get BE container dataset "
459 "for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name);
460 return (ret);
461 }
462 bt.obe_root_ds = obe_root_ds;
463
464 /* Get handle to BE's root dataset */
465 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
466 NULL) {
467 be_print_err(gettext("be_unmount: failed to "
468 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
469 libzfs_error_description(g_zfs));
470 ret = zfs_err_to_be_err(g_zfs);
471 return (ret);
472 }
473
474 /* Make sure BE's root dataset is mounted somewhere */
475 if (!zfs_is_mounted(zhp, &mp)) {
476
477 be_print_err(gettext("be_unmount: "
478 "(%s) not mounted\n"), bt.obe_name);
479
480 /*
481 * BE is not mounted, fix this BE's mountpoint if its root
482 * dataset isn't set to either 'legacy' or '/'.
483 */
484 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
485 be_print_err(gettext("be_unmount: mountpoint check "
486 "failed for %s\n"), bt.obe_root_ds);
487 ZFS_CLOSE(zhp);
488 return (ret);
489 }
490
491 ZFS_CLOSE(zhp);
492 return (BE_ERR_NOTMOUNTED);
493 }
494
495 /*
496 * If we didn't get a mountpoint from the zfs_is_mounted call,
497 * try and get it from its property.
498 */
499 if (mp == NULL) {
500 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
501 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
502 be_print_err(gettext("be_unmount: failed to "
503 "get mountpoint of (%s)\n"), bt.obe_name);
504 ZFS_CLOSE(zhp);
505 return (BE_ERR_ZFS);
506 }
507 } else {
508 (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
509 free(mp);
510 }
511
512 /* If BE mounted as current root, fail */
513 if (strcmp(mountpoint, "/") == 0) {
514 be_print_err(gettext("be_unmount: "
515 "cannot unmount currently running BE\n"));
516 ZFS_CLOSE(zhp);
517 return (BE_ERR_UMOUNT_CURR_BE);
518 }
519
520 ud.altroot = mountpoint;
521 ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
522
523 /* Unmount all supported non-global zones if we're in the global zone */
524 if (getzoneid() == GLOBAL_ZONEID &&
525 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
526 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
527 ZFS_CLOSE(zhp);
528 return (ret);
529 }
530 }
531
532 /* TODO: Unmount all non-ZFS file systems - Not supported yet */
533
534 /* Unmount all ZFS file systems not under the BE root dataset */
535 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
536 be_print_err(gettext("be_unmount: failed to "
537 "unmount shared file systems\n"));
538 ZFS_CLOSE(zhp);
539 return (ret);
540 }
541
542 /* Unmount all children datasets under the BE's root dataset */
543 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
544 &ud)) != 0) {
545 be_print_err(gettext("be_unmount: failed to "
546 "unmount BE (%s)\n"), bt.obe_name);
547 ZFS_CLOSE(zhp);
548 return (zret);
549 }
550
551 /* Unmount this BE's root filesystem */
552 if (getzoneid() == GLOBAL_ZONEID) {
553 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
554 ZFS_CLOSE(zhp);
555 return (ret);
556 }
557 } else {
558 if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) {
559 ZFS_CLOSE(zhp);
560 return (ret);
561 }
562 }
563
564 ZFS_CLOSE(zhp);
565
566 return (BE_SUCCESS);
567 }
568
569 /*
570 * Function: be_mount_zone_root
571 * Description: Mounts the zone root dataset for a zone.
572 * Parameters:
573 * zfs - zfs_handle_t pointer to zone root dataset
574 * md - be_mount_data_t pointer to data for zone to be mounted
575 * Returns:
576 * BE_SUCCESS - Success
577 * be_errno_t - Failure
578 * Scope:
579 * Semi-private (library wide use only)
580 */
581 int
be_mount_zone_root(zfs_handle_t * zhp,be_mount_data_t * md)582 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
583 {
584 struct stat buf;
585 char mountpoint[MAXPATHLEN];
586 int err = 0;
587
588 /* Get mountpoint property of dataset */
589 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
590 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
591 be_print_err(gettext("be_mount_zone_root: failed to "
592 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
593 libzfs_error_description(g_zfs));
594 return (zfs_err_to_be_err(g_zfs));
595 }
596
597 /*
598 * Make sure zone's root dataset is set to 'legacy'. This is
599 * currently a requirement in this implementation of zones
600 * support.
601 */
602 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
603 be_print_err(gettext("be_mount_zone_root: "
604 "zone root dataset mountpoint is not 'legacy'\n"));
605 return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
606 }
607
608 /* Create the mountpoint if it doesn't exist */
609 if (lstat(md->altroot, &buf) != 0) {
610 if (mkdirp(md->altroot, 0755) != 0) {
611 err = errno;
612 be_print_err(gettext("be_mount_zone_root: failed "
613 "to create mountpoint %s\n"), md->altroot);
614 return (errno_to_be_err(err));
615 }
616 }
617
618 /*
619 * Legacy mount the zone root dataset.
620 *
621 * As a workaround for 6176743, we mount the zone's root with the
622 * MS_OVERLAY option in case an alternate BE is mounted, and we're
623 * mounting the root for the zone from the current BE here. When an
624 * alternate BE is mounted, it ties up the zone's zoneroot directory
625 * for the current BE since the zone's zonepath is loopback mounted
626 * from the current BE.
627 *
628 * TODO: The MS_OVERLAY option needs to be removed when 6176743
629 * is fixed.
630 */
631 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
632 NULL, 0, NULL, 0) != 0) {
633 err = errno;
634 be_print_err(gettext("be_mount_zone_root: failed to "
635 "legacy mount zone root dataset (%s) at %s\n"),
636 zfs_get_name(zhp), md->altroot);
637 return (errno_to_be_err(err));
638 }
639
640 return (BE_SUCCESS);
641 }
642
643 /*
644 * Function: be_unmount_zone_root
645 * Description: Unmounts the zone root dataset for a zone.
646 * Parameters:
647 * zhp - zfs_handle_t pointer to zone root dataset
648 * ud - be_unmount_data_t pointer to data for zone to be unmounted
649 * Returns:
650 * BE_SUCCESS - Success
651 * be_errno_t - Failure
652 * Scope:
653 * Semi-private (library wise use only)
654 */
655 int
be_unmount_zone_root(zfs_handle_t * zhp,be_unmount_data_t * ud)656 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
657 {
658 char mountpoint[MAXPATHLEN];
659
660 /* Unmount the dataset */
661 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
662 be_print_err(gettext("be_unmount_zone_root: failed to "
663 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
664 libzfs_error_description(g_zfs));
665 return (zfs_err_to_be_err(g_zfs));
666 }
667
668 /* Get the current mountpoint property for the zone root dataset */
669 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
670 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
671 be_print_err(gettext("be_unmount_zone_root: failed to "
672 "get mountpoint property for zone root dataset (%s): %s\n"),
673 zfs_get_name(zhp), libzfs_error_description(g_zfs));
674 return (zfs_err_to_be_err(g_zfs));
675 }
676
677 /* If mountpoint not already set to 'legacy', set it to 'legacy' */
678 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
679 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
680 ZFS_MOUNTPOINT_LEGACY) != 0) {
681 be_print_err(gettext("be_unmount_zone_root: "
682 "failed to set mountpoint of zone root dataset "
683 "%s to 'legacy': %s\n"), zfs_get_name(zhp),
684 libzfs_error_description(g_zfs));
685 return (zfs_err_to_be_err(g_zfs));
686 }
687 }
688
689 return (BE_SUCCESS);
690 }
691
692 /*
693 * Function: be_get_legacy_fs
694 * Description: This function iterates through all non-shared file systems
695 * of a BE and finds the ones with a legacy mountpoint. For
696 * those file systems, it reads the BE's vfstab to get the
697 * mountpoint. If found, it adds that file system to the
698 * be_fs_list_data_t passed in.
699 *
700 * This function can be used to gather legacy mounted file systems
701 * for both global BEs and non-global zone BEs. To get data for
702 * a non-global zone BE, the zoneroot_ds and zoneroot parameters
703 * will be specified, otherwise they should be set to NULL.
704 * Parameters:
705 * be_name - global BE name from which to get legacy file
706 * system list.
707 * be_root_ds - root dataset of global BE.
708 * zoneroot_ds - root dataset of zone.
709 * zoneroot - zoneroot path of zone.
710 * fld - be_fs_list_data_t pointer.
711 * Returns:
712 * BE_SUCCESS - Success
713 * be_errno_t - Failure
714 * Scope:
715 * Semi-private (library wide use only)
716 */
717 int
be_get_legacy_fs(char * be_name,char * be_root_ds,char * zoneroot_ds,char * zoneroot,be_fs_list_data_t * fld)718 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
719 char *zoneroot, be_fs_list_data_t *fld)
720 {
721 zfs_handle_t *zhp = NULL;
722 char mountpoint[MAXPATHLEN];
723 boolean_t mounted_here = B_FALSE;
724 boolean_t zone_mounted_here = B_FALSE;
725 int ret = BE_SUCCESS, err = 0;
726
727 if (be_name == NULL || be_root_ds == NULL || fld == NULL)
728 return (BE_ERR_INVAL);
729
730 /* Get handle to BE's root dataset */
731 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
732 == NULL) {
733 be_print_err(gettext("be_get_legacy_fs: failed to "
734 "open BE root dataset (%s): %s\n"), be_root_ds,
735 libzfs_error_description(g_zfs));
736 ret = zfs_err_to_be_err(g_zfs);
737 return (ret);
738 }
739
740 /* If BE is not already mounted, mount it. */
741 if (!zfs_is_mounted(zhp, &fld->altroot)) {
742 if ((ret = _be_mount(be_name, &fld->altroot,
743 zoneroot_ds ? BE_MOUNT_FLAG_NULL :
744 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
745 be_print_err(gettext("be_get_legacy_fs: "
746 "failed to mount BE %s\n"), be_name);
747 goto cleanup;
748 }
749
750 mounted_here = B_TRUE;
751 } else if (fld->altroot == NULL) {
752 be_print_err(gettext("be_get_legacy_fs: failed to "
753 "get altroot of mounted BE %s: %s\n"),
754 be_name, libzfs_error_description(g_zfs));
755 ret = zfs_err_to_be_err(g_zfs);
756 goto cleanup;
757 }
758
759 /*
760 * If a zone root dataset was passed in, we're wanting to get
761 * legacy mounted file systems for that zone, not the global
762 * BE.
763 */
764 if (zoneroot_ds != NULL) {
765 be_mount_data_t zone_md = { 0 };
766
767 /* Close off handle to global BE's root dataset */
768 ZFS_CLOSE(zhp);
769
770 /* Get handle to zone's root dataset */
771 if ((zhp = zfs_open(g_zfs, zoneroot_ds,
772 ZFS_TYPE_FILESYSTEM)) == NULL) {
773 be_print_err(gettext("be_get_legacy_fs: failed to "
774 "open zone BE root dataset (%s): %s\n"),
775 zoneroot_ds, libzfs_error_description(g_zfs));
776 ret = zfs_err_to_be_err(g_zfs);
777 goto cleanup;
778 }
779
780 /* Make sure the zone we're looking for is mounted */
781 if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
782 char zone_altroot[MAXPATHLEN];
783
784 /* Generate alternate root path for zone */
785 (void) snprintf(zone_altroot, sizeof (zone_altroot),
786 "%s%s", fld->altroot, zoneroot);
787 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
788 be_print_err(gettext("be_get_legacy_fs: "
789 "memory allocation failed\n"));
790 ret = BE_ERR_NOMEM;
791 goto cleanup;
792 }
793
794 if ((ret = be_mount_zone_root(zhp, &zone_md))
795 != BE_SUCCESS) {
796 be_print_err(gettext("be_get_legacy_fs: "
797 "failed to mount zone root %s\n"),
798 zoneroot_ds);
799 free(zone_md.altroot);
800 zone_md.altroot = NULL;
801 goto cleanup;
802 }
803 zone_mounted_here = B_TRUE;
804 }
805
806 free(fld->altroot);
807 fld->altroot = zone_md.altroot;
808 }
809
810 /*
811 * If the root dataset is in the vfstab with a mountpoint of "/",
812 * add it to the list
813 */
814 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
815 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
816 if (strcmp(mountpoint, "/") == 0) {
817 if (add_to_fs_list(fld, zfs_get_name(zhp))
818 != BE_SUCCESS) {
819 be_print_err(gettext("be_get_legacy_fs: "
820 "failed to add %s to fs list\n"),
821 zfs_get_name(zhp));
822 ret = BE_ERR_INVAL;
823 goto cleanup;
824 }
825 }
826 }
827
828 /* Iterate subordinate file systems looking for legacy mounts */
829 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
830 fld)) != 0) {
831 be_print_err(gettext("be_get_legacy_fs: "
832 "failed to iterate %s to get legacy mounts\n"),
833 zfs_get_name(zhp));
834 }
835
836 cleanup:
837 /* If we mounted the zone BE, unmount it */
838 if (zone_mounted_here) {
839 be_unmount_data_t zone_ud = { 0 };
840
841 zone_ud.altroot = fld->altroot;
842 zone_ud.force = B_TRUE;
843 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
844 be_print_err(gettext("be_get_legacy_fs: "
845 "failed to unmount zone root %s\n"),
846 zoneroot_ds);
847 if (ret == BE_SUCCESS)
848 ret = err;
849 }
850 }
851
852 /* If we mounted this BE, unmount it */
853 if (mounted_here) {
854 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
855 be_print_err(gettext("be_get_legacy_fs: "
856 "failed to unmount %s\n"), be_name);
857 if (ret == BE_SUCCESS)
858 ret = err;
859 }
860 }
861
862 ZFS_CLOSE(zhp);
863
864 free(fld->altroot);
865 fld->altroot = NULL;
866
867 return (ret);
868 }
869
870 /*
871 * Function: be_free_fs_list
872 * Description: Function used to free the members of a be_fs_list_data_t
873 * structure.
874 * Parameters:
875 * fld - be_fs_list_data_t pointer to free.
876 * Returns:
877 * None
878 * Scope:
879 * Semi-private (library wide use only)
880 */
881 void
be_free_fs_list(be_fs_list_data_t * fld)882 be_free_fs_list(be_fs_list_data_t *fld)
883 {
884 int i;
885
886 if (fld == NULL)
887 return;
888
889 free(fld->altroot);
890
891 if (fld->fs_list == NULL)
892 return;
893
894 for (i = 0; i < fld->fs_num; i++)
895 free(fld->fs_list[i]);
896
897 free(fld->fs_list);
898 }
899
900 /*
901 * Function: be_get_ds_from_dir(char *dir)
902 * Description: Given a directory path, find the underlying dataset mounted
903 * at that directory path if there is one. The returned name
904 * is allocated in heap storage, so the caller is responsible
905 * for freeing it.
906 * Parameters:
907 * dir - char pointer of directory to find.
908 * Returns:
909 * NULL - if directory is not mounted from a dataset.
910 * name of dataset mounted at dir.
911 * Scope:
912 * Semi-private (library wide use only)
913 */
914 char *
be_get_ds_from_dir(char * dir)915 be_get_ds_from_dir(char *dir)
916 {
917 dir_data_t dd = { 0 };
918 char resolved_dir[MAXPATHLEN];
919
920 /* Make sure length of dir is within the max length */
921 if (dir == NULL || strlen(dir) >= MAXPATHLEN)
922 return (NULL);
923
924 /* Resolve dir in case its lofs mounted */
925 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
926 z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
927
928 dd.dir = resolved_dir;
929
930 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
931
932 return (dd.ds);
933 }
934
935 /*
936 * Function: be_make_tmp_mountpoint
937 * Description: This function generates a random temporary mountpoint
938 * and creates that mountpoint directory. It returns the
939 * mountpoint in heap storage, so the caller is responsible
940 * for freeing it.
941 * Parameters:
942 * tmp_mp - reference to pointer of where to store generated
943 * temporary mountpoint.
944 * Returns:
945 * BE_SUCCESS - Success
946 * be_errno_t - Failure
947 * Scope:
948 * Semi-private (library wide use only)
949 */
950 int
be_make_tmp_mountpoint(char ** tmp_mp)951 be_make_tmp_mountpoint(char **tmp_mp)
952 {
953 int err = 0;
954
955 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
956 be_print_err(gettext("be_make_tmp_mountpoint: "
957 "malloc failed\n"));
958 return (BE_ERR_NOMEM);
959 }
960 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
961 if (mkdtemp(*tmp_mp) == NULL) {
962 err = errno;
963 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
964 "for %s: %s\n"), *tmp_mp, strerror(err));
965 free(*tmp_mp);
966 *tmp_mp = NULL;
967 return (errno_to_be_err(err));
968 }
969
970 return (BE_SUCCESS);
971 }
972
973 /*
974 * Function: be_mount_pool
975 * Description: This function determines if the pool's datase is mounted
976 * and if not it is used to mount the pool's dataset. The
977 * function returns the current mountpoint if we are able
978 * to mount the dataset.
979 * Parameters:
980 * zhp - handle to the pool's dataset
981 * tmp_mntpnt - The temporary mountpoint that the pool's
982 * dataset is mounted on. This is set only
983 * if the attempt to mount the dataset at it's
984 * set mountpoint fails, and we've used a
985 * temporary mount point for this dataset. It
986 * is expected that the caller will free this
987 * memory.
988 * orig_mntpnt - The original mountpoint for the pool. If a
989 * temporary mount point was needed this will
990 * be used to reset the mountpoint property to
991 * it's original mountpoint. It is expected that
992 * the caller will free this memory.
993 * pool_mounted - This flag indicates that the pool was mounted
994 * in this function.
995 * Returns:
996 * BE_SUCCESS - Success
997 * be_errno_t - Failure
998 * Scope:
999 * Semi-private (library wide use only)
1000 */
1001 int
be_mount_pool(zfs_handle_t * zhp,char ** tmp_mntpnt,char ** orig_mntpnt,boolean_t * pool_mounted)1002 be_mount_pool(
1003 zfs_handle_t *zhp,
1004 char **tmp_mntpnt,
1005 char **orig_mntpnt,
1006 boolean_t *pool_mounted)
1007 {
1008
1009 char mountpoint[MAXPATHLEN];
1010 int ret = 0;
1011
1012 *tmp_mntpnt = NULL;
1013 *orig_mntpnt = NULL;
1014 *pool_mounted = B_FALSE;
1015
1016 if (!zfs_is_mounted(zhp, NULL)) {
1017 if (zfs_mount(zhp, NULL, 0) != 0) {
1018 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1019 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1020 be_print_err(gettext("be_mount_pool: failed to "
1021 "get mountpoint of (%s): %s\n"),
1022 zfs_get_name(zhp),
1023 libzfs_error_description(g_zfs));
1024 return (zfs_err_to_be_err(g_zfs));
1025 }
1026 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
1027 be_print_err(gettext("be_mount_pool: memory "
1028 "allocation failed\n"));
1029 return (BE_ERR_NOMEM);
1030 }
1031 /*
1032 * attempt to mount on a temp mountpoint
1033 */
1034 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
1035 != BE_SUCCESS) {
1036 be_print_err(gettext("be_mount_pool: failed "
1037 "to make temporary mountpoint\n"));
1038 free(*orig_mntpnt);
1039 *orig_mntpnt = NULL;
1040 return (ret);
1041 }
1042
1043 if (zfs_prop_set(zhp,
1044 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1045 *tmp_mntpnt) != 0) {
1046 be_print_err(gettext("be_mount_pool: failed "
1047 "to set mountpoint of pool dataset %s to "
1048 "%s: %s\n"), zfs_get_name(zhp),
1049 *orig_mntpnt,
1050 libzfs_error_description(g_zfs));
1051 free(*tmp_mntpnt);
1052 free(*orig_mntpnt);
1053 *orig_mntpnt = NULL;
1054 *tmp_mntpnt = NULL;
1055 return (zfs_err_to_be_err(g_zfs));
1056 }
1057
1058 if (zfs_mount(zhp, NULL, 0) != 0) {
1059 be_print_err(gettext("be_mount_pool: failed "
1060 "to mount dataset %s at %s: %s\n"),
1061 zfs_get_name(zhp), *tmp_mntpnt,
1062 libzfs_error_description(g_zfs));
1063 ret = zfs_err_to_be_err(g_zfs);
1064 if (zfs_prop_set(zhp,
1065 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1066 mountpoint) != 0) {
1067 be_print_err(gettext("be_mount_pool: "
1068 "failed to set mountpoint of pool "
1069 "dataset %s to %s: %s\n"),
1070 zfs_get_name(zhp), *tmp_mntpnt,
1071 libzfs_error_description(g_zfs));
1072 }
1073 free(*tmp_mntpnt);
1074 free(*orig_mntpnt);
1075 *orig_mntpnt = NULL;
1076 *tmp_mntpnt = NULL;
1077 return (ret);
1078 }
1079 }
1080 *pool_mounted = B_TRUE;
1081 }
1082
1083 return (BE_SUCCESS);
1084 }
1085
1086 /*
1087 * Function: be_unmount_pool
1088 * Description: This function is used to unmount the pool's dataset if we
1089 * mounted it previously using be_mount_pool().
1090 * Parameters:
1091 * zhp - handle to the pool's dataset
1092 * tmp_mntpnt - If a temprary mount point was used this will
1093 * be set. Since this was created in be_mount_pool
1094 * we will need to clean it up here.
1095 * orig_mntpnt - The original mountpoint for the pool. This is
1096 * used to set the dataset mountpoint property
1097 * back to it's original value in the case where a
1098 * temporary mountpoint was used.
1099 * Returns:
1100 * BE_SUCCESS - Success
1101 * be_errno_t - Failure
1102 * Scope:
1103 * Semi-private (library wide use only)
1104 */
1105 int
be_unmount_pool(zfs_handle_t * zhp,char * tmp_mntpnt,char * orig_mntpnt)1106 be_unmount_pool(
1107 zfs_handle_t *zhp,
1108 char *tmp_mntpnt,
1109 char *orig_mntpnt)
1110 {
1111 if (zfs_unmount(zhp, NULL, 0) != 0) {
1112 be_print_err(gettext("be_unmount_pool: failed to "
1113 "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1114 libzfs_error_description(g_zfs));
1115 return (zfs_err_to_be_err(g_zfs));
1116 }
1117 if (orig_mntpnt != NULL) {
1118 if (tmp_mntpnt != NULL &&
1119 strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1120 (void) rmdir(tmp_mntpnt);
1121 }
1122 if (zfs_prop_set(zhp,
1123 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1124 orig_mntpnt) != 0) {
1125 be_print_err(gettext("be_unmount_pool: failed "
1126 "to set the mountpoint for dataset (%s) to "
1127 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1128 libzfs_error_description(g_zfs));
1129 return (zfs_err_to_be_err(g_zfs));
1130 }
1131 }
1132
1133 return (BE_SUCCESS);
1134 }
1135
1136 /* ******************************************************************** */
1137 /* Private Functions */
1138 /* ******************************************************************** */
1139
1140 /*
1141 * Function: be_mount_callback
1142 * Description: Callback function used to iterate through all of a BE's
1143 * subordinate file systems and to mount them accordingly.
1144 * Parameters:
1145 * zhp - zfs_handle_t pointer to current file system being
1146 * processed.
1147 * data - pointer to the altroot of where to mount BE.
1148 * Returns:
1149 * 0 - Success
1150 * be_errno_t - Failure
1151 * Scope:
1152 * Private
1153 */
1154 static int
be_mount_callback(zfs_handle_t * zhp,void * data)1155 be_mount_callback(zfs_handle_t *zhp, void *data)
1156 {
1157 zprop_source_t sourcetype;
1158 const char *fs_name = zfs_get_name(zhp);
1159 char source[ZFS_MAX_DATASET_NAME_LEN];
1160 char *altroot = data;
1161 char zhp_mountpoint[MAXPATHLEN];
1162 char mountpoint[MAXPATHLEN];
1163 int ret = 0;
1164
1165 /* Get dataset's mountpoint and source values */
1166 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1167 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1168 B_FALSE) != 0) {
1169 be_print_err(gettext("be_mount_callback: failed to "
1170 "get mountpoint and sourcetype for %s\n"),
1171 fs_name);
1172 ZFS_CLOSE(zhp);
1173 return (BE_ERR_ZFS);
1174 }
1175
1176 /*
1177 * Set this filesystem's 'canmount' property to 'noauto' just incase
1178 * it's been set 'on'.
1179 */
1180 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1181 be_print_err(gettext("be_mount_callback: failed to "
1182 "set canmount to 'noauto' (%s)\n"), fs_name);
1183 ZFS_CLOSE(zhp);
1184 return (BE_ERR_ZFS);
1185 }
1186
1187 /*
1188 * If the mountpoint is none, there's nothing to do, goto next.
1189 * If the mountpoint is legacy, legacy mount it with mount(2).
1190 * If the mountpoint is inherited, its mountpoint should
1191 * already be set. If it's not, then explicitly fix-up
1192 * the mountpoint now by appending its explicitly set
1193 * mountpoint value to the BE mountpoint.
1194 */
1195 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1196 goto next;
1197 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1198 /*
1199 * If the mountpoint is set to 'legacy', we need to
1200 * dig into this BE's vfstab to figure out where to
1201 * mount it, and just mount it via mount(2).
1202 */
1203 if (get_mountpoint_from_vfstab(altroot, fs_name,
1204 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1205
1206 /* Legacy mount the file system */
1207 if (mount(fs_name, mountpoint, MS_DATA,
1208 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1209 be_print_err(
1210 gettext("be_mount_callback: "
1211 "failed to mount %s on %s\n"),
1212 fs_name, mountpoint);
1213 }
1214 } else {
1215 be_print_err(
1216 gettext("be_mount_callback: "
1217 "no entry for %s in vfstab, "
1218 "skipping ...\n"), fs_name);
1219 }
1220
1221 goto next;
1222
1223 } else if ((sourcetype & (ZPROP_SRC_INHERITED|ZPROP_SRC_LOCAL)) == 0) {
1224 /*
1225 * Skip dataset if mountpoint is not inherited
1226 * or explicitly set.
1227 */
1228 be_print_err(gettext("be_mount_callback: "
1229 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1230 fs_name, sourcetype);
1231
1232 goto next;
1233 }
1234
1235 /* Mount this filesystem */
1236 if (mount_zfs(zhp, altroot) != 0) {
1237 be_print_err(gettext("be_mount_callback: failed to "
1238 "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1239 strerror(errno));
1240 ZFS_CLOSE(zhp);
1241 return (BE_ERR_MOUNT);
1242 }
1243
1244 next:
1245 /* Iterate through this dataset's children and mount them */
1246 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1247 altroot)) != 0) {
1248 ZFS_CLOSE(zhp);
1249 return (ret);
1250 }
1251
1252
1253 ZFS_CLOSE(zhp);
1254 return (0);
1255 }
1256
1257 /*
1258 * Function: be_unmount_callback
1259 * Description: Callback function used to iterate through all of a BE's
1260 * subordinate file systems and to unmount them.
1261 * Parameters:
1262 * zhp - zfs_handle_t pointer to current file system being
1263 * processed.
1264 * data - pointer to the mountpoint of where BE is mounted.
1265 * Returns:
1266 * 0 - Success
1267 * be_errno_t - Failure
1268 * Scope:
1269 * Private
1270 */
1271 static int
be_unmount_callback(zfs_handle_t * zhp,void * data)1272 be_unmount_callback(zfs_handle_t *zhp, void *data)
1273 {
1274 be_unmount_data_t *ud = data;
1275 zprop_source_t sourcetype;
1276 const char *fs_name = zfs_get_name(zhp);
1277 char source[ZFS_MAX_DATASET_NAME_LEN];
1278 char mountpoint[MAXPATHLEN];
1279 char *zhp_mountpoint;
1280 int ret = 0;
1281
1282 /* Iterate down this dataset's children first */
1283 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1284 ret = BE_ERR_UMOUNT;
1285 goto done;
1286 }
1287
1288 /* Is dataset even mounted ? */
1289 if (!zfs_is_mounted(zhp, NULL))
1290 goto done;
1291
1292 /* Unmount this file system */
1293 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1294 be_print_err(gettext("be_unmount_callback: "
1295 "failed to unmount %s: %s\n"), fs_name,
1296 libzfs_error_description(g_zfs));
1297 ret = zfs_err_to_be_err(g_zfs);
1298 goto done;
1299 }
1300
1301 /* Get dataset's current mountpoint and source value */
1302 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1303 sizeof (mountpoint), &sourcetype, source, sizeof (source),
1304 B_FALSE) != 0) {
1305 be_print_err(gettext("be_unmount_callback: "
1306 "failed to get mountpoint and sourcetype for %s: %s\n"),
1307 fs_name, libzfs_error_description(g_zfs));
1308 ret = zfs_err_to_be_err(g_zfs);
1309 goto done;
1310 }
1311
1312 if (sourcetype & ZPROP_SRC_INHERITED) {
1313 /*
1314 * If the mountpoint is inherited we don't need to
1315 * do anything. When its parent gets processed
1316 * its mountpoint will be set accordingly.
1317 */
1318 goto done;
1319 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1320
1321 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1322 /*
1323 * If the mountpoint is set to 'legacy', its already
1324 * been unmounted (from above call to zfs_unmount), and
1325 * we don't need to do anything else with it.
1326 */
1327 goto done;
1328
1329 } else {
1330 /*
1331 * Else process dataset with explicitly set mountpoint.
1332 */
1333
1334 /*
1335 * Get this dataset's mountpoint relative to
1336 * the BE's mountpoint.
1337 */
1338 if ((strncmp(mountpoint, ud->altroot,
1339 strlen(ud->altroot)) == 0) &&
1340 (mountpoint[strlen(ud->altroot)] == '/')) {
1341
1342 zhp_mountpoint = mountpoint +
1343 strlen(ud->altroot);
1344
1345 /* Set this dataset's mountpoint value */
1346 if (zfs_prop_set(zhp,
1347 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1348 zhp_mountpoint)) {
1349 be_print_err(
1350 gettext("be_unmount_callback: "
1351 "failed to set mountpoint for "
1352 "%s to %s: %s\n"), fs_name,
1353 zhp_mountpoint,
1354 libzfs_error_description(g_zfs));
1355 ret = zfs_err_to_be_err(g_zfs);
1356 }
1357 } else {
1358 /*
1359 * Nothing to do, mountpoint shouldn't be
1360 * corrected.
1361 */
1362 goto done;
1363 }
1364 }
1365 } else {
1366 be_print_err(gettext("be_unmount_callback: "
1367 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1368 fs_name, sourcetype);
1369 ret = BE_ERR_ZFS;
1370 }
1371
1372 done:
1373 /* Set this filesystem's 'canmount' property to 'noauto' */
1374 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1375 be_print_err(gettext("be_unmount_callback: "
1376 "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1377 if (ret == 0)
1378 ret = BE_ERR_ZFS;
1379 }
1380
1381 ZFS_CLOSE(zhp);
1382 return (ret);
1383 }
1384
1385 /*
1386 * Function: be_get_legacy_fs_callback
1387 * Description: The callback function is used to iterate through all
1388 * non-shared file systems of a BE, finding ones that have
1389 * a legacy mountpoint and an entry in the BE's vfstab.
1390 * It adds these file systems to the callback data.
1391 * Parameters:
1392 * zhp - zfs_handle_t pointer to current file system being
1393 * processed.
1394 * data - be_fs_list_data_t pointer
1395 * Returns:
1396 * 0 - Success
1397 * be_errno_t - Failure
1398 * Scope:
1399 * Private
1400 */
1401 static int
be_get_legacy_fs_callback(zfs_handle_t * zhp,void * data)1402 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1403 {
1404 be_fs_list_data_t *fld = data;
1405 const char *fs_name = zfs_get_name(zhp);
1406 char zhp_mountpoint[MAXPATHLEN];
1407 char mountpoint[MAXPATHLEN];
1408 int ret = 0;
1409
1410 /* Get this dataset's mountpoint property */
1411 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1412 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1413 be_print_err(gettext("be_get_legacy_fs_callback: "
1414 "failed to get mountpoint for %s: %s\n"),
1415 fs_name, libzfs_error_description(g_zfs));
1416 ret = zfs_err_to_be_err(g_zfs);
1417 ZFS_CLOSE(zhp);
1418 return (ret);
1419 }
1420
1421 /*
1422 * If mountpoint is legacy, try to get its mountpoint from this BE's
1423 * vfstab. If it exists in the vfstab, add this file system to the
1424 * callback data.
1425 */
1426 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1427 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1428 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1429 be_print_err(gettext("be_get_legacy_fs_callback: "
1430 "no entry for %s in vfstab, "
1431 "skipping ...\n"), fs_name);
1432
1433 goto next;
1434 }
1435
1436 /* Record file system into the callback data. */
1437 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1438 be_print_err(gettext("be_get_legacy_fs_callback: "
1439 "failed to add %s to fs list\n"), mountpoint);
1440 ZFS_CLOSE(zhp);
1441 return (BE_ERR_NOMEM);
1442 }
1443 }
1444
1445 next:
1446 /* Iterate through this dataset's children file systems */
1447 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1448 fld)) != 0) {
1449 ZFS_CLOSE(zhp);
1450 return (ret);
1451 }
1452 ZFS_CLOSE(zhp);
1453 return (0);
1454 }
1455
1456 /*
1457 * Function: add_to_fs_list
1458 * Description: Function used to add a file system to the fs_list array in
1459 * a be_fs_list_data_t structure.
1460 * Parameters:
1461 * fld - be_fs_list_data_t pointer
1462 * fs - file system to add
1463 * Returns:
1464 * BE_SUCCESS - Success
1465 * 1 - Failure
1466 * Scope:
1467 * Private
1468 */
1469 static int
add_to_fs_list(be_fs_list_data_t * fld,const char * fs)1470 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1471 {
1472 if (fld == NULL || fs == NULL)
1473 return (1);
1474
1475 if ((fld->fs_list = (char **)realloc(fld->fs_list,
1476 sizeof (char *)*(fld->fs_num + 1))) == NULL) {
1477 be_print_err(gettext("add_to_fs_list: "
1478 "memory allocation failed\n"));
1479 return (1);
1480 }
1481
1482 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1483 be_print_err(gettext("add_to_fs_list: "
1484 "memory allocation failed\n"));
1485 return (1);
1486 }
1487
1488 return (BE_SUCCESS);
1489 }
1490
1491 /*
1492 * Function: zpool_shared_fs_callback
1493 * Description: Callback function used to iterate through all existing pools
1494 * to find and mount all shared filesystems. This function
1495 * processes the pool's "pool data" dataset, then uses
1496 * iter_shared_fs_callback to iterate through the pool's
1497 * datasets.
1498 * Parameters:
1499 * zlp - zpool_handle_t pointer to the current pool being
1500 * looked at.
1501 * data - be_mount_data_t pointer
1502 * Returns:
1503 * 0 - Success
1504 * be_errno_t - Failure
1505 * Scope:
1506 * Private
1507 */
1508 static int
zpool_shared_fs_callback(zpool_handle_t * zlp,void * data)1509 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1510 {
1511 be_mount_data_t *md = data;
1512 zfs_handle_t *zhp = NULL;
1513 const char *zpool = zpool_get_name(zlp);
1514 int ret = 0;
1515
1516 /*
1517 * Get handle to pool's "pool data" dataset
1518 */
1519 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1520 be_print_err(gettext("zpool_shared_fs: "
1521 "failed to open pool dataset %s: %s\n"), zpool,
1522 libzfs_error_description(g_zfs));
1523 ret = zfs_err_to_be_err(g_zfs);
1524 zpool_close(zlp);
1525 return (ret);
1526 }
1527
1528 /* Process this pool's "pool data" dataset */
1529 (void) loopback_mount_shared_fs(zhp, md);
1530
1531 /* Interate through this pool's children */
1532 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1533
1534 ZFS_CLOSE(zhp);
1535 zpool_close(zlp);
1536
1537 return (0);
1538 }
1539
1540 /*
1541 * Function: iter_shared_fs_callback
1542 * Description: Callback function used to iterate through a pool's datasets
1543 * to find and mount all shared filesystems. It makes sure to
1544 * find the BE container dataset of the pool, if it exists, and
1545 * does not process and iterate down that path.
1546 *
1547 * Note - This function iterates linearly down the
1548 * hierarchical dataset paths and mounts things as it goes
1549 * along. It does not make sure that something deeper down
1550 * a dataset path has an interim mountpoint for something
1551 * processed earlier.
1552 *
1553 * Parameters:
1554 * zhp - zfs_handle_t pointer to the current dataset being
1555 * processed.
1556 * data - be_mount_data_t pointer
1557 * Returns:
1558 * 0 - Success
1559 * be_errno_t - Failure
1560 * Scope:
1561 * Private
1562 */
1563 static int
iter_shared_fs_callback(zfs_handle_t * zhp,void * data)1564 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1565 {
1566 be_mount_data_t *md = data;
1567 const char *name = zfs_get_name(zhp);
1568 char container_ds[MAXPATHLEN];
1569 char tmp_name[MAXPATHLEN];
1570 char *pool;
1571
1572 /* Get the pool's name */
1573 (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1574 pool = strtok(tmp_name, "/");
1575
1576 if (pool) {
1577 /*
1578 * If what we're processing is this pool's BE container
1579 * dataset, skip it.
1580 */
1581 if (be_make_container_ds(pool, container_ds,
1582 sizeof (container_ds)) == BE_SUCCESS &&
1583 strcmp(name, container_ds) == 0) {
1584 ZFS_CLOSE(zhp);
1585 return (0);
1586 }
1587 } else {
1588 /* Getting the pool name failed, return error */
1589 be_print_err(gettext("iter_shared_fs_callback: "
1590 "failed to get pool name from %s\n"), name);
1591 ZFS_CLOSE(zhp);
1592 return (BE_ERR_POOL_NOENT);
1593 }
1594
1595 /* Mount this shared filesystem */
1596 (void) loopback_mount_shared_fs(zhp, md);
1597
1598 /* Iterate this dataset's children file systems */
1599 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1600 ZFS_CLOSE(zhp);
1601
1602 return (0);
1603 }
1604
1605 /*
1606 * Function: loopback_mount_shared_fs
1607 * Description: This function loopback mounts a file system into the altroot
1608 * area of the BE being mounted. Since these are shared file
1609 * systems, they are expected to be already mounted for the
1610 * current BE, and this function just loopback mounts them into
1611 * the BE mountpoint. If they are not mounted for the current
1612 * live system, they are skipped and not mounted into the BE
1613 * we're mounting.
1614 * Parameters:
1615 * zhp - zfs_handle_t pointer to the dataset to loopback mount
1616 * md - be_mount_data_t pointer
1617 * Returns:
1618 * BE_SUCCESS - Success
1619 * be_errno_t - Failure
1620 * Scope:
1621 * Private
1622 */
1623 static int
loopback_mount_shared_fs(zfs_handle_t * zhp,be_mount_data_t * md)1624 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1625 {
1626 char zhp_mountpoint[MAXPATHLEN];
1627 char mountpoint[MAXPATHLEN];
1628 char *mp = NULL;
1629 char optstr[MAX_MNTOPT_STR];
1630 int mflag = MS_OPTIONSTR;
1631 int err;
1632
1633 /*
1634 * Check if file system is currently mounted and not delegated
1635 * to a non-global zone (if we're in the global zone)
1636 */
1637 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1638 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1639 /*
1640 * If we didn't get a mountpoint from the zfs_is_mounted call,
1641 * get it from the mountpoint property.
1642 */
1643 if (mp == NULL) {
1644 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1645 zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1646 NULL, 0, B_FALSE) != 0) {
1647 be_print_err(
1648 gettext("loopback_mount_shared_fs: "
1649 "failed to get mountpoint property\n"));
1650 return (BE_ERR_ZFS);
1651 }
1652 } else {
1653 (void) strlcpy(zhp_mountpoint, mp,
1654 sizeof (zhp_mountpoint));
1655 free(mp);
1656 }
1657
1658 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1659 md->altroot, zhp_mountpoint);
1660
1661 /* Mount it read-only if read-write was not requested */
1662 if (!md->shared_rw) {
1663 mflag |= MS_RDONLY;
1664 }
1665
1666 /* Add the "nosub" option to the mount options string */
1667 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1668
1669 /* Loopback mount this dataset at the altroot */
1670 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1671 NULL, 0, optstr, sizeof (optstr)) != 0) {
1672 err = errno;
1673 be_print_err(gettext("loopback_mount_shared_fs: "
1674 "failed to loopback mount %s at %s: %s\n"),
1675 zhp_mountpoint, mountpoint, strerror(err));
1676 return (BE_ERR_MOUNT);
1677 }
1678 }
1679
1680 return (BE_SUCCESS);
1681 }
1682
1683 /*
1684 * Function: loopback_mount_zonepath
1685 * Description: This function loopback mounts a zonepath into the altroot
1686 * area of the BE being mounted.
1687 * Parameters:
1688 * zonepath - pointer to zone path in the current BE
1689 * md - be_mount_data_t pointer
1690 * Returns:
1691 * BE_SUCCESS - Success
1692 * be_errno_t - Failure
1693 * Scope:
1694 * Private
1695 */
1696 static int
loopback_mount_zonepath(const char * zonepath,be_mount_data_t * md)1697 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1698 {
1699 FILE *fp = (FILE *)NULL;
1700 struct stat st;
1701 char *p;
1702 char *p1;
1703 char *parent_dir;
1704 struct extmnttab extmtab;
1705 dev_t dev = NODEV;
1706 char *parentmnt;
1707 char alt_parentmnt[MAXPATHLEN];
1708 struct mnttab mntref;
1709 char altzonepath[MAXPATHLEN];
1710 char optstr[MAX_MNTOPT_STR];
1711 int mflag = MS_OPTIONSTR;
1712 int ret;
1713 int err;
1714
1715 fp = fopen(MNTTAB, "r");
1716 if (fp == NULL) {
1717 err = errno;
1718 be_print_err(gettext("loopback_mount_zonepath: "
1719 "failed to open /etc/mnttab\n"));
1720 return (errno_to_be_err(err));
1721 }
1722
1723 /*
1724 * before attempting the loopback mount of zonepath under altroot,
1725 * we need to make sure that all intermediate file systems in the
1726 * zone path are also mounted under altroot
1727 */
1728
1729 /* get the parent directory for zonepath */
1730 p = strrchr(zonepath, '/');
1731 if (p != NULL && p != zonepath) {
1732 if ((parent_dir = (char *)calloc(sizeof (char),
1733 p - zonepath + 1)) == NULL) {
1734 ret = BE_ERR_NOMEM;
1735 goto done;
1736 }
1737 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1738 if (stat(parent_dir, &st) < 0) {
1739 ret = errno_to_be_err(errno);
1740 be_print_err(gettext("loopback_mount_zonepath: "
1741 "failed to stat %s"),
1742 parent_dir);
1743 free(parent_dir);
1744 goto done;
1745 }
1746 free(parent_dir);
1747
1748 /*
1749 * After the above stat call, st.st_dev contains ID of the
1750 * device over which parent dir resides.
1751 * Now, search mnttab and find mount point of parent dir device.
1752 */
1753
1754 resetmnttab(fp);
1755 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1756 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1757 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1758 MNTTYPE_ZFS) == 0) {
1759 p1 = strchr(extmtab.mnt_special, '/');
1760 if (p1 == NULL || strncmp(p1 + 1,
1761 BE_CONTAINER_DS_NAME, 4) != 0 ||
1762 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1763 /*
1764 * if parent dir is in a shared file
1765 * system, check whether it is already
1766 * loopback mounted under altroot or
1767 * not. It would have been mounted
1768 * already under altroot if it is in
1769 * a non-shared filesystem.
1770 */
1771 parentmnt = strdup(extmtab.mnt_mountp);
1772 (void) snprintf(alt_parentmnt,
1773 sizeof (alt_parentmnt), "%s%s",
1774 md->altroot, parentmnt);
1775 mntref.mnt_mountp = alt_parentmnt;
1776 mntref.mnt_special = parentmnt;
1777 mntref.mnt_fstype = MNTTYPE_LOFS;
1778 mntref.mnt_mntopts = NULL;
1779 mntref.mnt_time = NULL;
1780 resetmnttab(fp);
1781 if (getmntany(fp, (struct mnttab *)
1782 &extmtab, &mntref) != 0) {
1783 ret = loopback_mount_zonepath(
1784 parentmnt, md);
1785 if (ret != BE_SUCCESS) {
1786 free(parentmnt);
1787 goto done;
1788 }
1789 }
1790 free(parentmnt);
1791 }
1792 break;
1793 }
1794 }
1795 }
1796
1797
1798 if (!md->shared_rw) {
1799 mflag |= MS_RDONLY;
1800 }
1801
1802 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1803 md->altroot, zonepath);
1804
1805 /* Add the "nosub" option to the mount options string */
1806 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1807
1808 /* Loopback mount this dataset at the altroot */
1809 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1810 NULL, 0, optstr, sizeof (optstr)) != 0) {
1811 err = errno;
1812 be_print_err(gettext("loopback_mount_zonepath: "
1813 "failed to loopback mount %s at %s: %s\n"),
1814 zonepath, altzonepath, strerror(err));
1815 ret = BE_ERR_MOUNT;
1816 goto done;
1817 }
1818 ret = BE_SUCCESS;
1819
1820 done :
1821 (void) fclose(fp);
1822 return (ret);
1823 }
1824
1825 /*
1826 * Function: unmount_shared_fs
1827 * Description: This function iterates through the mnttab and finds all
1828 * loopback mount entries that reside within the altroot of
1829 * where the BE is mounted, and unmounts it.
1830 * Parameters:
1831 * ud - be_unmount_data_t pointer
1832 * Returns:
1833 * BE_SUCCESS - Success
1834 * be_errno_t - Failure
1835 * Scope:
1836 * Private
1837 */
1838 static int
unmount_shared_fs(be_unmount_data_t * ud)1839 unmount_shared_fs(be_unmount_data_t *ud)
1840 {
1841 FILE *fp = NULL;
1842 struct mnttab *table = NULL;
1843 struct mnttab ent;
1844 struct mnttab *entp = NULL;
1845 size_t size = 0;
1846 int read_chunk = 32;
1847 int i;
1848 int altroot_len;
1849 int err = 0;
1850
1851 errno = 0;
1852
1853 /* Read in the mnttab into a table */
1854 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1855 err = errno;
1856 be_print_err(gettext("unmount_shared_fs: "
1857 "failed to open mnttab\n"));
1858 return (errno_to_be_err(err));
1859 }
1860
1861 while (getmntent(fp, &ent) == 0) {
1862 if (size % read_chunk == 0) {
1863 table = (struct mnttab *)realloc(table,
1864 (size + read_chunk) * sizeof (ent));
1865 }
1866 entp = &table[size++];
1867
1868 /*
1869 * Copy over the current mnttab entry into our table,
1870 * copying only the fields that we care about.
1871 */
1872 (void) memset(entp, 0, sizeof (*entp));
1873 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1874 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1875 be_print_err(gettext("unmount_shared_fs: "
1876 "memory allocation failed\n"));
1877 return (BE_ERR_NOMEM);
1878 }
1879 }
1880 (void) fclose(fp);
1881
1882 /*
1883 * Process the mnttab entries in reverse order, looking for
1884 * loopback mount entries mounted under our altroot.
1885 */
1886 altroot_len = strlen(ud->altroot);
1887 for (i = size; i > 0; i--) {
1888 entp = &table[i - 1];
1889
1890 /* If not of type lofs, skip */
1891 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1892 continue;
1893
1894 /* If inside the altroot, unmount it */
1895 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1896 entp->mnt_mountp[altroot_len] == '/') {
1897 if (umount(entp->mnt_mountp) != 0) {
1898 err = errno;
1899 if (err == EBUSY) {
1900 (void) sleep(1);
1901 err = errno = 0;
1902 if (umount(entp->mnt_mountp) != 0)
1903 err = errno;
1904 }
1905 if (err != 0) {
1906 be_print_err(gettext(
1907 "unmount_shared_fs: "
1908 "failed to unmount shared file "
1909 "system %s: %s\n"),
1910 entp->mnt_mountp, strerror(err));
1911 return (errno_to_be_err(err));
1912 }
1913 }
1914 }
1915 }
1916
1917 return (BE_SUCCESS);
1918 }
1919
1920 /*
1921 * Function: get_mountpoint_from_vfstab
1922 * Description: This function digs into the vfstab in the given altroot,
1923 * and searches for an entry for the fs passed in. If found,
1924 * it returns the mountpoint of that fs in the mountpoint
1925 * buffer passed in. If the get_alt_mountpoint flag is set,
1926 * it returns the mountpoint with the altroot prepended.
1927 * Parameters:
1928 * altroot - pointer to the alternate root location
1929 * fs - pointer to the file system name to look for in the
1930 * vfstab in altroot
1931 * mountpoint - pointer to buffer of where the mountpoint of
1932 * fs will be returned.
1933 * size_mp - size of mountpoint argument
1934 * get_alt_mountpoint - flag to indicate whether or not the
1935 * mountpoint should be populated with the altroot
1936 * prepended.
1937 * Returns:
1938 * BE_SUCCESS - Success
1939 * 1 - Failure
1940 * Scope:
1941 * Private
1942 */
1943 static int
get_mountpoint_from_vfstab(char * altroot,const char * fs,char * mountpoint,size_t size_mp,boolean_t get_alt_mountpoint)1944 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1945 size_t size_mp, boolean_t get_alt_mountpoint)
1946 {
1947 struct vfstab vp;
1948 FILE *fp = NULL;
1949 char alt_vfstab[MAXPATHLEN];
1950
1951 /* Generate path to alternate root vfstab */
1952 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1953 altroot);
1954
1955 /* Open alternate root vfstab */
1956 if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1957 be_print_err(gettext("get_mountpoint_from_vfstab: "
1958 "failed to open vfstab (%s)\n"), alt_vfstab);
1959 return (1);
1960 }
1961
1962 if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1963 /*
1964 * Found entry for fs, grab its mountpoint.
1965 * If the flag to prepend the altroot into the mountpoint
1966 * is set, prepend it. Otherwise, just return the mountpoint.
1967 */
1968 if (get_alt_mountpoint) {
1969 (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
1970 vp.vfs_mountp);
1971 } else {
1972 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
1973 }
1974 } else {
1975 (void) fclose(fp);
1976 return (1);
1977 }
1978
1979 (void) fclose(fp);
1980
1981 return (BE_SUCCESS);
1982 }
1983
1984 /*
1985 * Function: fix_mountpoint_callback
1986 * Description: This callback function is used to iterate through a BE's
1987 * children filesystems to check if its mountpoint is currently
1988 * set to be mounted at some specified altroot. If so, fix it by
1989 * removing altroot from the beginning of its mountpoint.
1990 *
1991 * Note - There's no way to tell if a child filesystem's
1992 * mountpoint isn't broken, and just happens to begin with
1993 * the altroot we're looking for. In this case, this function
1994 * will errantly remove the altroot portion from the beginning
1995 * of this filesystem's mountpoint.
1996 *
1997 * Parameters:
1998 * zhp - zfs_handle_t pointer to filesystem being processed.
1999 * data - altroot of where BE is to be mounted.
2000 * Returns:
2001 * 0 - Success
2002 * be_errno_t - Failure
2003 * Scope:
2004 * Private
2005 */
2006 static int
fix_mountpoint_callback(zfs_handle_t * zhp,void * data)2007 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
2008 {
2009 zprop_source_t sourcetype;
2010 char source[ZFS_MAX_DATASET_NAME_LEN];
2011 char mountpoint[MAXPATHLEN];
2012 char *zhp_mountpoint = NULL;
2013 char *altroot = data;
2014 int ret = 0;
2015
2016 /* Get dataset's mountpoint and source values */
2017 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2018 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2019 B_FALSE) != 0) {
2020 be_print_err(gettext("fix_mountpoint_callback: "
2021 "failed to get mountpoint and sourcetype for %s\n"),
2022 zfs_get_name(zhp));
2023 ZFS_CLOSE(zhp);
2024 return (BE_ERR_ZFS);
2025 }
2026
2027 /*
2028 * If the mountpoint is not inherited and the mountpoint is not
2029 * 'legacy', this file system potentially needs its mountpoint
2030 * fixed.
2031 */
2032 if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2033 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2034
2035 /*
2036 * Check if this file system's current mountpoint is
2037 * under the altroot we're fixing it against.
2038 */
2039 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2040 mountpoint[strlen(altroot)] == '/') {
2041
2042 /*
2043 * Get this dataset's mountpoint relative to the
2044 * altroot.
2045 */
2046 zhp_mountpoint = mountpoint + strlen(altroot);
2047
2048 /* Fix this dataset's mountpoint value */
2049 if (zfs_prop_set(zhp,
2050 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2051 zhp_mountpoint)) {
2052 be_print_err(gettext("fix_mountpoint_callback: "
2053 "failed to set mountpoint for %s to "
2054 "%s: %s\n"), zfs_get_name(zhp),
2055 zhp_mountpoint,
2056 libzfs_error_description(g_zfs));
2057 ret = zfs_err_to_be_err(g_zfs);
2058 ZFS_CLOSE(zhp);
2059 return (ret);
2060 }
2061 }
2062 }
2063
2064 /* Iterate through this dataset's children and fix them */
2065 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2066 altroot)) != 0) {
2067 ZFS_CLOSE(zhp);
2068 return (ret);
2069 }
2070
2071
2072 ZFS_CLOSE(zhp);
2073 return (0);
2074 }
2075
2076 /*
2077 * Function: be_mount_root
2078 * Description: This function mounts the root dataset of a BE at the
2079 * specified altroot.
2080 * Parameters:
2081 * zhp - zfs_handle_t pointer to root dataset of a BE that is
2082 * to be mounted at altroot.
2083 * altroot - location of where to mount the BE root.
2084 * Return:
2085 * BE_SUCCESS - Success
2086 * be_errno_t - Failure
2087 * Scope:
2088 * Private
2089 */
2090 static int
be_mount_root(zfs_handle_t * zhp,char * altroot)2091 be_mount_root(zfs_handle_t *zhp, char *altroot)
2092 {
2093 char mountpoint[MAXPATHLEN];
2094
2095 /* Get mountpoint property of dataset */
2096 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2097 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2098 be_print_err(gettext("be_mount_root: failed to "
2099 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2100 libzfs_error_description(g_zfs));
2101 return (zfs_err_to_be_err(g_zfs));
2102 }
2103
2104 /*
2105 * Set the canmount property for the BE's root dataset to 'noauto' just
2106 * in case it's been set to 'on'.
2107 */
2108 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2109 != 0) {
2110 be_print_err(gettext("be_mount_root: failed to "
2111 "set canmount property to 'noauto' (%s): %s\n"),
2112 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2113 return (zfs_err_to_be_err(g_zfs));
2114 }
2115
2116 /* Mount the BE's root filesystem */
2117 if (mount_zfs(zhp, altroot) != 0) {
2118 be_print_err(gettext("be_mount_root: failed to "
2119 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2120 altroot, strerror(errno));
2121 return (BE_ERR_ZFS);
2122 }
2123
2124 return (BE_SUCCESS);
2125 }
2126
2127 /*
2128 * Function: be_unmount_root
2129 * Description: This function unmounts the root dataset of a BE, but before
2130 * unmounting, it looks at the BE's vfstab to determine
2131 * if the root dataset mountpoint should be left as 'legacy'
2132 * or '/'. If the vfstab contains an entry for this root
2133 * dataset with a mountpoint of '/', it sets the mountpoint
2134 * property to 'legacy'.
2135 *
2136 * Parameters:
2137 * zhp - zfs_handle_t pointer of the BE root dataset that
2138 * is currently mounted.
2139 * ud - be_unmount_data_t pointer providing unmount data
2140 * for the given BE root dataset.
2141 * Returns:
2142 * BE_SUCCESS - Success
2143 * be_errno_t - Failure
2144 * Scope:
2145 * Private
2146 */
2147 static int
be_unmount_root(zfs_handle_t * zhp,be_unmount_data_t * ud)2148 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
2149 {
2150 char mountpoint[MAXPATHLEN];
2151 boolean_t is_legacy = B_FALSE;
2152
2153 /* See if this is a legacy mounted root */
2154 if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
2155 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
2156 strcmp(mountpoint, "/") == 0) {
2157 is_legacy = B_TRUE;
2158 }
2159
2160 /* Unmount the dataset */
2161 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
2162 be_print_err(gettext("be_unmount_root: failed to "
2163 "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
2164 libzfs_error_description(g_zfs));
2165 return (zfs_err_to_be_err(g_zfs));
2166 }
2167
2168 /* Set canmount property for this BE's root filesystem to noauto */
2169 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2170 != 0) {
2171 be_print_err(gettext("be_unmount_root: failed to "
2172 "set canmount property for %s to 'noauto': %s\n"),
2173 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2174 return (zfs_err_to_be_err(g_zfs));
2175 }
2176
2177 /*
2178 * Set mountpoint for BE's root dataset back to '/', or 'legacy'
2179 * if its a legacy mounted root.
2180 */
2181 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2182 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
2183 be_print_err(gettext("be_unmount_root: failed to "
2184 "set mountpoint of %s to %s\n"), zfs_get_name(zhp),
2185 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
2186 return (zfs_err_to_be_err(g_zfs));
2187 }
2188
2189 return (BE_SUCCESS);
2190 }
2191
2192 /*
2193 * Function: fix_mountpoint
2194 * Description: This function checks the mountpoint of an unmounted BE to make
2195 * sure that it is set to either 'legacy' or '/'. If it's not,
2196 * then we're in a situation where an unmounted BE has some random
2197 * mountpoint set for it. (This could happen if the system was
2198 * rebooted while an inactive BE was mounted). This function
2199 * attempts to fix its mountpoints.
2200 * Parameters:
2201 * zhp - zfs_handle_t pointer to root dataset of the BE
2202 * whose mountpoint needs to be checked.
2203 * Return:
2204 * BE_SUCCESS - Success
2205 * be_errno_t - Failure
2206 * Scope:
2207 * Private
2208 */
2209 static int
fix_mountpoint(zfs_handle_t * zhp)2210 fix_mountpoint(zfs_handle_t *zhp)
2211 {
2212 be_unmount_data_t ud = { 0 };
2213 char *altroot = NULL;
2214 char mountpoint[MAXPATHLEN];
2215 int ret = BE_SUCCESS;
2216
2217 /*
2218 * Record what this BE's root dataset mountpoint property is currently
2219 * set to.
2220 */
2221 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2222 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2223 be_print_err(gettext("fix_mountpoint: failed to get "
2224 "mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
2225 libzfs_error_description(g_zfs));
2226 return (BE_ERR_ZFS);
2227 }
2228
2229 /*
2230 * If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
2231 */
2232 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2233 strcmp(mountpoint, "/") == 0) {
2234 return (BE_SUCCESS);
2235 }
2236
2237 /*
2238 * Iterate through this BE's children datasets and fix
2239 * them if they need fixing.
2240 */
2241 if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
2242 != 0) {
2243 return (BE_ERR_ZFS);
2244 }
2245
2246 /*
2247 * The process of mounting and unmounting the root file system
2248 * will fix its mountpoint to correctly be either 'legacy' or '/'
2249 * since be_unmount_root will do the right thing by looking at
2250 * its vfstab.
2251 */
2252
2253 /* Generate temporary altroot to mount the root file system */
2254 if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
2255 be_print_err(gettext("fix_mountpoint: failed to "
2256 "make temporary mountpoint\n"));
2257 return (ret);
2258 }
2259
2260 /* Mount and unmount the root. */
2261 if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
2262 be_print_err(gettext("fix_mountpoint: failed to "
2263 "mount BE root file system\n"));
2264 goto cleanup;
2265 }
2266 ud.altroot = altroot;
2267 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
2268 be_print_err(gettext("fix_mountpoint: failed to "
2269 "unmount BE root file system\n"));
2270 goto cleanup;
2271 }
2272
2273 cleanup:
2274 free(altroot);
2275
2276 return (ret);
2277 }
2278
2279 /*
2280 * Function: be_mount_zones
2281 * Description: This function finds all supported non-global zones in the
2282 * given global BE and mounts them with respect to where the
2283 * global BE is currently mounted. The global BE datasets
2284 * (including its shared datasets) are expected to already
2285 * be mounted.
2286 * Parameters:
2287 * be_zhp - zfs_handle_t pointer to the root dataset of the
2288 * global BE.
2289 * md - be_mount_data_t pointer to data for global BE.
2290 * Returns:
2291 * BE_SUCCESS - Success
2292 * be_errno_t - Failure
2293 * Scope:
2294 * Private
2295 */
2296 static int
be_mount_zones(zfs_handle_t * be_zhp,be_mount_data_t * md)2297 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
2298 {
2299 zoneBrandList_t *brands = NULL;
2300 zoneList_t zlst = NULL;
2301 char *zonename = NULL;
2302 char *zonepath = NULL;
2303 char *zonepath_ds = NULL;
2304 int k;
2305 int ret = BE_SUCCESS;
2306
2307 z_set_zone_root(md->altroot);
2308
2309 if ((brands = be_get_supported_brandlist()) == NULL) {
2310 be_print_err(gettext("be_mount_zones: "
2311 "no supported brands\n"));
2312 return (BE_SUCCESS);
2313 }
2314
2315 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2316 if (zlst == NULL) {
2317 z_free_brand_list(brands);
2318 return (BE_SUCCESS);
2319 }
2320
2321 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2322 if (z_zlist_get_current_state(zlst, k) ==
2323 ZONE_STATE_INSTALLED) {
2324 zonepath = z_zlist_get_zonepath(zlst, k);
2325
2326 /*
2327 * Get the dataset of this zonepath in current BE.
2328 * If its not a dataset, skip it.
2329 */
2330 if ((zonepath_ds = be_get_ds_from_dir(zonepath))
2331 == NULL)
2332 continue;
2333
2334 /*
2335 * Check if this zone is supported based on
2336 * the dataset of its zonepath
2337 */
2338 if (!be_zone_supported(zonepath_ds)) {
2339 free(zonepath_ds);
2340 zonepath_ds = NULL;
2341 continue;
2342 }
2343
2344 /*
2345 * if BE's shared file systems are already mounted,
2346 * zone path dataset would have already been lofs
2347 * mounted under altroot. Otherwise, we need to do
2348 * it here.
2349 */
2350 if (!md->shared_fs) {
2351 ret = loopback_mount_zonepath(zonepath, md);
2352 if (ret != BE_SUCCESS)
2353 goto done;
2354 }
2355
2356
2357 /* Mount this zone */
2358 ret = be_mount_one_zone(be_zhp, md, zonename,
2359 zonepath, zonepath_ds);
2360
2361 free(zonepath_ds);
2362 zonepath_ds = NULL;
2363
2364 if (ret != BE_SUCCESS) {
2365 be_print_err(gettext("be_mount_zones: "
2366 "failed to mount zone %s under "
2367 "altroot %s\n"), zonename, md->altroot);
2368 goto done;
2369 }
2370 }
2371 }
2372
2373 done:
2374 z_free_brand_list(brands);
2375 z_free_zone_list(zlst);
2376 /*
2377 * libinstzones caches mnttab and uses cached version for resolving lofs
2378 * mounts when we call z_resolve_lofs. It creates the cached version
2379 * when the first call to z_resolve_lofs happens. So, library's cached
2380 * mnttab doesn't contain entries for lofs mounts created in the above
2381 * loop. Because of this, subsequent calls to z_resolve_lofs would fail
2382 * to resolve these lofs mounts. So, here we destroy library's cached
2383 * mnttab to force its recreation when the next call to z_resolve_lofs
2384 * happens.
2385 */
2386 z_destroyMountTable();
2387 return (ret);
2388 }
2389
2390 /*
2391 * Function: be_unmount_zones
2392 * Description: This function finds all supported non-global zones in the
2393 * given mounted global BE and unmounts them.
2394 * Parameters:
2395 * ud - unmount_data_t pointer data for the global BE.
2396 * Returns:
2397 * BE_SUCCESS - Success
2398 * be_errno_t - Failure
2399 * Scope:
2400 * Private
2401 */
2402 static int
be_unmount_zones(be_unmount_data_t * ud)2403 be_unmount_zones(be_unmount_data_t *ud)
2404 {
2405 zoneBrandList_t *brands = NULL;
2406 zoneList_t zlst = NULL;
2407 char *zonename = NULL;
2408 char *zonepath = NULL;
2409 char alt_zonepath[MAXPATHLEN];
2410 char *zonepath_ds = NULL;
2411 int k;
2412 int ret = BE_SUCCESS;
2413
2414 z_set_zone_root(ud->altroot);
2415
2416 if ((brands = be_get_supported_brandlist()) == NULL) {
2417 be_print_err(gettext("be_unmount_zones: "
2418 "no supported brands\n"));
2419 return (BE_SUCCESS);
2420 }
2421
2422 zlst = z_get_nonglobal_zone_list_by_brand(brands);
2423 if (zlst == NULL) {
2424 z_free_brand_list(brands);
2425 return (BE_SUCCESS);
2426 }
2427
2428 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
2429 if (z_zlist_get_current_state(zlst, k) ==
2430 ZONE_STATE_INSTALLED) {
2431 zonepath = z_zlist_get_zonepath(zlst, k);
2432
2433 /* Build zone's zonepath wrt the global BE altroot */
2434 (void) snprintf(alt_zonepath, sizeof (alt_zonepath),
2435 "%s%s", ud->altroot, zonepath);
2436
2437 /*
2438 * Get the dataset of this zonepath. If its not
2439 * a dataset, skip it.
2440 */
2441 if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
2442 == NULL)
2443 continue;
2444
2445 /*
2446 * Check if this zone is supported based on the
2447 * dataset of its zonepath.
2448 */
2449 if (!be_zone_supported(zonepath_ds)) {
2450 free(zonepath_ds);
2451 zonepath_ds = NULL;
2452 continue;
2453 }
2454
2455 /* Unmount this zone */
2456 ret = be_unmount_one_zone(ud, zonename, zonepath,
2457 zonepath_ds);
2458
2459 free(zonepath_ds);
2460 zonepath_ds = NULL;
2461
2462 if (ret != BE_SUCCESS) {
2463 be_print_err(gettext("be_unmount_zones:"
2464 " failed to unmount zone %s from "
2465 "altroot %s\n"), zonename, ud->altroot);
2466 goto done;
2467 }
2468 }
2469 }
2470
2471 done:
2472 z_free_brand_list(brands);
2473 z_free_zone_list(zlst);
2474 return (ret);
2475 }
2476
2477 /*
2478 * Function: be_mount_one_zone
2479 * Description: This function is called to mount one zone for a given
2480 * global BE.
2481 * Parameters:
2482 * be_zhp - zfs_handle_t pointer to the root dataset of the
2483 * global BE
2484 * md - be_mount_data_t pointer to data for global BE
2485 * zonename - name of zone to mount
2486 * zonepath - zonepath of zone to mount
2487 * zonepath_ds - dataset for the zonepath
2488 * Returns:
2489 * BE_SUCCESS - Success
2490 * be_errno_t - Failure
2491 * Scope:
2492 * Private
2493 */
2494 static int
be_mount_one_zone(zfs_handle_t * be_zhp,be_mount_data_t * md,char * zonename,char * zonepath,char * zonepath_ds)2495 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
2496 char *zonepath, char *zonepath_ds)
2497 {
2498 be_mount_data_t zone_md = { 0 };
2499 zfs_handle_t *zone_zhp = NULL;
2500 char zone_altroot[MAXPATHLEN];
2501 char zoneroot[MAXPATHLEN];
2502 char zoneroot_ds[MAXPATHLEN];
2503 int ret = BE_SUCCESS;
2504
2505 /* Find the active zone root dataset for this zone for this BE */
2506 if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
2507 sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
2508 be_print_err(gettext("be_mount_one_zone: did not "
2509 "find active zone root for zone %s, skipping ...\n"),
2510 zonename);
2511 return (BE_SUCCESS);
2512 } else if (ret != BE_SUCCESS) {
2513 be_print_err(gettext("be_mount_one_zone: failed to "
2514 "find active zone root for zone %s\n"), zonename);
2515 return (ret);
2516 }
2517
2518 /* Get handle to active zoneroot dataset */
2519 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2520 == NULL) {
2521 be_print_err(gettext("be_mount_one_zone: failed to "
2522 "open zone root dataset (%s): %s\n"), zoneroot_ds,
2523 libzfs_error_description(g_zfs));
2524 return (zfs_err_to_be_err(g_zfs));
2525 }
2526
2527 /* Generate string for zone's altroot path */
2528 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2529 (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
2530 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2531
2532 /* Build mount_data for the zone */
2533 zone_md.altroot = zone_altroot;
2534 zone_md.shared_fs = md->shared_fs;
2535 zone_md.shared_rw = md->shared_rw;
2536
2537 /* Mount the zone's root file system */
2538 if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
2539 be_print_err(gettext("be_mount_one_zone: failed to "
2540 "mount zone root file system at %s\n"), zone_altroot);
2541 goto done;
2542 }
2543
2544 /* Iterate through zone's children filesystems */
2545 if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
2546 zone_altroot)) != 0) {
2547 be_print_err(gettext("be_mount_one_zone: failed to "
2548 "mount zone subordinate file systems at %s\n"),
2549 zone_altroot);
2550 goto done;
2551 }
2552
2553 /* TODO: Mount all shared file systems for this zone */
2554
2555 done:
2556 ZFS_CLOSE(zone_zhp);
2557 return (ret);
2558 }
2559
2560 /*
2561 * Function: be_unmount_one_zone
2562 * Description: This function unmount one zone for a give global BE.
2563 * Parameters:
2564 * ud - be_unmount_data_t pointer to data for global BE
2565 * zonename - name of zone to unmount
2566 * zonepath - zonepath of the zone to unmount
2567 * zonepath_ds - dataset for the zonepath
2568 * Returns:
2569 * BE_SUCCESS - Success
2570 * be_errno_t - Failure
2571 * Scope:
2572 * Private
2573 */
2574 static int
be_unmount_one_zone(be_unmount_data_t * ud,char * zonename,char * zonepath,char * zonepath_ds)2575 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
2576 char *zonepath_ds)
2577 {
2578 be_unmount_data_t zone_ud = { 0 };
2579 zfs_handle_t *zone_zhp = NULL;
2580 char zone_altroot[MAXPATHLEN];
2581 char zoneroot[MAXPATHLEN];
2582 char zoneroot_ds[MAXPATHLEN];
2583 int ret = BE_SUCCESS;
2584
2585 /* Generate string for zone's alternate root path */
2586 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
2587 (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
2588 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
2589
2590 /* Build be_unmount_data for zone */
2591 zone_ud.altroot = zone_altroot;
2592 zone_ud.force = ud->force;
2593
2594 /* Find the mounted zone root dataset for this zone for this BE */
2595 if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
2596 zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
2597 be_print_err(gettext("be_unmount_one_zone: did not "
2598 "find any zone root mounted for zone %s\n"), zonename);
2599 return (BE_SUCCESS);
2600 } else if (ret != BE_SUCCESS) {
2601 be_print_err(gettext("be_unmount_one_zone: failed to "
2602 "find mounted zone root for zone %s\n"), zonename);
2603 return (ret);
2604 }
2605
2606 /* Get handle to zoneroot dataset mounted for this BE */
2607 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
2608 == NULL) {
2609 be_print_err(gettext("be_unmount_one_zone: failed to "
2610 "open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
2611 libzfs_error_description(g_zfs));
2612 return (zfs_err_to_be_err(g_zfs));
2613 }
2614
2615 /* TODO: Unmount all shared file systems for this zone */
2616
2617 /* Iterate through zone's children filesystems and unmount them */
2618 if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
2619 &zone_ud)) != 0) {
2620 be_print_err(gettext("be_unmount_one_zone: failed to "
2621 "unmount zone subordinate file systems at %s\n"),
2622 zone_altroot);
2623 goto done;
2624 }
2625
2626 /* Unmount the zone's root filesystem */
2627 if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
2628 be_print_err(gettext("be_unmount_one_zone: failed to "
2629 "unmount zone root file system at %s\n"), zone_altroot);
2630 goto done;
2631 }
2632
2633 done:
2634 ZFS_CLOSE(zone_zhp);
2635 return (ret);
2636 }
2637
2638 /*
2639 * Function: be_get_ds_from_dir_callback
2640 * Description: This is a callback function used to iterate all datasets
2641 * to find the one that is currently mounted at the directory
2642 * being searched for. If matched, the name of the dataset is
2643 * returned in heap storage, so the caller is responsible for
2644 * freeing it.
2645 * Parameters:
2646 * zhp - zfs_handle_t pointer to current dataset being processed.
2647 * data - dir_data_t pointer providing name of directory being
2648 * searched for.
2649 * Returns:
2650 * 1 - This dataset is mounted at directory being searched for.
2651 * 0 - This dataset is not mounted at directory being searched for.
2652 * Scope:
2653 * Private
2654 */
2655 static int
be_get_ds_from_dir_callback(zfs_handle_t * zhp,void * data)2656 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2657 {
2658 dir_data_t *dd = data;
2659 char *mp = NULL;
2660 int zret = 0;
2661
2662 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2663 ZFS_CLOSE(zhp);
2664 return (0);
2665 }
2666
2667 if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2668 strcmp(mp, dd->dir) == 0) {
2669 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2670 be_print_err(gettext("be_get_ds_from_dir_callback: "
2671 "memory allocation failed\n"));
2672 ZFS_CLOSE(zhp);
2673 return (0);
2674 }
2675 ZFS_CLOSE(zhp);
2676 return (1);
2677 }
2678
2679 zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2680
2681 ZFS_CLOSE(zhp);
2682
2683 return (zret);
2684 }
2685
2686 /*
2687 * Function: mount_zfs
2688 * Description: This is a function to mount zfs filesystem to alternative
2689 * root without changing zfs mountpoint property. Logic is
2690 * similar to zfs_mount.
2691 * Parameters:
2692 * zhp - zfs_handle_t pointer to current dataset being processed.
2693 * altroot - char pointer to current alternative root.
2694 * Returns:
2695 * BE_SUCCESS - Success
2696 * be_errno_t - Failure
2697 * Scope:
2698 * Private
2699 */
2700 static int
mount_zfs(zfs_handle_t * zhp,char * altroot)2701 mount_zfs(zfs_handle_t *zhp, char *altroot)
2702 {
2703 int flags = 0;
2704 char mountpoint[MAXPATHLEN];
2705 char real_mountpoint[MAXPATHLEN];
2706 char source[MAXNAMELEN];
2707 char optstr[MAX_MNTOPT_STR];
2708 zprop_source_t sourcetype;
2709 struct stat buf;
2710
2711 optstr[0] = '\0';
2712
2713 /* Get dataset's mountpoint and source values */
2714 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2715 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2716 B_FALSE) != 0) {
2717 be_print_err(gettext("mount_zfs: "
2718 "failed to get mountpoint and sourcetype for %s\n"),
2719 zfs_get_name(zhp));
2720 ZFS_CLOSE(zhp);
2721 return (BE_ERR_ZFS);
2722 }
2723
2724 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
2725 strcmp(mountpoint, "/") == 0) {
2726 /*
2727 * We are called only from be_mount_root or be_mount_callback
2728 * when mountpoint != LEGACY
2729 */
2730 mountpoint[0] = '\0';
2731 }
2732
2733 (void) snprintf(real_mountpoint, MAXPATHLEN, "%s%s", altroot,
2734 mountpoint);
2735
2736 if (zpool_get_prop_int(zfs_get_pool_handle(zhp), ZPOOL_PROP_READONLY,
2737 NULL))
2738 flags |= MS_RDONLY;
2739
2740 /* Create the directory if it doesn't already exist */
2741 if (lstat(real_mountpoint, &buf) != 0) {
2742 if (mkdirp(real_mountpoint, 0755) != 0) {
2743 be_print_err(gettext("mount_zfs: "
2744 "failed to create mountpoint for %s\n"),
2745 zfs_get_name(zhp));
2746 ZFS_CLOSE(zhp);
2747 return (BE_ERR_ZFS);
2748 }
2749 }
2750
2751 if (mount(zfs_get_name(zhp), real_mountpoint, MS_OPTIONSTR | flags,
2752 MNTTYPE_ZFS, NULL, 0, optstr, sizeof (optstr))) {
2753 be_print_err(gettext("mount_zfs: failed to "
2754 "mount dataset %s at %s\n"), zfs_get_name(zhp),
2755 real_mountpoint);
2756 return (BE_ERR_ZFS);
2757 }
2758
2759 return (BE_SUCCESS);
2760 }
2761