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