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 */
25
26 /*
27 * System includes
28 */
29 #include <assert.h>
30 #include <errno.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libzfs.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/mntent.h>
38 #include <sys/mnttab.h>
39 #include <sys/mount.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/vfstab.h>
43 #include <sys/zone.h>
44 #include <sys/mkdev.h>
45 #include <unistd.h>
46
47 #include <libbe.h>
48 #include <libbe_priv.h>
49
50 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
51
52 typedef struct dir_data {
53 char *dir;
54 char *ds;
55 } dir_data_t;
56
57 /* Private function prototypes */
58 static int be_mount_callback(zfs_handle_t *, void *);
59 static int be_unmount_callback(zfs_handle_t *, void *);
60 static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
61 static int fix_mountpoint(zfs_handle_t *);
62 static int fix_mountpoint_callback(zfs_handle_t *, void *);
63 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
64 boolean_t);
65 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
66 static int loopback_mount_zonepath(const char *, be_mount_data_t *);
67 static int iter_shared_fs_callback(zfs_handle_t *, void *);
68 static int zpool_shared_fs_callback(zpool_handle_t *, void *);
69 static int unmount_shared_fs(be_unmount_data_t *);
70 static int add_to_fs_list(be_fs_list_data_t *, const char *);
71 static int be_mount_root(zfs_handle_t *, char *);
72 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
73 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
74 static int be_unmount_zones(be_unmount_data_t *);
75 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
76 char *);
77 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
78 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
79
80
81 /* ******************************************************************** */
82 /* Public Functions */
83 /* ******************************************************************** */
84
85 /*
86 * Function: be_mount
87 * Description: Mounts a BE and its subordinate datasets at a given mountpoint.
88 * Parameters:
89 * be_attrs - pointer to nvlist_t of attributes being passed in.
90 * The following attributes are used by this function:
91 *
92 * BE_ATTR_ORIG_BE_NAME *required
93 * BE_ATTR_MOUNTPOINT *required
94 * BE_ATTR_MOUNT_FLAGS *optional
95 * Return:
96 * BE_SUCCESS - Success
97 * be_errno_t - Failure
98 * Scope:
99 * Public
100 */
101 int
be_mount(nvlist_t * be_attrs)102 be_mount(nvlist_t *be_attrs)
103 {
104 char *be_name = NULL;
105 char *mountpoint = NULL;
106 uint16_t flags = 0;
107 int ret = BE_SUCCESS;
108
109 /* Initialize libzfs handle */
110 if (!be_zfs_init())
111 return (BE_ERR_INIT);
112
113 /* Get original BE name */
114 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
115 != 0) {
116 be_print_err(gettext("be_mount: failed to lookup "
117 "BE_ATTR_ORIG_BE_NAME attribute\n"));
118 return (BE_ERR_INVAL);
119 }
120
121 /* Validate original BE name */
122 if (!be_valid_be_name(be_name)) {
123 be_print_err(gettext("be_mount: invalid BE name %s\n"),
124 be_name);
125 return (BE_ERR_INVAL);
126 }
127
128 /* Get mountpoint */
129 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
130 != 0) {
131 be_print_err(gettext("be_mount: failed to lookup "
132 "BE_ATTR_MOUNTPOINT attribute\n"));
133 return (BE_ERR_INVAL);
134 }
135
136 /* Get flags */
137 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
138 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
139 be_print_err(gettext("be_mount: failed to lookup "
140 "BE_ATTR_MOUNT_FLAGS attribute\n"));
141 return (BE_ERR_INVAL);
142 }
143
144 ret = _be_mount(be_name, &mountpoint, flags);
145
146 be_zfs_fini();
147
148 return (ret);
149 }
150
151 /*
152 * Function: be_unmount
153 * Description: Unmounts a BE and its subordinate datasets.
154 * Parameters:
155 * be_attrs - pointer to nvlist_t of attributes being passed in.
156 * The following attributes are used by this function:
157 *
158 * BE_ATTR_ORIG_BE_NAME *required
159 * BE_ATTR_UNMOUNT_FLAGS *optional
160 * Return:
161 * BE_SUCCESS - Success
162 * be_errno_t - Failure
163 * Scope:
164 * Public
165 */
166 int
be_unmount(nvlist_t * be_attrs)167 be_unmount(nvlist_t *be_attrs)
168 {
169 char *be_name = NULL;
170 uint16_t flags = 0;
171 int ret = BE_SUCCESS;
172
173 /* Initialize libzfs handle */
174 if (!be_zfs_init())
175 return (BE_ERR_INIT);
176
177 /* Get original BE name */
178 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
179 != 0) {
180 be_print_err(gettext("be_unmount: failed to lookup "
181 "BE_ATTR_ORIG_BE_NAME attribute\n"));
182 return (BE_ERR_INVAL);
183 }
184
185 /* Validate original BE name */
186 if (!be_valid_be_name(be_name)) {
187 be_print_err(gettext("be_unmount: invalid BE name %s\n"),
188 be_name);
189 return (BE_ERR_INVAL);
190 }
191
192 /* Get unmount flags */
193 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
194 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
195 be_print_err(gettext("be_unmount: failed to loookup "
196 "BE_ATTR_UNMOUNT_FLAGS attribute\n"));
197 return (BE_ERR_INVAL);
198 }
199
200 ret = _be_unmount(be_name, flags);
201
202 be_zfs_fini();
203
204 return (ret);
205 }
206
207 /* ******************************************************************** */
208 /* Semi-Private Functions */
209 /* ******************************************************************** */
210
211 /*
212 * Function: _be_mount
213 * Description: Mounts a BE. If the altroot is not provided, this function
214 * will generate a temporary mountpoint to mount the BE at. It
215 * will return this temporary mountpoint to the caller via the
216 * altroot reference pointer passed in. This returned value is
217 * allocated on heap storage and is the repsonsibility of the
218 * caller to free.
219 * Parameters:
220 * be_name - pointer to name of BE to mount.
221 * altroot - reference pointer to altroot of where to mount BE.
222 * flags - flag indicating special handling for mounting the BE
223 * Return:
224 * BE_SUCCESS - Success
225 * be_errno_t - Failure
226 * Scope:
227 * Semi-private (library wide use only)
228 */
229 int
_be_mount(char * be_name,char ** altroot,int flags)230 _be_mount(char *be_name, char **altroot, int flags)
231 {
232 be_transaction_data_t bt = { 0 };
233 be_mount_data_t md = { 0 };
234 zfs_handle_t *zhp;
235 char obe_root_ds[MAXPATHLEN];
236 char *mp = NULL;
237 char *tmp_altroot = NULL;
238 int ret = BE_SUCCESS, err = 0;
239 uuid_t uu = { 0 };
240 boolean_t gen_tmp_altroot = B_FALSE;
241
242 if (be_name == NULL || altroot == NULL)
243 return (BE_ERR_INVAL);
244
245 /* Set be_name as obe_name in bt structure */
246 bt.obe_name = be_name;
247
248 /* Find which zpool obe_name lives in */
249 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
250 be_print_err(gettext("be_mount: failed to "
251 "find zpool for BE (%s)\n"), bt.obe_name);
252 return (BE_ERR_BE_NOENT);
253 } else if (err < 0) {
254 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
255 libzfs_error_description(g_zfs));
256 return (zfs_err_to_be_err(g_zfs));
257 }
258
259 /* Generate string for obe_name's root dataset */
260 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
261 sizeof (obe_root_ds));
262 bt.obe_root_ds = obe_root_ds;
263
264 /* Get handle to BE's root dataset */
265 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
266 NULL) {
267 be_print_err(gettext("be_mount: failed to "
268 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
269 libzfs_error_description(g_zfs));
270 return (zfs_err_to_be_err(g_zfs));
271 }
272
273 /* Make sure BE's root dataset isn't already mounted somewhere */
274 if (zfs_is_mounted(zhp, &mp)) {
275 ZFS_CLOSE(zhp);
276 be_print_err(gettext("be_mount: %s is already mounted "
277 "at %s\n"), bt.obe_name, mp != NULL ? mp : "");
278 free(mp);
279 return (BE_ERR_MOUNTED);
280 }
281
282 /*
283 * Fix this BE's mountpoint if its root dataset isn't set to
284 * either 'legacy' or '/'.
285 */
286 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
287 be_print_err(gettext("be_mount: mountpoint check "
288 "failed for %s\n"), bt.obe_root_ds);
289 ZFS_CLOSE(zhp);
290 return (ret);
291 }
292
293 /*
294 * If altroot not provided, create a temporary alternate root
295 * to mount on
296 */
297 if (*altroot == NULL) {
298 if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
299 != BE_SUCCESS) {
300 be_print_err(gettext("be_mount: failed to "
301 "make temporary mountpoint\n"));
302 ZFS_CLOSE(zhp);
303 return (ret);
304 }
305 gen_tmp_altroot = B_TRUE;
306 } else {
307 tmp_altroot = *altroot;
308 }
309
310 /* Mount the BE's root file system */
311 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
312 be_print_err(gettext("be_mount: failed to "
313 "mount BE root file system\n"));
314 if (gen_tmp_altroot)
315 free(tmp_altroot);
316 ZFS_CLOSE(zhp);
317 return (ret);
318 }
319
320 /* Iterate through BE's children filesystems */
321 if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
322 tmp_altroot)) != 0) {
323 be_print_err(gettext("be_mount: failed to "
324 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
325 if (gen_tmp_altroot)
326 free(tmp_altroot);
327 ZFS_CLOSE(zhp);
328 return (err);
329 }
330
331 md.altroot = tmp_altroot;
332 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
333 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
334
335 /*
336 * Mount shared file systems if mount flag says so.
337 */
338 if (md.shared_fs) {
339 /*
340 * Mount all ZFS file systems not under the BE's root dataset
341 */
342 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
343
344 /* TODO: Mount all non-ZFS file systems - Not supported yet */
345 }
346
347 /*
348 * If we're in the global zone and the global zone has a valid uuid,
349 * mount all supported non-global zones.
350 */
351 if (getzoneid() == GLOBAL_ZONEID &&
352 !(flags & BE_MOUNT_FLAG_NO_ZONES) &&
353 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
354 if ((ret = be_mount_zones(zhp, &md)) != BE_SUCCESS) {
355 (void) _be_unmount(bt.obe_name, 0);
356 if (gen_tmp_altroot)
357 free(tmp_altroot);
358 ZFS_CLOSE(zhp);
359 return (ret);
360 }
361 }
362
363 ZFS_CLOSE(zhp);
364
365 /*
366 * If a NULL altroot was passed in, pass the generated altroot
367 * back to the caller in altroot.
368 */
369 if (gen_tmp_altroot)
370 *altroot = tmp_altroot;
371
372 return (BE_SUCCESS);
373 }
374
375 /*
376 * Function: _be_unmount
377 * Description: Unmount a BE.
378 * Parameters:
379 * be_name - pointer to name of BE to unmount.
380 * flags - flags for unmounting the BE.
381 * Returns:
382 * BE_SUCCESS - Success
383 * be_errno_t - Failure
384 * Scope:
385 * Semi-private (library wide use only)
386 */
387 int
_be_unmount(char * be_name,int flags)388 _be_unmount(char *be_name, int flags)
389 {
390 be_transaction_data_t bt = { 0 };
391 be_unmount_data_t ud = { 0 };
392 zfs_handle_t *zhp;
393 uuid_t uu = { 0 };
394 char obe_root_ds[MAXPATHLEN];
395 char mountpoint[MAXPATHLEN];
396 char *mp = NULL;
397 int ret = BE_SUCCESS;
398 int zret = 0;
399
400 if (be_name == NULL)
401 return (BE_ERR_INVAL);
402
403 /* Set be_name as obe_name in bt structure */
404 bt.obe_name = be_name;
405
406 /* Find which zpool obe_name lives in */
407 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
408 be_print_err(gettext("be_unmount: failed to "
409 "find zpool for BE (%s)\n"), bt.obe_name);
410 return (BE_ERR_BE_NOENT);
411 } else if (zret < 0) {
412 be_print_err(gettext("be_unmount: "
413 "zpool_iter failed: %s\n"),
414 libzfs_error_description(g_zfs));
415 ret = zfs_err_to_be_err(g_zfs);
416 return (ret);
417 }
418
419 /* Generate string for obe_name's root dataset */
420 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
421 sizeof (obe_root_ds));
422 bt.obe_root_ds = obe_root_ds;
423
424 /* Get handle to BE's root dataset */
425 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
426 NULL) {
427 be_print_err(gettext("be_unmount: failed to "
428 "open BE root dataset (%s): %s\n"), bt.obe_root_ds,
429 libzfs_error_description(g_zfs));
430 ret = zfs_err_to_be_err(g_zfs);
431 return (ret);
432 }
433
434 /* Make sure BE's root dataset is mounted somewhere */
435 if (!zfs_is_mounted(zhp, &mp)) {
436
437 be_print_err(gettext("be_unmount: "
438 "(%s) not mounted\n"), bt.obe_name);
439
440 /*
441 * BE is not mounted, fix this BE's mountpoint if its root
442 * dataset isn't set to either 'legacy' or '/'.
443 */
444 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
445 be_print_err(gettext("be_unmount: mountpoint check "
446 "failed for %s\n"), bt.obe_root_ds);
447 ZFS_CLOSE(zhp);
448 return (ret);
449 }
450
451 ZFS_CLOSE(zhp);
452 return (BE_SUCCESS);
453 }
454
455 /*
456 * If we didn't get a mountpoint from the zfs_is_mounted call,
457 * try and get it from its property.
458 */
459 if (mp == NULL) {
460 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
461 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
462 be_print_err(gettext("be_unmount: failed to "
463 "get mountpoint of (%s)\n"), bt.obe_name);
464 ZFS_CLOSE(zhp);
465 return (BE_ERR_ZFS);
466 }
467 } else {
468 (void) strlcpy(mountpoint, mp, sizeof (mountpoint));
469 free(mp);
470 }
471
472 /* If BE mounted as current root, fail */
473 if (strcmp(mountpoint, "/") == 0) {
474 be_print_err(gettext("be_unmount: "
475 "cannot unmount currently running BE\n"));
476 ZFS_CLOSE(zhp);
477 return (BE_ERR_UMOUNT_CURR_BE);
478 }
479
480 ud.altroot = mountpoint;
481 ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
482
483 /* Unmount all supported non-global zones if we're in the global zone */
484 if (getzoneid() == GLOBAL_ZONEID &&
485 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
486 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
487 ZFS_CLOSE(zhp);
488 return (ret);
489 }
490 }
491
492 /* TODO: Unmount all non-ZFS file systems - Not supported yet */
493
494 /* Unmount all ZFS file systems not under the BE root dataset */
495 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
496 be_print_err(gettext("be_unmount: failed to "
497 "unmount shared file systems\n"));
498 ZFS_CLOSE(zhp);
499 return (ret);
500 }
501
502 /* Unmount all children datasets under the BE's root dataset */
503 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
504 &ud)) != 0) {
505 be_print_err(gettext("be_unmount: failed to "
506 "unmount BE (%s)\n"), bt.obe_name);
507 ZFS_CLOSE(zhp);
508 return (zret);
509 }
510
511 /* Unmount this BE's root filesystem */
512 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
513 ZFS_CLOSE(zhp);
514 return (ret);
515 }
516
517 ZFS_CLOSE(zhp);
518
519 return (BE_SUCCESS);
520 }
521
522 /*
523 * Function: be_mount_zone_root
524 * Description: Mounts the zone root dataset for a zone.
525 * Parameters:
526 * zfs - zfs_handle_t pointer to zone root dataset
527 * md - be_mount_data_t pointer to data for zone to be mounted
528 * Returns:
529 * BE_SUCCESS - Success
530 * be_errno_t - Failure
531 * Scope:
532 * Semi-private (library wide use only)
533 */
534 int
be_mount_zone_root(zfs_handle_t * zhp,be_mount_data_t * md)535 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
536 {
537 char mountpoint[MAXPATHLEN];
538 int err = 0;
539
540 /* Get mountpoint property of dataset */
541 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
542 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
543 be_print_err(gettext("be_mount_zone_root: failed to "
544 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
545 libzfs_error_description(g_zfs));
546 return (zfs_err_to_be_err(g_zfs));
547 }
548
549 /*
550 * Make sure zone's root dataset is set to 'legacy'. This is
551 * currently a requirement in this implementation of zones
552 * support.
553 */
554 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
555 be_print_err(gettext("be_mount_zone_root: "
556 "zone root dataset mountpoint is not 'legacy'\n"));
557 return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
558 }
559
560 /*
561 * Legacy mount the zone root dataset.
562 *
563 * As a workaround for 6176743, we mount the zone's root with the
564 * MS_OVERLAY option in case an alternate BE is mounted, and we're
565 * mounting the root for the zone from the current BE here. When an
566 * alternate BE is mounted, it ties up the zone's zoneroot directory
567 * for the current BE since the zone's zonepath is loopback mounted
568 * from the current BE.
569 *
570 * TODO: The MS_OVERLAY option needs to be removed when 6176743
571 * is fixed.
572 */
573 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
574 NULL, 0, NULL, 0) != 0) {
575 err = errno;
576 be_print_err(gettext("be_mount_zone_root: failed to "
577 "legacy mount zone root dataset (%s) at %s\n"),
578 zfs_get_name(zhp), md->altroot);
579 return (errno_to_be_err(err));
580 }
581
582 return (BE_SUCCESS);
583 }
584
585 /*
586 * Function: be_unmount_zone_root
587 * Description: Unmounts the zone root dataset for a zone.
588 * Parameters:
589 * zhp - zfs_handle_t pointer to zone root dataset
590 * ud - be_unmount_data_t pointer to data for zone to be unmounted
591 * Returns:
592 * BE_SUCCESS - Success
593 * be_errno_t - Failure
594 * Scope:
595 * Semi-private (library wise use only)
596 */
597 int
be_unmount_zone_root(zfs_handle_t * zhp,be_unmount_data_t * ud)598 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
599 {
600 char mountpoint[MAXPATHLEN];
601
602 /* Unmount the dataset */
603 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
604 be_print_err(gettext("be_unmount_zone_root: failed to "
605 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
606 libzfs_error_description(g_zfs));
607 return (zfs_err_to_be_err(g_zfs));
608 }
609
610 /* Get the current mountpoint property for the zone root dataset */
611 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
612 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
613 be_print_err(gettext("be_unmount_zone_root: failed to "
614 "get mountpoint property for zone root dataset (%s): %s\n"),
615 zfs_get_name(zhp), libzfs_error_description(g_zfs));
616 return (zfs_err_to_be_err(g_zfs));
617 }
618
619 /* If mountpoint not already set to 'legacy', set it to 'legacy' */
620 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
621 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
622 ZFS_MOUNTPOINT_LEGACY) != 0) {
623 be_print_err(gettext("be_unmount_zone_root: "
624 "failed to set mountpoint of zone root dataset "
625 "%s to 'legacy': %s\n"), zfs_get_name(zhp),
626 libzfs_error_description(g_zfs));
627 return (zfs_err_to_be_err(g_zfs));
628 }
629 }
630
631 return (BE_SUCCESS);
632 }
633
634 /*
635 * Function: be_get_legacy_fs
636 * Description: This function iterates through all non-shared file systems
637 * of a BE and finds the ones with a legacy mountpoint. For
638 * those file systems, it reads the BE's vfstab to get the
639 * mountpoint. If found, it adds that file system to the
640 * be_fs_list_data_t passed in.
641 *
642 * This function can be used to gather legacy mounted file systems
643 * for both global BEs and non-global zone BEs. To get data for
644 * a non-global zone BE, the zoneroot_ds and zoneroot parameters
645 * will be specified, otherwise they should be set to NULL.
646 * Parameters:
647 * be_name - global BE name from which to get legacy file
648 * system list.
649 * be_root_ds - root dataset of global BE.
650 * zoneroot_ds - root dataset of zone.
651 * zoneroot - zoneroot path of zone.
652 * fld - be_fs_list_data_t pointer.
653 * Returns:
654 * BE_SUCCESS - Success
655 * be_errno_t - Failure
656 * Scope:
657 * Semi-private (library wide use only)
658 */
659 int
be_get_legacy_fs(char * be_name,char * be_root_ds,char * zoneroot_ds,char * zoneroot,be_fs_list_data_t * fld)660 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
661 char *zoneroot, be_fs_list_data_t *fld)
662 {
663 zfs_handle_t *zhp = NULL;
664 char mountpoint[MAXPATHLEN];
665 boolean_t mounted_here = B_FALSE;
666 boolean_t zone_mounted_here = B_FALSE;
667 int ret = BE_SUCCESS, err = 0;
668
669 if (be_name == NULL || be_root_ds == NULL || fld == NULL)
670 return (BE_ERR_INVAL);
671
672 /* Get handle to BE's root dataset */
673 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
674 == NULL) {
675 be_print_err(gettext("be_get_legacy_fs: failed to "
676 "open BE root dataset (%s): %s\n"), be_root_ds,
677 libzfs_error_description(g_zfs));
678 ret = zfs_err_to_be_err(g_zfs);
679 return (ret);
680 }
681
682 /* If BE is not already mounted, mount it. */
683 if (!zfs_is_mounted(zhp, &fld->altroot)) {
684 if ((ret = _be_mount(be_name, &fld->altroot,
685 zoneroot_ds ? BE_MOUNT_FLAG_NULL :
686 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
687 be_print_err(gettext("be_get_legacy_fs: "
688 "failed to mount BE %s\n"), be_name);
689 goto cleanup;
690 }
691
692 mounted_here = B_TRUE;
693 } else if (fld->altroot == NULL) {
694 be_print_err(gettext("be_get_legacy_fs: failed to "
695 "get altroot of mounted BE %s: %s\n"),
696 be_name, libzfs_error_description(g_zfs));
697 ret = zfs_err_to_be_err(g_zfs);
698 goto cleanup;
699 }
700
701 /*
702 * If a zone root dataset was passed in, we're wanting to get
703 * legacy mounted file systems for that zone, not the global
704 * BE.
705 */
706 if (zoneroot_ds != NULL) {
707 be_mount_data_t zone_md = { 0 };
708
709 /* Close off handle to global BE's root dataset */
710 ZFS_CLOSE(zhp);
711
712 /* Get handle to zone's root dataset */
713 if ((zhp = zfs_open(g_zfs, zoneroot_ds,
714 ZFS_TYPE_FILESYSTEM)) == NULL) {
715 be_print_err(gettext("be_get_legacy_fs: failed to "
716 "open zone BE root dataset (%s): %s\n"),
717 zoneroot_ds, libzfs_error_description(g_zfs));
718 ret = zfs_err_to_be_err(g_zfs);
719 goto cleanup;
720 }
721
722 /* Make sure the zone we're looking for is mounted */
723 if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
724 char zone_altroot[MAXPATHLEN];
725
726 /* Generate alternate root path for zone */
727 (void) snprintf(zone_altroot, sizeof (zone_altroot),
728 "%s%s", fld->altroot, zoneroot);
729 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
730 be_print_err(gettext("be_get_legacy_fs: "
731 "memory allocation failed\n"));
732 ret = BE_ERR_NOMEM;
733 goto cleanup;
734 }
735
736 if ((ret = be_mount_zone_root(zhp, &zone_md))
737 != BE_SUCCESS) {
738 be_print_err(gettext("be_get_legacy_fs: "
739 "failed to mount zone root %s\n"),
740 zoneroot_ds);
741 free(zone_md.altroot);
742 zone_md.altroot = NULL;
743 goto cleanup;
744 }
745 zone_mounted_here = B_TRUE;
746 }
747
748 free(fld->altroot);
749 fld->altroot = zone_md.altroot;
750 }
751
752 /*
753 * If the root dataset is in the vfstab with a mountpoint of "/",
754 * add it to the list
755 */
756 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
757 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
758 if (strcmp(mountpoint, "/") == 0) {
759 if (add_to_fs_list(fld, zfs_get_name(zhp))
760 != BE_SUCCESS) {
761 be_print_err(gettext("be_get_legacy_fs: "
762 "failed to add %s to fs list\n"),
763 zfs_get_name(zhp));
764 ret = BE_ERR_INVAL;
765 goto cleanup;
766 }
767 }
768 }
769
770 /* Iterate subordinate file systems looking for legacy mounts */
771 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
772 fld)) != 0) {
773 be_print_err(gettext("be_get_legacy_fs: "
774 "failed to iterate %s to get legacy mounts\n"),
775 zfs_get_name(zhp));
776 }
777
778 cleanup:
779 /* If we mounted the zone BE, unmount it */
780 if (zone_mounted_here) {
781 be_unmount_data_t zone_ud = { 0 };
782
783 zone_ud.altroot = fld->altroot;
784 zone_ud.force = B_TRUE;
785 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
786 be_print_err(gettext("be_get_legacy_fs: "
787 "failed to unmount zone root %s\n"),
788 zoneroot_ds);
789 if (ret == BE_SUCCESS)
790 ret = err;
791 }
792 }
793
794 /* If we mounted this BE, unmount it */
795 if (mounted_here) {
796 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
797 be_print_err(gettext("be_get_legacy_fs: "
798 "failed to unmount %s\n"), be_name);
799 if (ret == BE_SUCCESS)
800 ret = err;
801 }
802 }
803
804 ZFS_CLOSE(zhp);
805
806 free(fld->altroot);
807 fld->altroot = NULL;
808
809 return (ret);
810 }
811
812 /*
813 * Function: be_free_fs_list
814 * Description: Function used to free the members of a be_fs_list_data_t
815 * structure.
816 * Parameters:
817 * fld - be_fs_list_data_t pointer to free.
818 * Returns:
819 * None
820 * Scope:
821 * Semi-private (library wide use only)
822 */
823 void
be_free_fs_list(be_fs_list_data_t * fld)824 be_free_fs_list(be_fs_list_data_t *fld)
825 {
826 int i;
827
828 if (fld == NULL)
829 return;
830
831 free(fld->altroot);
832
833 if (fld->fs_list == NULL)
834 return;
835
836 for (i = 0; i < fld->fs_num; i++)
837 free(fld->fs_list[i]);
838
839 free(fld->fs_list);
840 }
841
842 /*
843 * Function: be_get_ds_from_dir(char *dir)
844 * Description: Given a directory path, find the underlying dataset mounted
845 * at that directory path if there is one. The returned name
846 * is allocated in heap storage, so the caller is responsible
847 * for freeing it.
848 * Parameters:
849 * dir - char pointer of directory to find.
850 * Returns:
851 * NULL - if directory is not mounted from a dataset.
852 * name of dataset mounted at dir.
853 * Scope:
854 * Semi-private (library wide use only)
855 */
856 char *
be_get_ds_from_dir(char * dir)857 be_get_ds_from_dir(char *dir)
858 {
859 dir_data_t dd = { 0 };
860 char resolved_dir[MAXPATHLEN];
861
862 /* Make sure length of dir is within the max length */
863 if (dir == NULL || strlen(dir) >= MAXPATHLEN)
864 return (NULL);
865
866 /* Resolve dir in case its lofs mounted */
867 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
868 z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
869
870 dd.dir = resolved_dir;
871
872 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
873
874 return (dd.ds);
875 }
876
877 /*
878 * Function: be_make_tmp_mountpoint
879 * Description: This function generates a random temporary mountpoint
880 * and creates that mountpoint directory. It returns the
881 * mountpoint in heap storage, so the caller is responsible
882 * for freeing it.
883 * Parameters:
884 * tmp_mp - reference to pointer of where to store generated
885 * temporary mountpoint.
886 * Returns:
887 * BE_SUCCESS - Success
888 * be_errno_t - Failure
889 * Scope:
890 * Semi-private (library wide use only)
891 */
892 int
be_make_tmp_mountpoint(char ** tmp_mp)893 be_make_tmp_mountpoint(char **tmp_mp)
894 {
895 int err = 0;
896
897 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
898 be_print_err(gettext("be_make_tmp_mountpoint: "
899 "malloc failed\n"));
900 return (BE_ERR_NOMEM);
901 }
902 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
903 if (mkdtemp(*tmp_mp) == NULL) {
904 err = errno;
905 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
906 "for %s: %s\n"), *tmp_mp, strerror(err));
907 free(*tmp_mp);
908 *tmp_mp = NULL;
909 return (errno_to_be_err(err));
910 }
911
912 return (BE_SUCCESS);
913 }
914
915 /*
916 * Function: be_mount_pool
917 * Description: This function determines if the pool's datase is mounted
918 * and if not it is used to mount the pool's dataset. The
919 * function returns the current mountpoint if we are able
920 * to mount the dataset.
921 * Parameters:
922 * zhp - handle to the pool's dataset
923 * tmp_mntpnt - The temporary mountpoint that the pool's
924 * dataset is mounted on. This is set only
925 * if the attempt to mount the dataset at it's
926 * set mountpoint fails, and we've used a
927 * temporary mount point for this dataset. It
928 * is expected that the caller will free this
929 * memory.
930 * orig_mntpnt - The original mountpoint for the pool. If a
931 * temporary mount point was needed this will
932 * be used to reset the mountpoint property to
933 * it's original mountpoint. It is expected that
934 * the caller will free this memory.
935 * pool_mounted - This flag indicates that the pool was mounted
936 * in this function.
937 * Returns:
938 * BE_SUCCESS - Success
939 * be_errno_t - Failure
940 * Scope:
941 * Semi-private (library wide use only)
942 */
943 int
be_mount_pool(zfs_handle_t * zhp,char ** tmp_mntpnt,char ** orig_mntpnt,boolean_t * pool_mounted)944 be_mount_pool(
945 zfs_handle_t *zhp,
946 char **tmp_mntpnt,
947 char **orig_mntpnt,
948 boolean_t *pool_mounted)
949 {
950
951 char mountpoint[MAXPATHLEN];
952 int ret = 0;
953
954 *tmp_mntpnt = NULL;
955 *orig_mntpnt = NULL;
956 *pool_mounted = B_FALSE;
957
958 if (!zfs_is_mounted(zhp, NULL)) {
959 if (zfs_mount(zhp, NULL, 0) != 0) {
960 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
961 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
962 be_print_err(gettext("be_mount_pool: failed to "
963 "get mountpoint of (%s): %s\n"),
964 zfs_get_name(zhp),
965 libzfs_error_description(g_zfs));
966 return (zfs_err_to_be_err(g_zfs));
967 }
968 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
969 be_print_err(gettext("be_mount_pool: memory "
970 "allocation failed\n"));
971 return (BE_ERR_NOMEM);
972 }
973 /*
974 * attempt to mount on a temp mountpoint
975 */
976 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
977 != BE_SUCCESS) {
978 be_print_err(gettext("be_mount_pool: failed "
979 "to make temporary mountpoint\n"));
980 free(*orig_mntpnt);
981 *orig_mntpnt = NULL;
982 return (ret);
983 }
984
985 if (zfs_prop_set(zhp,
986 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
987 *tmp_mntpnt) != 0) {
988 be_print_err(gettext("be_mount_pool: failed "
989 "to set mountpoint of pool dataset %s to "
990 "%s: %s\n"), zfs_get_name(zhp),
991 *orig_mntpnt,
992 libzfs_error_description(g_zfs));
993 free(*tmp_mntpnt);
994 free(*orig_mntpnt);
995 *orig_mntpnt = NULL;
996 *tmp_mntpnt = NULL;
997 return (zfs_err_to_be_err(g_zfs));
998 }
999
1000 if (zfs_mount(zhp, NULL, 0) != 0) {
1001 be_print_err(gettext("be_mount_pool: failed "
1002 "to mount dataset %s at %s: %s\n"),
1003 zfs_get_name(zhp), *tmp_mntpnt,
1004 libzfs_error_description(g_zfs));
1005 ret = zfs_err_to_be_err(g_zfs);
1006 if (zfs_prop_set(zhp,
1007 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1008 mountpoint) != 0) {
1009 be_print_err(gettext("be_mount_pool: "
1010 "failed to set mountpoint of pool "
1011 "dataset %s to %s: %s\n"),
1012 zfs_get_name(zhp), *tmp_mntpnt,
1013 libzfs_error_description(g_zfs));
1014 }
1015 free(*tmp_mntpnt);
1016 free(*orig_mntpnt);
1017 *orig_mntpnt = NULL;
1018 *tmp_mntpnt = NULL;
1019 return (ret);
1020 }
1021 }
1022 *pool_mounted = B_TRUE;
1023 }
1024
1025 return (BE_SUCCESS);
1026 }
1027
1028 /*
1029 * Function: be_unmount_pool
1030 * Description: This function is used to unmount the pool's dataset if we
1031 * mounted it previously using be_mount_pool().
1032 * Parameters:
1033 * zhp - handle to the pool's dataset
1034 * tmp_mntpnt - If a temprary mount point was used this will
1035 * be set. Since this was created in be_mount_pool
1036 * we will need to clean it up here.
1037 * orig_mntpnt - The original mountpoint for the pool. This is
1038 * used to set the dataset mountpoint property
1039 * back to it's original value in the case where a
1040 * temporary mountpoint was used.
1041 * Returns:
1042 * BE_SUCCESS - Success
1043 * be_errno_t - Failure
1044 * Scope:
1045 * Semi-private (library wide use only)
1046 */
1047 int
be_unmount_pool(zfs_handle_t * zhp,char * tmp_mntpnt,char * orig_mntpnt)1048 be_unmount_pool(
1049 zfs_handle_t *zhp,
1050 char *tmp_mntpnt,
1051 char *orig_mntpnt)
1052 {
1053 if (zfs_unmount(zhp, NULL, 0) != 0) {
1054 be_print_err(gettext("be_unmount_pool: failed to "
1055 "unmount pool (%s): %s\n"), zfs_get_name(zhp),
1056 libzfs_error_description(g_zfs));
1057 return (zfs_err_to_be_err(g_zfs));
1058 }
1059 if (orig_mntpnt != NULL) {
1060 if (tmp_mntpnt != NULL &&
1061 strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
1062 (void) rmdir(tmp_mntpnt);
1063 }
1064 if (zfs_prop_set(zhp,
1065 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1066 orig_mntpnt) != 0) {
1067 be_print_err(gettext("be_unmount_pool: failed "
1068 "to set the mountpoint for dataset (%s) to "
1069 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
1070 libzfs_error_description(g_zfs));
1071 return (zfs_err_to_be_err(g_zfs));
1072 }
1073 }
1074
1075 return (BE_SUCCESS);
1076 }
1077
1078 /* ******************************************************************** */
1079 /* Private Functions */
1080 /* ******************************************************************** */
1081
1082 /*
1083 * Function: be_mount_callback
1084 * Description: Callback function used to iterate through all of a BE's
1085 * subordinate file systems and to mount them accordingly.
1086 * Parameters:
1087 * zhp - zfs_handle_t pointer to current file system being
1088 * processed.
1089 * data - pointer to the altroot of where to mount BE.
1090 * Returns:
1091 * 0 - Success
1092 * be_errno_t - Failure
1093 * Scope:
1094 * Private
1095 */
1096 static int
be_mount_callback(zfs_handle_t * zhp,void * data)1097 be_mount_callback(zfs_handle_t *zhp, void *data)
1098 {
1099 zprop_source_t sourcetype;
1100 const char *fs_name = zfs_get_name(zhp);
1101 char source[ZFS_MAXNAMELEN];
1102 char *altroot = data;
1103 char zhp_mountpoint[MAXPATHLEN];
1104 char mountpoint[MAXPATHLEN];
1105 int ret = 0;
1106
1107 /* Get dataset's mountpoint and source values */
1108 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1109 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
1110 B_FALSE) != 0) {
1111 be_print_err(gettext("be_mount_callback: failed to "
1112 "get mountpoint and sourcetype for %s\n"),
1113 fs_name);
1114 ZFS_CLOSE(zhp);
1115 return (BE_ERR_ZFS);
1116 }
1117
1118 /*
1119 * Set this filesystem's 'canmount' property to 'noauto' just incase
1120 * it's been set 'on'. We do this so that when we change its
1121 * mountpoint zfs won't immediately try to mount it.
1122 */
1123 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1124 be_print_err(gettext("be_mount_callback: failed to "
1125 "set canmount to 'noauto' (%s)\n"), fs_name);
1126 ZFS_CLOSE(zhp);
1127 return (BE_ERR_ZFS);
1128 }
1129
1130 /*
1131 * If the mountpoint is none, there's nothing to do, goto next.
1132 * If the mountpoint is legacy, legacy mount it with mount(2).
1133 * If the mountpoint is inherited, its mountpoint should
1134 * already be set. If it's not, then explicitly fix-up
1135 * the mountpoint now by appending its explicitly set
1136 * mountpoint value to the BE mountpoint.
1137 */
1138 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
1139 goto next;
1140 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1141 /*
1142 * If the mountpoint is set to 'legacy', we need to
1143 * dig into this BE's vfstab to figure out where to
1144 * mount it, and just mount it via mount(2).
1145 */
1146 if (get_mountpoint_from_vfstab(altroot, fs_name,
1147 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
1148
1149 /* Legacy mount the file system */
1150 if (mount(fs_name, mountpoint, MS_DATA,
1151 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
1152 be_print_err(
1153 gettext("be_mount_callback: "
1154 "failed to mount %s on %s\n"),
1155 fs_name, mountpoint);
1156 }
1157 } else {
1158 be_print_err(
1159 gettext("be_mount_callback: "
1160 "no entry for %s in vfstab, "
1161 "skipping ...\n"), fs_name);
1162 }
1163
1164 goto next;
1165
1166 } else if (sourcetype & ZPROP_SRC_INHERITED) {
1167 /*
1168 * If the mountpoint is inherited, its parent should have
1169 * already been processed so its current mountpoint value
1170 * is what its mountpoint ought to be.
1171 */
1172 (void) strlcpy(mountpoint, zhp_mountpoint, sizeof (mountpoint));
1173 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1174 /*
1175 * Else process dataset with explicitly set mountpoint.
1176 */
1177 (void) snprintf(mountpoint, sizeof (mountpoint),
1178 "%s%s", altroot, zhp_mountpoint);
1179
1180 /* Set the new mountpoint for the dataset */
1181 if (zfs_prop_set(zhp,
1182 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1183 mountpoint)) {
1184 be_print_err(gettext("be_mount_callback: "
1185 "failed to set mountpoint for %s to "
1186 "%s\n"), fs_name, mountpoint);
1187 ZFS_CLOSE(zhp);
1188 return (BE_ERR_ZFS);
1189 }
1190 } else {
1191 be_print_err(gettext("be_mount_callback: "
1192 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1193 fs_name, sourcetype);
1194
1195 goto next;
1196 }
1197
1198 /* Mount this filesystem */
1199 if (zfs_mount(zhp, NULL, 0) != 0) {
1200 be_print_err(gettext("be_mount_callback: failed to "
1201 "mount dataset %s at %s: %s\n"), fs_name, mountpoint,
1202 libzfs_error_description(g_zfs));
1203 /*
1204 * Set this filesystem's 'mountpoint' property back to what
1205 * it was
1206 */
1207 if (sourcetype & ZPROP_SRC_LOCAL &&
1208 strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
1209 (void) zfs_prop_set(zhp,
1210 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1211 zhp_mountpoint);
1212 }
1213
1214 ZFS_CLOSE(zhp);
1215 return (BE_ERR_MOUNT);
1216 }
1217
1218 next:
1219 /* Iterate through this dataset's children and mount them */
1220 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
1221 altroot)) != 0) {
1222 ZFS_CLOSE(zhp);
1223 return (ret);
1224 }
1225
1226
1227 ZFS_CLOSE(zhp);
1228 return (0);
1229 }
1230
1231 /*
1232 * Function: be_unmount_callback
1233 * Description: Callback function used to iterate through all of a BE's
1234 * subordinate file systems and to unmount them.
1235 * Parameters:
1236 * zhp - zfs_handle_t pointer to current file system being
1237 * processed.
1238 * data - pointer to the mountpoint of where BE is mounted.
1239 * Returns:
1240 * 0 - Success
1241 * be_errno_t - Failure
1242 * Scope:
1243 * Private
1244 */
1245 static int
be_unmount_callback(zfs_handle_t * zhp,void * data)1246 be_unmount_callback(zfs_handle_t *zhp, void *data)
1247 {
1248 be_unmount_data_t *ud = data;
1249 zprop_source_t sourcetype;
1250 const char *fs_name = zfs_get_name(zhp);
1251 char source[ZFS_MAXNAMELEN];
1252 char mountpoint[MAXPATHLEN];
1253 char *zhp_mountpoint;
1254 int ret = 0;
1255
1256 /* Iterate down this dataset's children first */
1257 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
1258 ret = BE_ERR_UMOUNT;
1259 goto done;
1260 }
1261
1262 /* Is dataset even mounted ? */
1263 if (!zfs_is_mounted(zhp, NULL))
1264 goto done;
1265
1266 /* Unmount this file system */
1267 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
1268 be_print_err(gettext("be_unmount_callback: "
1269 "failed to unmount %s: %s\n"), fs_name,
1270 libzfs_error_description(g_zfs));
1271 ret = zfs_err_to_be_err(g_zfs);
1272 goto done;
1273 }
1274
1275 /* Get dataset's current mountpoint and source value */
1276 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1277 sizeof (mountpoint), &sourcetype, source, sizeof (source),
1278 B_FALSE) != 0) {
1279 be_print_err(gettext("be_unmount_callback: "
1280 "failed to get mountpoint and sourcetype for %s: %s\n"),
1281 fs_name, libzfs_error_description(g_zfs));
1282 ret = zfs_err_to_be_err(g_zfs);
1283 goto done;
1284 }
1285
1286 if (sourcetype & ZPROP_SRC_INHERITED) {
1287 /*
1288 * If the mountpoint is inherited we don't need to
1289 * do anything. When its parent gets processed
1290 * its mountpoint will be set accordingly.
1291 */
1292 goto done;
1293 } else if (sourcetype & ZPROP_SRC_LOCAL) {
1294
1295 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1296 /*
1297 * If the mountpoint is set to 'legacy', its already
1298 * been unmounted (from above call to zfs_unmount), and
1299 * we don't need to do anything else with it.
1300 */
1301 goto done;
1302
1303 } else {
1304 /*
1305 * Else process dataset with explicitly set mountpoint.
1306 */
1307
1308 /*
1309 * Get this dataset's mountpoint relative to
1310 * the BE's mountpoint.
1311 */
1312 if ((strncmp(mountpoint, ud->altroot,
1313 strlen(ud->altroot)) == 0) &&
1314 (mountpoint[strlen(ud->altroot)] == '/')) {
1315
1316 zhp_mountpoint = mountpoint +
1317 strlen(ud->altroot);
1318
1319 /* Set this dataset's mountpoint value */
1320 if (zfs_prop_set(zhp,
1321 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1322 zhp_mountpoint)) {
1323 be_print_err(
1324 gettext("be_unmount_callback: "
1325 "failed to set mountpoint for "
1326 "%s to %s: %s\n"), fs_name,
1327 zhp_mountpoint,
1328 libzfs_error_description(g_zfs));
1329 ret = zfs_err_to_be_err(g_zfs);
1330 }
1331 } else {
1332 be_print_err(
1333 gettext("be_unmount_callback: "
1334 "%s not mounted under BE's altroot %s, "
1335 "skipping ...\n"), fs_name, ud->altroot);
1336 /*
1337 * fs_name is mounted but not under the
1338 * root for this BE.
1339 */
1340 ret = BE_ERR_INVALMOUNTPOINT;
1341 }
1342 }
1343 } else {
1344 be_print_err(gettext("be_unmount_callback: "
1345 "mountpoint sourcetype of %s is %d, skipping ...\n"),
1346 fs_name, sourcetype);
1347 ret = BE_ERR_ZFS;
1348 }
1349
1350 done:
1351 /* Set this filesystem's 'canmount' property to 'noauto' */
1352 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
1353 be_print_err(gettext("be_unmount_callback: "
1354 "failed to set canmount to 'noauto' (%s)\n"), fs_name);
1355 if (ret == 0)
1356 ret = BE_ERR_ZFS;
1357 }
1358
1359 ZFS_CLOSE(zhp);
1360 return (ret);
1361 }
1362
1363 /*
1364 * Function: be_get_legacy_fs_callback
1365 * Description: The callback function is used to iterate through all
1366 * non-shared file systems of a BE, finding ones that have
1367 * a legacy mountpoint and an entry in the BE's vfstab.
1368 * It adds these file systems to the callback data.
1369 * Parameters:
1370 * zhp - zfs_handle_t pointer to current file system being
1371 * processed.
1372 * data - be_fs_list_data_t pointer
1373 * Returns:
1374 * 0 - Success
1375 * be_errno_t - Failure
1376 * Scope:
1377 * Private
1378 */
1379 static int
be_get_legacy_fs_callback(zfs_handle_t * zhp,void * data)1380 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
1381 {
1382 be_fs_list_data_t *fld = data;
1383 const char *fs_name = zfs_get_name(zhp);
1384 char zhp_mountpoint[MAXPATHLEN];
1385 char mountpoint[MAXPATHLEN];
1386 int ret = 0;
1387
1388 /* Get this dataset's mountpoint property */
1389 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
1390 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
1391 be_print_err(gettext("be_get_legacy_fs_callback: "
1392 "failed to get mountpoint for %s: %s\n"),
1393 fs_name, libzfs_error_description(g_zfs));
1394 ret = zfs_err_to_be_err(g_zfs);
1395 ZFS_CLOSE(zhp);
1396 return (ret);
1397 }
1398
1399 /*
1400 * If mountpoint is legacy, try to get its mountpoint from this BE's
1401 * vfstab. If it exists in the vfstab, add this file system to the
1402 * callback data.
1403 */
1404 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1405 if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
1406 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
1407 be_print_err(gettext("be_get_legacy_fs_callback: "
1408 "no entry for %s in vfstab, "
1409 "skipping ...\n"), fs_name);
1410
1411 goto next;
1412 }
1413
1414 /* Record file system into the callback data. */
1415 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
1416 be_print_err(gettext("be_get_legacy_fs_callback: "
1417 "failed to add %s to fs list\n"), mountpoint);
1418 ZFS_CLOSE(zhp);
1419 return (BE_ERR_NOMEM);
1420 }
1421 }
1422
1423 next:
1424 /* Iterate through this dataset's children file systems */
1425 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
1426 fld)) != 0) {
1427 ZFS_CLOSE(zhp);
1428 return (ret);
1429 }
1430 ZFS_CLOSE(zhp);
1431 return (0);
1432 }
1433
1434 /*
1435 * Function: add_to_fs_list
1436 * Description: Function used to add a file system to the fs_list array in
1437 * a be_fs_list_data_t structure.
1438 * Parameters:
1439 * fld - be_fs_list_data_t pointer
1440 * fs - file system to add
1441 * Returns:
1442 * BE_SUCCESS - Success
1443 * 1 - Failure
1444 * Scope:
1445 * Private
1446 */
1447 static int
add_to_fs_list(be_fs_list_data_t * fld,const char * fs)1448 add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
1449 {
1450 if (fld == NULL || fs == NULL)
1451 return (1);
1452
1453 if ((fld->fs_list = (char **)realloc(fld->fs_list,
1454 sizeof (char *)*(fld->fs_num + 1))) == NULL) {
1455 be_print_err(gettext("add_to_fs_list: "
1456 "memory allocation failed\n"));
1457 return (1);
1458 }
1459
1460 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
1461 be_print_err(gettext("add_to_fs_list: "
1462 "memory allocation failed\n"));
1463 return (1);
1464 }
1465
1466 return (BE_SUCCESS);
1467 }
1468
1469 /*
1470 * Function: zpool_shared_fs_callback
1471 * Description: Callback function used to iterate through all existing pools
1472 * to find and mount all shared filesystems. This function
1473 * processes the pool's "pool data" dataset, then uses
1474 * iter_shared_fs_callback to iterate through the pool's
1475 * datasets.
1476 * Parameters:
1477 * zlp - zpool_handle_t pointer to the current pool being
1478 * looked at.
1479 * data - be_mount_data_t pointer
1480 * Returns:
1481 * 0 - Success
1482 * be_errno_t - Failure
1483 * Scope:
1484 * Private
1485 */
1486 static int
zpool_shared_fs_callback(zpool_handle_t * zlp,void * data)1487 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
1488 {
1489 be_mount_data_t *md = data;
1490 zfs_handle_t *zhp = NULL;
1491 const char *zpool = zpool_get_name(zlp);
1492 int ret = 0;
1493
1494 /*
1495 * Get handle to pool's "pool data" dataset
1496 */
1497 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
1498 be_print_err(gettext("zpool_shared_fs: "
1499 "failed to open pool dataset %s: %s\n"), zpool,
1500 libzfs_error_description(g_zfs));
1501 ret = zfs_err_to_be_err(g_zfs);
1502 zpool_close(zlp);
1503 return (ret);
1504 }
1505
1506 /* Process this pool's "pool data" dataset */
1507 (void) loopback_mount_shared_fs(zhp, md);
1508
1509 /* Interate through this pool's children */
1510 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1511
1512 ZFS_CLOSE(zhp);
1513 zpool_close(zlp);
1514
1515 return (0);
1516 }
1517
1518 /*
1519 * Function: iter_shared_fs_callback
1520 * Description: Callback function used to iterate through a pool's datasets
1521 * to find and mount all shared filesystems. It makes sure to
1522 * find the BE container dataset of the pool, if it exists, and
1523 * does not process and iterate down that path.
1524 *
1525 * Note - This function iterates linearly down the
1526 * hierarchical dataset paths and mounts things as it goes
1527 * along. It does not make sure that something deeper down
1528 * a dataset path has an interim mountpoint for something
1529 * processed earlier.
1530 *
1531 * Parameters:
1532 * zhp - zfs_handle_t pointer to the current dataset being
1533 * processed.
1534 * data - be_mount_data_t pointer
1535 * Returns:
1536 * 0 - Success
1537 * be_errno_t - Failure
1538 * Scope:
1539 * Private
1540 */
1541 static int
iter_shared_fs_callback(zfs_handle_t * zhp,void * data)1542 iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
1543 {
1544 be_mount_data_t *md = data;
1545 const char *name = zfs_get_name(zhp);
1546 char container_ds[MAXPATHLEN];
1547 char tmp_name[MAXPATHLEN];
1548 char *pool;
1549
1550 /* Get the pool's name */
1551 (void) strlcpy(tmp_name, name, sizeof (tmp_name));
1552 pool = strtok(tmp_name, "/");
1553
1554 if (pool) {
1555 /* Get the name of this pool's container dataset */
1556 be_make_container_ds(pool, container_ds,
1557 sizeof (container_ds));
1558
1559 /*
1560 * If what we're processing is this pool's BE container
1561 * dataset, skip it.
1562 */
1563 if (strcmp(name, container_ds) == 0) {
1564 ZFS_CLOSE(zhp);
1565 return (0);
1566 }
1567 } else {
1568 /* Getting the pool name failed, return error */
1569 be_print_err(gettext("iter_shared_fs_callback: "
1570 "failed to get pool name from %s\n"), name);
1571 ZFS_CLOSE(zhp);
1572 return (BE_ERR_POOL_NOENT);
1573 }
1574
1575 /* Mount this shared filesystem */
1576 (void) loopback_mount_shared_fs(zhp, md);
1577
1578 /* Iterate this dataset's children file systems */
1579 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
1580 ZFS_CLOSE(zhp);
1581
1582 return (0);
1583 }
1584
1585 /*
1586 * Function: loopback_mount_shared_fs
1587 * Description: This function loopback mounts a file system into the altroot
1588 * area of the BE being mounted. Since these are shared file
1589 * systems, they are expected to be already mounted for the
1590 * current BE, and this function just loopback mounts them into
1591 * the BE mountpoint. If they are not mounted for the current
1592 * live system, they are skipped and not mounted into the BE
1593 * we're mounting.
1594 * Parameters:
1595 * zhp - zfs_handle_t pointer to the dataset to loopback mount
1596 * md - be_mount_data_t pointer
1597 * Returns:
1598 * BE_SUCCESS - Success
1599 * be_errno_t - Failure
1600 * Scope:
1601 * Private
1602 */
1603 static int
loopback_mount_shared_fs(zfs_handle_t * zhp,be_mount_data_t * md)1604 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
1605 {
1606 char zhp_mountpoint[MAXPATHLEN];
1607 char mountpoint[MAXPATHLEN];
1608 char *mp = NULL;
1609 char optstr[MAX_MNTOPT_STR];
1610 int mflag = MS_OPTIONSTR;
1611 int err;
1612
1613 /*
1614 * Check if file system is currently mounted and not delegated
1615 * to a non-global zone (if we're in the global zone)
1616 */
1617 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
1618 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
1619 /*
1620 * If we didn't get a mountpoint from the zfs_is_mounted call,
1621 * get it from the mountpoint property.
1622 */
1623 if (mp == NULL) {
1624 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
1625 zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
1626 NULL, 0, B_FALSE) != 0) {
1627 be_print_err(
1628 gettext("loopback_mount_shared_fs: "
1629 "failed to get mountpoint property\n"));
1630 return (BE_ERR_ZFS);
1631 }
1632 } else {
1633 (void) strlcpy(zhp_mountpoint, mp,
1634 sizeof (zhp_mountpoint));
1635 free(mp);
1636 }
1637
1638 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
1639 md->altroot, zhp_mountpoint);
1640
1641 /* Mount it read-only if read-write was not requested */
1642 if (!md->shared_rw) {
1643 mflag |= MS_RDONLY;
1644 }
1645
1646 /* Add the "nosub" option to the mount options string */
1647 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1648
1649 /* Loopback mount this dataset at the altroot */
1650 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
1651 NULL, 0, optstr, sizeof (optstr)) != 0) {
1652 err = errno;
1653 be_print_err(gettext("loopback_mount_shared_fs: "
1654 "failed to loopback mount %s at %s: %s\n"),
1655 zhp_mountpoint, mountpoint, strerror(err));
1656 return (BE_ERR_MOUNT);
1657 }
1658 }
1659
1660 return (BE_SUCCESS);
1661 }
1662
1663 /*
1664 * Function: loopback_mount_zonepath
1665 * Description: This function loopback mounts a zonepath into the altroot
1666 * area of the BE being mounted. Since these are shared file
1667 * systems, they are expected to be already mounted for the
1668 * current BE, and this function just loopback mounts them into
1669 * the BE mountpoint.
1670 * Parameters:
1671 * zonepath - pointer to zone path in the current BE
1672 * md - be_mount_data_t pointer
1673 * Returns:
1674 * BE_SUCCESS - Success
1675 * be_errno_t - Failure
1676 * Scope:
1677 * Private
1678 */
1679 static int
loopback_mount_zonepath(const char * zonepath,be_mount_data_t * md)1680 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
1681 {
1682 FILE *fp = (FILE *)NULL;
1683 struct stat st;
1684 char *p;
1685 char *p1;
1686 char *parent_dir;
1687 struct extmnttab extmtab;
1688 dev_t dev = NODEV;
1689 char *parentmnt;
1690 char alt_parentmnt[MAXPATHLEN];
1691 struct mnttab mntref;
1692 char altzonepath[MAXPATHLEN];
1693 char optstr[MAX_MNTOPT_STR];
1694 int mflag = MS_OPTIONSTR;
1695 int ret;
1696 int err;
1697
1698 fp = fopen(MNTTAB, "r");
1699 if (fp == NULL) {
1700 err = errno;
1701 be_print_err(gettext("loopback_mount_zonepath: "
1702 "failed to open /etc/mnttab\n"));
1703 return (errno_to_be_err(err));
1704 }
1705
1706 /*
1707 * before attempting the loopback mount of zonepath under altroot,
1708 * we need to make sure that all intermediate file systems in the
1709 * zone path are also mounted under altroot
1710 */
1711
1712 /* get the parent directory for zonepath */
1713 p = strrchr(zonepath, '/');
1714 if (p != NULL && p != zonepath) {
1715 if ((parent_dir = (char *)calloc(sizeof (char),
1716 p - zonepath + 1)) == NULL) {
1717 ret = BE_ERR_NOMEM;
1718 goto done;
1719 }
1720 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
1721 if (stat(parent_dir, &st) < 0) {
1722 ret = errno_to_be_err(errno);
1723 be_print_err(gettext("loopback_mount_zonepath: "
1724 "failed to stat %s"),
1725 parent_dir);
1726 free(parent_dir);
1727 goto done;
1728 }
1729 free(parent_dir);
1730
1731 /*
1732 * After the above stat call, st.st_dev contains ID of the
1733 * device over which parent dir resides.
1734 * Now, search mnttab and find mount point of parent dir device.
1735 */
1736
1737 resetmnttab(fp);
1738 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
1739 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
1740 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
1741 MNTTYPE_ZFS) == 0) {
1742 p1 = strchr(extmtab.mnt_special, '/');
1743 if (p1 == NULL || strncmp(p1 + 1,
1744 BE_CONTAINER_DS_NAME, 4) != 0 ||
1745 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
1746 /*
1747 * if parent dir is in a shared file
1748 * system, check whether it is already
1749 * loopback mounted under altroot or
1750 * not. It would have been mounted
1751 * already under altroot if it is in
1752 * a non-shared filesystem.
1753 */
1754 parentmnt = strdup(extmtab.mnt_mountp);
1755 (void) snprintf(alt_parentmnt,
1756 sizeof (alt_parentmnt), "%s%s",
1757 md->altroot, parentmnt);
1758 mntref.mnt_mountp = alt_parentmnt;
1759 mntref.mnt_special = parentmnt;
1760 mntref.mnt_fstype = MNTTYPE_LOFS;
1761 mntref.mnt_mntopts = NULL;
1762 mntref.mnt_time = NULL;
1763 resetmnttab(fp);
1764 if (getmntany(fp, (struct mnttab *)
1765 &extmtab, &mntref) != 0) {
1766 ret = loopback_mount_zonepath(
1767 parentmnt, md);
1768 if (ret != BE_SUCCESS) {
1769 free(parentmnt);
1770 goto done;
1771 }
1772 }
1773 free(parentmnt);
1774 }
1775 break;
1776 }
1777 }
1778 }
1779
1780
1781 if (!md->shared_rw) {
1782 mflag |= MS_RDONLY;
1783 }
1784
1785 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
1786 md->altroot, zonepath);
1787
1788 /* Add the "nosub" option to the mount options string */
1789 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
1790
1791 /* Loopback mount this dataset at the altroot */
1792 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
1793 NULL, 0, optstr, sizeof (optstr)) != 0) {
1794 err = errno;
1795 be_print_err(gettext("loopback_mount_zonepath: "
1796 "failed to loopback mount %s at %s: %s\n"),
1797 zonepath, altzonepath, strerror(err));
1798 ret = BE_ERR_MOUNT;
1799 goto done;
1800 }
1801 ret = BE_SUCCESS;
1802
1803 done :
1804 (void) fclose(fp);
1805 return (ret);
1806 }
1807
1808 /*
1809 * Function: unmount_shared_fs
1810 * Description: This function iterates through the mnttab and finds all
1811 * loopback mount entries that reside within the altroot of
1812 * where the BE is mounted, and unmounts it.
1813 * Parameters:
1814 * ud - be_unmount_data_t pointer
1815 * Returns:
1816 * BE_SUCCESS - Success
1817 * be_errno_t - Failure
1818 * Scope:
1819 * Private
1820 */
1821 static int
unmount_shared_fs(be_unmount_data_t * ud)1822 unmount_shared_fs(be_unmount_data_t *ud)
1823 {
1824 FILE *fp = NULL;
1825 struct mnttab *table = NULL;
1826 struct mnttab ent;
1827 struct mnttab *entp = NULL;
1828 size_t size = 0;
1829 int read_chunk = 32;
1830 int i;
1831 int altroot_len;
1832 int err = 0;
1833
1834 errno = 0;
1835
1836 /* Read in the mnttab into a table */
1837 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1838 err = errno;
1839 be_print_err(gettext("unmount_shared_fs: "
1840 "failed to open mnttab\n"));
1841 return (errno_to_be_err(err));
1842 }
1843
1844 while (getmntent(fp, &ent) == 0) {
1845 if (size % read_chunk == 0) {
1846 table = (struct mnttab *)realloc(table,
1847 (size + read_chunk) * sizeof (ent));
1848 }
1849 entp = &table[size++];
1850
1851 /*
1852 * Copy over the current mnttab entry into our table,
1853 * copying only the fields that we care about.
1854 */
1855 (void) memset(entp, 0, sizeof (*entp));
1856 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
1857 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
1858 be_print_err(gettext("unmount_shared_fs: "
1859 "memory allocation failed\n"));
1860 return (BE_ERR_NOMEM);
1861 }
1862 }
1863 (void) fclose(fp);
1864
1865 /*
1866 * Process the mnttab entries in reverse order, looking for
1867 * loopback mount entries mounted under our altroot.
1868 */
1869 altroot_len = strlen(ud->altroot);
1870 for (i = size; i > 0; i--) {
1871 entp = &table[i - 1];
1872
1873 /* If not of type lofs, skip */
1874 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
1875 continue;
1876
1877 /* If inside the altroot, unmount it */
1878 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
1879 entp->mnt_mountp[altroot_len] == '/') {
1880 if (umount(entp->mnt_mountp) != 0) {
1881 err = errno;
1882 if (err == EBUSY) {
1883 (void) sleep(1);
1884 err = errno = 0;
1885 if (umount(entp->mnt_mountp) != 0)
1886 err = errno;
1887 }
1888 if (err != 0) {
1889 be_print_err(gettext(
1890 "unmount_shared_fs: "
1891 "failed to unmount shared file "
1892 "system %s: %s\n"),
1893 entp->mnt_mountp, strerror(err));
1894 return (errno_to_be_err(err));
1895 }
1896 }
1897 }
1898 }
1899
1900 return (BE_SUCCESS);
1901 }
1902
1903 /*
1904 * Function: get_mountpoint_from_vfstab
1905 * Description: This function digs into the vfstab in the given altroot,
1906 * and searches for an entry for the fs passed in. If found,
1907 * it returns the mountpoint of that fs in the mountpoint
1908 * buffer passed in. If the get_alt_mountpoint flag is set,
1909 * it returns the mountpoint with the altroot prepended.
1910 * Parameters:
1911 * altroot - pointer to the alternate root location
1912 * fs - pointer to the file system name to look for in the
1913 * vfstab in altroot
1914 * mountpoint - pointer to buffer of where the mountpoint of
1915 * fs will be returned.
1916 * size_mp - size of mountpoint argument
1917 * get_alt_mountpoint - flag to indicate whether or not the
1918 * mountpoint should be populated with the altroot
1919 * prepended.
1920 * Returns:
1921 * BE_SUCCESS - Success
1922 * 1 - Failure
1923 * Scope:
1924 * Private
1925 */
1926 static int
get_mountpoint_from_vfstab(char * altroot,const char * fs,char * mountpoint,size_t size_mp,boolean_t get_alt_mountpoint)1927 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
1928 size_t size_mp, boolean_t get_alt_mountpoint)
1929 {
1930 struct vfstab vp;
1931 FILE *fp = NULL;
1932 char alt_vfstab[MAXPATHLEN];
1933
1934 /* Generate path to alternate root vfstab */
1935 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1936 altroot);
1937
1938 /* Open alternate root vfstab */
1939 if ((fp = fopen(alt_vfstab, "r")) == NULL) {
1940 be_print_err(gettext("get_mountpoint_from_vfstab: "
1941 "failed to open vfstab (%s)\n"), alt_vfstab);
1942 return (1);
1943 }
1944
1945 if (getvfsspec(fp, &vp, (char *)fs) == 0) {
1946 /*
1947 * Found entry for fs, grab its mountpoint.
1948 * If the flag to prepend the altroot into the mountpoint
1949 * is set, prepend it. Otherwise, just return the mountpoint.
1950 */
1951 if (get_alt_mountpoint) {
1952 (void) snprintf(mountpoint, size_mp, "%s%s", altroot,
1953 vp.vfs_mountp);
1954 } else {
1955 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
1956 }
1957 } else {
1958 (void) fclose(fp);
1959 return (1);
1960 }
1961
1962 (void) fclose(fp);
1963
1964 return (BE_SUCCESS);
1965 }
1966
1967 /*
1968 * Function: fix_mountpoint_callback
1969 * Description: This callback function is used to iterate through a BE's
1970 * children filesystems to check if its mountpoint is currently
1971 * set to be mounted at some specified altroot. If so, fix it by
1972 * removing altroot from the beginning of its mountpoint.
1973 *
1974 * Note - There's no way to tell if a child filesystem's
1975 * mountpoint isn't broken, and just happens to begin with
1976 * the altroot we're looking for. In this case, this function
1977 * will errantly remove the altroot portion from the beginning
1978 * of this filesystem's mountpoint.
1979 *
1980 * Parameters:
1981 * zhp - zfs_handle_t pointer to filesystem being processed.
1982 * data - altroot of where BE is to be mounted.
1983 * Returns:
1984 * 0 - Success
1985 * be_errno_t - Failure
1986 * Scope:
1987 * Private
1988 */
1989 static int
fix_mountpoint_callback(zfs_handle_t * zhp,void * data)1990 fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
1991 {
1992 zprop_source_t sourcetype;
1993 char source[ZFS_MAXNAMELEN];
1994 char mountpoint[MAXPATHLEN];
1995 char *zhp_mountpoint = NULL;
1996 char *altroot = data;
1997 int ret = 0;
1998
1999 /* Get dataset's mountpoint and source values */
2000 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2001 sizeof (mountpoint), &sourcetype, source, sizeof (source),
2002 B_FALSE) != 0) {
2003 be_print_err(gettext("fix_mountpoint_callback: "
2004 "failed to get mountpoint and sourcetype for %s\n"),
2005 zfs_get_name(zhp));
2006 ZFS_CLOSE(zhp);
2007 return (BE_ERR_ZFS);
2008 }
2009
2010 /*
2011 * If the mountpoint is not inherited and the mountpoint is not
2012 * 'legacy', this file system potentially needs its mountpoint
2013 * fixed.
2014 */
2015 if (!(sourcetype & ZPROP_SRC_INHERITED) &&
2016 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
2017
2018 /*
2019 * Check if this file system's current mountpoint is
2020 * under the altroot we're fixing it against.
2021 */
2022 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
2023 mountpoint[strlen(altroot)] == '/') {
2024
2025 /*
2026 * Get this dataset's mountpoint relative to the
2027 * altroot.
2028 */
2029 zhp_mountpoint = mountpoint + strlen(altroot);
2030
2031 /* Fix this dataset's mountpoint value */
2032 if (zfs_prop_set(zhp,
2033 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2034 zhp_mountpoint)) {
2035 be_print_err(gettext("fix_mountpoint_callback: "
2036 "failed to set mountpoint for %s to "
2037 "%s: %s\n"), zfs_get_name(zhp),
2038 zhp_mountpoint,
2039 libzfs_error_description(g_zfs));
2040 ret = zfs_err_to_be_err(g_zfs);
2041 ZFS_CLOSE(zhp);
2042 return (ret);
2043 }
2044 }
2045 }
2046
2047 /* Iterate through this dataset's children and fix them */
2048 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
2049 altroot)) != 0) {
2050 ZFS_CLOSE(zhp);
2051 return (ret);
2052 }
2053
2054
2055 ZFS_CLOSE(zhp);
2056 return (0);
2057 }
2058
2059 /*
2060 * Function: be_mount_root
2061 * Description: This function mounts the root dataset of a BE at the
2062 * specified altroot.
2063 * Parameters:
2064 * zhp - zfs_handle_t pointer to root dataset of a BE that is
2065 * to be mounted at altroot.
2066 * altroot - location of where to mount the BE root.
2067 * Return:
2068 * BE_SUCCESS - Success
2069 * be_errno_t - Failure
2070 * Scope:
2071 * Private
2072 */
2073 static int
be_mount_root(zfs_handle_t * zhp,char * altroot)2074 be_mount_root(zfs_handle_t *zhp, char *altroot)
2075 {
2076 char mountpoint[MAXPATHLEN];
2077
2078 /* Get mountpoint property of dataset */
2079 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2080 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2081 be_print_err(gettext("be_mount_root: failed to "
2082 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
2083 libzfs_error_description(g_zfs));
2084 return (zfs_err_to_be_err(g_zfs));
2085 }
2086
2087 /*
2088 * Set the canmount property for the BE's root dataset to 'noauto' just
2089 * in case it's been set to 'on'. We do this so that when we change its
2090 * mountpoint, zfs won't immediately try to mount it.
2091 */
2092 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
2093 != 0) {
2094 be_print_err(gettext("be_mount_root: failed to "
2095 "set canmount property to 'noauto' (%s): %s\n"),
2096 zfs_get_name(zhp), libzfs_error_description(g_zfs));
2097 return (zfs_err_to_be_err(g_zfs));
2098 }
2099
2100 /* Set mountpoint for BE's root filesystem */
2101 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), altroot)
2102 != 0) {
2103 be_print_err(gettext("be_mount_root: failed to "
2104 "set mountpoint of %s to %s: %s\n"),
2105 zfs_get_name(zhp), altroot,
2106 libzfs_error_description(g_zfs));
2107 return (zfs_err_to_be_err(g_zfs));
2108 }
2109
2110 /* Mount the BE's root filesystem */
2111 if (zfs_mount(zhp, NULL, 0) != 0) {
2112 be_print_err(gettext("be_mount_root: failed to "
2113 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
2114 altroot, libzfs_error_description(g_zfs));
2115 /*
2116 * Set this BE's root filesystem 'mountpoint' property
2117 * back to what it was before.
2118 */
2119 (void) zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
2120 mountpoint);
2121 return (zfs_err_to_be_err(g_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