Mercurial > illumos > illumos-gate
comparison usr/src/lib/libbe/common/be_create.c @ 13025:3c7681e3e323
PSARC 2010/059 SNAP BE Management
6964804 SNAP BE management into ON
6971379 libbe should capture and give useful error when installgrub or ict.py fails.
6971390 beadm does not support labeled brand zones
6971394 BEADM_ERR_BE_DOES_NOT_EXIST has an extra space
6971397 libbe error messages need internationalization
6971402 Remove be_get_last_zone_be_callback
6971409 be_create_menu returns errors from both be_errno_t and errno sets
author | Glenn Lagasse <glenn.lagasse@oracle.com> |
---|---|
date | Wed, 04 Aug 2010 12:28:19 -0700 |
parents | |
children | 70ccb19abd77 |
comparison
equal
deleted
inserted
replaced
13024:c176c071a066 | 13025:3c7681e3e323 |
---|---|
1 /* | |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License (the "License"). | |
6 * You may not use this file except in compliance with the License. | |
7 * | |
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
9 * or http://www.opensolaris.org/os/licensing. | |
10 * See the License for the specific language governing permissions | |
11 * and limitations under the License. | |
12 * | |
13 * When distributing Covered Code, include this CDDL HEADER in each | |
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 * If applicable, add the following below this CDDL HEADER, with the | |
16 * fields enclosed by brackets "[]" replaced with your own identifying | |
17 * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 * | |
19 * CDDL HEADER END | |
20 */ | |
21 | |
22 /* | |
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. | |
24 */ | |
25 | |
26 /* | |
27 * System includes | |
28 */ | |
29 | |
30 #include <assert.h> | |
31 #include <ctype.h> | |
32 #include <errno.h> | |
33 #include <libgen.h> | |
34 #include <libintl.h> | |
35 #include <libnvpair.h> | |
36 #include <libzfs.h> | |
37 #include <stdio.h> | |
38 #include <stdlib.h> | |
39 #include <string.h> | |
40 #include <sys/mnttab.h> | |
41 #include <sys/mount.h> | |
42 #include <sys/stat.h> | |
43 #include <sys/types.h> | |
44 #include <sys/wait.h> | |
45 #include <unistd.h> | |
46 | |
47 #include <libbe.h> | |
48 #include <libbe_priv.h> | |
49 | |
50 /* Library wide variables */ | |
51 libzfs_handle_t *g_zfs = NULL; | |
52 | |
53 /* Private function prototypes */ | |
54 static int _be_destroy(const char *, be_destroy_data_t *); | |
55 static int be_destroy_zones(char *, char *, be_destroy_data_t *); | |
56 static int be_destroy_zone_roots(char *, be_destroy_data_t *); | |
57 static int be_destroy_zone_roots_callback(zfs_handle_t *, void *); | |
58 static int be_copy_zones(char *, char *, char *); | |
59 static int be_clone_fs_callback(zfs_handle_t *, void *); | |
60 static int be_destroy_callback(zfs_handle_t *, void *); | |
61 static int be_send_fs_callback(zfs_handle_t *, void *); | |
62 static int be_demote_callback(zfs_handle_t *, void *); | |
63 static int be_demote_find_clone_callback(zfs_handle_t *, void *); | |
64 static int be_demote_get_one_clone(zfs_handle_t *, void *); | |
65 static int be_get_snap(char *, char **); | |
66 static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *, | |
67 char *, int); | |
68 static boolean_t be_create_container_ds(char *); | |
69 static char *be_get_zone_be_name(char *root_ds, char *container_ds); | |
70 static int be_zone_root_exists_callback(zfs_handle_t *, void *); | |
71 | |
72 /* ******************************************************************** */ | |
73 /* Public Functions */ | |
74 /* ******************************************************************** */ | |
75 | |
76 /* | |
77 * Function: be_init | |
78 * Description: Creates the initial datasets for a BE and leaves them | |
79 * unpopulated. The resultant BE can be mounted but can't | |
80 * yet be activated or booted. | |
81 * Parameters: | |
82 * be_attrs - pointer to nvlist_t of attributes being passed in. | |
83 * The following attributes are used by this function: | |
84 * | |
85 * BE_ATTR_NEW_BE_NAME *required | |
86 * BE_ATTR_NEW_BE_POOL *required | |
87 * BE_ATTR_ZFS_PROPERTIES *optional | |
88 * BE_ATTR_FS_NAMES *optional | |
89 * BE_ATTR_FS_NUM *optional | |
90 * BE_ATTR_SHARED_FS_NAMES *optional | |
91 * BE_ATTR_SHARED_FS_NUM *optional | |
92 * Return: | |
93 * BE_SUCCESS - Success | |
94 * be_errno_t - Failure | |
95 * Scope: | |
96 * Public | |
97 */ | |
98 int | |
99 be_init(nvlist_t *be_attrs) | |
100 { | |
101 be_transaction_data_t bt = { 0 }; | |
102 zpool_handle_t *zlp; | |
103 nvlist_t *zfs_props = NULL; | |
104 char nbe_root_ds[MAXPATHLEN]; | |
105 char child_fs[MAXPATHLEN]; | |
106 char **fs_names = NULL; | |
107 char **shared_fs_names = NULL; | |
108 uint16_t fs_num = 0; | |
109 uint16_t shared_fs_num = 0; | |
110 int nelem; | |
111 int i; | |
112 int zret = 0, ret = BE_SUCCESS; | |
113 | |
114 /* Initialize libzfs handle */ | |
115 if (!be_zfs_init()) | |
116 return (BE_ERR_INIT); | |
117 | |
118 /* Get new BE name */ | |
119 if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name) | |
120 != 0) { | |
121 be_print_err(gettext("be_init: failed to lookup " | |
122 "BE_ATTR_NEW_BE_NAME attribute\n")); | |
123 return (BE_ERR_INVAL); | |
124 } | |
125 | |
126 /* Validate new BE name */ | |
127 if (!be_valid_be_name(bt.nbe_name)) { | |
128 be_print_err(gettext("be_init: invalid BE name %s\n"), | |
129 bt.nbe_name); | |
130 return (BE_ERR_INVAL); | |
131 } | |
132 | |
133 /* Get zpool name */ | |
134 if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_POOL, &bt.nbe_zpool) | |
135 != 0) { | |
136 be_print_err(gettext("be_init: failed to lookup " | |
137 "BE_ATTR_NEW_BE_POOL attribute\n")); | |
138 return (BE_ERR_INVAL); | |
139 } | |
140 | |
141 /* Get file system attributes */ | |
142 nelem = 0; | |
143 if (nvlist_lookup_pairs(be_attrs, 0, | |
144 BE_ATTR_FS_NUM, DATA_TYPE_UINT16, &fs_num, | |
145 BE_ATTR_FS_NAMES, DATA_TYPE_STRING_ARRAY, &fs_names, &nelem, | |
146 NULL) != 0) { | |
147 be_print_err(gettext("be_init: failed to lookup fs " | |
148 "attributes\n")); | |
149 return (BE_ERR_INVAL); | |
150 } | |
151 if (nelem != fs_num) { | |
152 be_print_err(gettext("be_init: size of FS_NAMES array (%d) " | |
153 "does not match FS_NUM (%d)\n"), nelem, fs_num); | |
154 return (BE_ERR_INVAL); | |
155 } | |
156 | |
157 /* Get shared file system attributes */ | |
158 nelem = 0; | |
159 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
160 BE_ATTR_SHARED_FS_NUM, DATA_TYPE_UINT16, &shared_fs_num, | |
161 BE_ATTR_SHARED_FS_NAMES, DATA_TYPE_STRING_ARRAY, &shared_fs_names, | |
162 &nelem, NULL) != 0) { | |
163 be_print_err(gettext("be_init: failed to lookup " | |
164 "shared fs attributes\n")); | |
165 return (BE_ERR_INVAL); | |
166 } | |
167 if (nelem != shared_fs_num) { | |
168 be_print_err(gettext("be_init: size of SHARED_FS_NAMES " | |
169 "array does not match SHARED_FS_NUM\n")); | |
170 return (BE_ERR_INVAL); | |
171 } | |
172 | |
173 /* Verify that nbe_zpool exists */ | |
174 if ((zlp = zpool_open(g_zfs, bt.nbe_zpool)) == NULL) { | |
175 be_print_err(gettext("be_init: failed to " | |
176 "find existing zpool (%s): %s\n"), bt.nbe_zpool, | |
177 libzfs_error_description(g_zfs)); | |
178 return (zfs_err_to_be_err(g_zfs)); | |
179 } | |
180 zpool_close(zlp); | |
181 | |
182 /* | |
183 * Verify BE container dataset in nbe_zpool exists. | |
184 * If not, create it. | |
185 */ | |
186 if (!be_create_container_ds(bt.nbe_zpool)) | |
187 return (BE_ERR_CREATDS); | |
188 | |
189 /* | |
190 * Verify that nbe_name doesn't already exist in some pool. | |
191 */ | |
192 if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) > 0) { | |
193 be_print_err(gettext("be_init: BE (%s) already exists\n"), | |
194 bt.nbe_name); | |
195 return (BE_ERR_BE_EXISTS); | |
196 } else if (zret < 0) { | |
197 be_print_err(gettext("be_init: zpool_iter failed: %s\n"), | |
198 libzfs_error_description(g_zfs)); | |
199 return (zfs_err_to_be_err(g_zfs)); | |
200 } | |
201 | |
202 /* Generate string for BE's root dataset */ | |
203 be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds, | |
204 sizeof (nbe_root_ds)); | |
205 | |
206 /* | |
207 * Create property list for new BE root dataset. If some | |
208 * zfs properties were already provided by the caller, dup | |
209 * that list. Otherwise initialize a new property list. | |
210 */ | |
211 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
212 BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL) | |
213 != 0) { | |
214 be_print_err(gettext("be_init: failed to lookup " | |
215 "BE_ATTR_ZFS_PROPERTIES attribute\n")); | |
216 return (BE_ERR_INVAL); | |
217 } | |
218 if (zfs_props != NULL) { | |
219 /* Make sure its a unique nvlist */ | |
220 if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) && | |
221 !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) { | |
222 be_print_err(gettext("be_init: ZFS property list " | |
223 "not unique\n")); | |
224 return (BE_ERR_INVAL); | |
225 } | |
226 | |
227 /* Dup the list */ | |
228 if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) { | |
229 be_print_err(gettext("be_init: failed to dup ZFS " | |
230 "property list\n")); | |
231 return (BE_ERR_NOMEM); | |
232 } | |
233 } else { | |
234 /* Initialize new nvlist */ | |
235 if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { | |
236 be_print_err(gettext("be_init: internal " | |
237 "error: out of memory\n")); | |
238 return (BE_ERR_NOMEM); | |
239 } | |
240 } | |
241 | |
242 /* Set the mountpoint property for the root dataset */ | |
243 if (nvlist_add_string(bt.nbe_zfs_props, | |
244 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), "/") != 0) { | |
245 be_print_err(gettext("be_init: internal error " | |
246 "out of memory\n")); | |
247 ret = BE_ERR_NOMEM; | |
248 goto done; | |
249 } | |
250 | |
251 /* Set the 'canmount' property */ | |
252 if (nvlist_add_string(bt.nbe_zfs_props, | |
253 zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) { | |
254 be_print_err(gettext("be_init: internal error " | |
255 "out of memory\n")); | |
256 ret = BE_ERR_NOMEM; | |
257 goto done; | |
258 } | |
259 | |
260 /* Create BE root dataset for the new BE */ | |
261 if (zfs_create(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM, | |
262 bt.nbe_zfs_props) != 0) { | |
263 be_print_err(gettext("be_init: failed to " | |
264 "create BE root dataset (%s): %s\n"), nbe_root_ds, | |
265 libzfs_error_description(g_zfs)); | |
266 ret = zfs_err_to_be_err(g_zfs); | |
267 goto done; | |
268 } | |
269 | |
270 /* Set UUID for new BE */ | |
271 if ((ret = be_set_uuid(nbe_root_ds)) != BE_SUCCESS) { | |
272 be_print_err(gettext("be_init: failed to " | |
273 "set uuid for new BE\n")); | |
274 } | |
275 | |
276 /* | |
277 * Clear the mountpoint property so that the non-shared | |
278 * file systems created below inherit their mountpoints. | |
279 */ | |
280 (void) nvlist_remove(bt.nbe_zfs_props, | |
281 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), DATA_TYPE_STRING); | |
282 | |
283 /* Create the new BE's non-shared file systems */ | |
284 for (i = 0; i < fs_num && fs_names[i]; i++) { | |
285 /* | |
286 * If fs == "/", skip it; | |
287 * we already created the root dataset | |
288 */ | |
289 if (strcmp(fs_names[i], "/") == 0) | |
290 continue; | |
291 | |
292 /* Generate string for file system */ | |
293 (void) snprintf(child_fs, sizeof (child_fs), "%s%s", | |
294 nbe_root_ds, fs_names[i]); | |
295 | |
296 /* Create file system */ | |
297 if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, | |
298 bt.nbe_zfs_props) != 0) { | |
299 be_print_err(gettext("be_init: failed to create " | |
300 "BE's child dataset (%s): %s\n"), child_fs, | |
301 libzfs_error_description(g_zfs)); | |
302 ret = zfs_err_to_be_err(g_zfs); | |
303 goto done; | |
304 } | |
305 } | |
306 | |
307 /* Create the new BE's shared file systems */ | |
308 if (shared_fs_num > 0) { | |
309 nvlist_t *props = NULL; | |
310 | |
311 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { | |
312 be_print_err(gettext("be_init: nvlist_alloc failed\n")); | |
313 ret = BE_ERR_NOMEM; | |
314 goto done; | |
315 } | |
316 | |
317 for (i = 0; i < shared_fs_num; i++) { | |
318 /* Generate string for shared file system */ | |
319 (void) snprintf(child_fs, sizeof (child_fs), "%s%s", | |
320 bt.nbe_zpool, shared_fs_names[i]); | |
321 | |
322 if (nvlist_add_string(props, | |
323 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), | |
324 shared_fs_names[i]) != 0) { | |
325 be_print_err(gettext("be_init: " | |
326 "internal error: out of memory\n")); | |
327 nvlist_free(props); | |
328 ret = BE_ERR_NOMEM; | |
329 goto done; | |
330 } | |
331 | |
332 /* Create file system if it doesn't already exist */ | |
333 if (zfs_dataset_exists(g_zfs, child_fs, | |
334 ZFS_TYPE_FILESYSTEM)) { | |
335 continue; | |
336 } | |
337 if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, | |
338 props) != 0) { | |
339 be_print_err(gettext("be_init: failed to " | |
340 "create BE's shared dataset (%s): %s\n"), | |
341 child_fs, libzfs_error_description(g_zfs)); | |
342 ret = zfs_err_to_be_err(g_zfs); | |
343 nvlist_free(props); | |
344 goto done; | |
345 } | |
346 } | |
347 | |
348 nvlist_free(props); | |
349 } | |
350 | |
351 done: | |
352 if (bt.nbe_zfs_props != NULL) | |
353 nvlist_free(bt.nbe_zfs_props); | |
354 | |
355 be_zfs_fini(); | |
356 | |
357 return (ret); | |
358 } | |
359 | |
360 /* | |
361 * Function: be_destroy | |
362 * Description: Destroy a BE and all of its children datasets, snapshots and | |
363 * zones that belong to the parent BE. | |
364 * Parameters: | |
365 * be_attrs - pointer to nvlist_t of attributes being passed in. | |
366 * The following attributes are used by this function: | |
367 * | |
368 * BE_ATTR_ORIG_BE_NAME *required | |
369 * BE_ATTR_DESTROY_FLAGS *optional | |
370 * Return: | |
371 * BE_SUCCESS - Success | |
372 * be_errno_t - Failure | |
373 * Scope: | |
374 * Public | |
375 */ | |
376 int | |
377 be_destroy(nvlist_t *be_attrs) | |
378 { | |
379 zfs_handle_t *zhp = NULL; | |
380 be_transaction_data_t bt = { 0 }; | |
381 be_transaction_data_t cur_bt = { 0 }; | |
382 be_destroy_data_t dd = { 0 }; | |
383 int ret = BE_SUCCESS; | |
384 uint16_t flags = 0; | |
385 int zret; | |
386 char obe_root_ds[MAXPATHLEN]; | |
387 char *mp = NULL; | |
388 | |
389 /* Initialize libzfs handle */ | |
390 if (!be_zfs_init()) | |
391 return (BE_ERR_INIT); | |
392 | |
393 /* Get name of BE to delete */ | |
394 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name) | |
395 != 0) { | |
396 be_print_err(gettext("be_destroy: failed to lookup " | |
397 "BE_ATTR_ORIG_BE_NAME attribute\n")); | |
398 return (BE_ERR_INVAL); | |
399 } | |
400 | |
401 /* | |
402 * Validate BE name. If valid, then check that the original BE is not | |
403 * the active BE. If it is the 'active' BE then return an error code | |
404 * since we can't destroy the active BE. | |
405 */ | |
406 if (!be_valid_be_name(bt.obe_name)) { | |
407 be_print_err(gettext("be_destroy: invalid BE name %s\n"), | |
408 bt.obe_name); | |
409 return (BE_ERR_INVAL); | |
410 } else if (bt.obe_name != NULL) { | |
411 if ((ret = be_find_current_be(&cur_bt)) != BE_SUCCESS) { | |
412 return (ret); | |
413 } | |
414 if (strcmp(cur_bt.obe_name, bt.obe_name) == 0) { | |
415 return (BE_ERR_DESTROY_CURR_BE); | |
416 } | |
417 } | |
418 | |
419 /* Get destroy flags if provided */ | |
420 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
421 BE_ATTR_DESTROY_FLAGS, DATA_TYPE_UINT16, &flags, NULL) | |
422 != 0) { | |
423 be_print_err(gettext("be_destroy: failed to lookup " | |
424 "BE_ATTR_DESTROY_FLAGS attribute\n")); | |
425 return (BE_ERR_INVAL); | |
426 } | |
427 | |
428 dd.destroy_snaps = flags & BE_DESTROY_FLAG_SNAPSHOTS; | |
429 dd.force_unmount = flags & BE_DESTROY_FLAG_FORCE_UNMOUNT; | |
430 | |
431 /* Find which zpool obe_name lives in */ | |
432 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { | |
433 be_print_err(gettext("be_destroy: failed to find zpool " | |
434 "for BE (%s)\n"), bt.obe_name); | |
435 return (BE_ERR_BE_NOENT); | |
436 } else if (zret < 0) { | |
437 be_print_err(gettext("be_destroy: zpool_iter failed: %s\n"), | |
438 libzfs_error_description(g_zfs)); | |
439 return (zfs_err_to_be_err(g_zfs)); | |
440 } | |
441 | |
442 /* Generate string for obe_name's root dataset */ | |
443 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, | |
444 sizeof (obe_root_ds)); | |
445 bt.obe_root_ds = obe_root_ds; | |
446 | |
447 /* | |
448 * Detect if the BE to destroy has the 'active on boot' property set. | |
449 * If so, set the 'active on boot' property on the the 'active' BE. | |
450 */ | |
451 if (be_is_active_on_boot(bt.obe_name)) { | |
452 if ((ret = be_activate_current_be()) != BE_SUCCESS) { | |
453 be_print_err(gettext("be_destroy: failed to " | |
454 "make the current BE 'active on boot'\n")); | |
455 return (ret); | |
456 } | |
457 } | |
458 | |
459 /* Get handle to BE's root dataset */ | |
460 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == | |
461 NULL) { | |
462 be_print_err(gettext("be_destroy: failed to " | |
463 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, | |
464 libzfs_error_description(g_zfs)); | |
465 return (zfs_err_to_be_err(g_zfs)); | |
466 } | |
467 | |
468 /* Get the UUID of the global BE */ | |
469 if (be_get_uuid(zfs_get_name(zhp), &dd.gz_be_uuid) != BE_SUCCESS) { | |
470 be_print_err(gettext("be_destroy: BE has no UUID (%s)\n"), | |
471 zfs_get_name(zhp)); | |
472 } | |
473 | |
474 /* | |
475 * If the global BE is mounted, make sure we've been given the | |
476 * flag to forcibly unmount it. | |
477 */ | |
478 if (zfs_is_mounted(zhp, &mp)) { | |
479 if (!(dd.force_unmount)) { | |
480 be_print_err(gettext("be_destroy: " | |
481 "%s is currently mounted at %s, cannot destroy\n"), | |
482 bt.obe_name, mp != NULL ? mp : "<unknown>"); | |
483 | |
484 free(mp); | |
485 ZFS_CLOSE(zhp); | |
486 return (BE_ERR_MOUNTED); | |
487 } | |
488 free(mp); | |
489 } | |
490 | |
491 /* | |
492 * Destroy the non-global zone BE's if we are in the global zone | |
493 * and there is a UUID associated with the global zone BE | |
494 */ | |
495 if (getzoneid() == GLOBAL_ZONEID && !uuid_is_null(dd.gz_be_uuid)) { | |
496 if ((ret = be_destroy_zones(bt.obe_name, bt.obe_root_ds, &dd)) | |
497 != BE_SUCCESS) { | |
498 be_print_err(gettext("be_destroy: failed to " | |
499 "destroy one or more zones for BE %s\n"), | |
500 bt.obe_name); | |
501 goto done; | |
502 } | |
503 } | |
504 | |
505 /* Unmount the BE if it was mounted */ | |
506 if (zfs_is_mounted(zhp, NULL)) { | |
507 if ((ret = _be_unmount(bt.obe_name, BE_UNMOUNT_FLAG_FORCE)) | |
508 != BE_SUCCESS) { | |
509 be_print_err(gettext("be_destroy: " | |
510 "failed to unmount %s\n"), bt.obe_name); | |
511 ZFS_CLOSE(zhp); | |
512 return (ret); | |
513 } | |
514 } | |
515 ZFS_CLOSE(zhp); | |
516 | |
517 /* Destroy this BE */ | |
518 if ((ret = _be_destroy((const char *)bt.obe_root_ds, &dd)) | |
519 != BE_SUCCESS) { | |
520 goto done; | |
521 } | |
522 | |
523 /* Remove BE's entry from the boot menu */ | |
524 if (getzoneid() == GLOBAL_ZONEID) { | |
525 if ((ret = be_remove_menu(bt.obe_name, bt.obe_zpool, NULL)) | |
526 != BE_SUCCESS) { | |
527 be_print_err(gettext("be_destroy: failed to " | |
528 "remove BE %s from the boot menu\n"), | |
529 bt.obe_root_ds); | |
530 goto done; | |
531 } | |
532 } | |
533 | |
534 done: | |
535 be_zfs_fini(); | |
536 | |
537 return (ret); | |
538 } | |
539 | |
540 /* | |
541 * Function: be_copy | |
542 * Description: This function makes a copy of an existing BE. If the original | |
543 * BE and the new BE are in the same pool, it uses zfs cloning to | |
544 * create the new BE, otherwise it does a physical copy. | |
545 * If the original BE name isn't provided, it uses the currently | |
546 * booted BE. If the new BE name isn't provided, it creates an | |
547 * auto named BE and returns that name to the caller. | |
548 * Parameters: | |
549 * be_attrs - pointer to nvlist_t of attributes being passed in. | |
550 * The following attributes are used by this function: | |
551 * | |
552 * BE_ATTR_ORIG_BE_NAME *optional | |
553 * BE_ATTR_SNAP_NAME *optional | |
554 * BE_ATTR_NEW_BE_NAME *optional | |
555 * BE_ATTR_NEW_BE_POOL *optional | |
556 * BE_ATTR_NEW_BE_DESC *optional | |
557 * BE_ATTR_ZFS_PROPERTIES *optional | |
558 * BE_ATTR_POLICY *optional | |
559 * | |
560 * If the BE_ATTR_NEW_BE_NAME was not passed in, upon | |
561 * successful BE creation, the following attribute values | |
562 * will be returned to the caller by setting them in the | |
563 * be_attrs parameter passed in: | |
564 * | |
565 * BE_ATTR_SNAP_NAME | |
566 * BE_ATTR_NEW_BE_NAME | |
567 * Return: | |
568 * BE_SUCCESS - Success | |
569 * be_errno_t - Failure | |
570 * Scope: | |
571 * Public | |
572 */ | |
573 int | |
574 be_copy(nvlist_t *be_attrs) | |
575 { | |
576 be_transaction_data_t bt = { 0 }; | |
577 be_fs_list_data_t fld = { 0 }; | |
578 zfs_handle_t *zhp = NULL; | |
579 nvlist_t *zfs_props = NULL; | |
580 uuid_t uu = { 0 }; | |
581 char obe_root_ds[MAXPATHLEN]; | |
582 char nbe_root_ds[MAXPATHLEN]; | |
583 char ss[MAXPATHLEN]; | |
584 char *new_mp = NULL; | |
585 boolean_t autoname = B_FALSE; | |
586 boolean_t be_created = B_FALSE; | |
587 int i; | |
588 int zret; | |
589 int ret = BE_SUCCESS; | |
590 | |
591 /* Initialize libzfs handle */ | |
592 if (!be_zfs_init()) | |
593 return (BE_ERR_INIT); | |
594 | |
595 /* Get original BE name */ | |
596 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
597 BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &bt.obe_name, NULL) != 0) { | |
598 be_print_err(gettext("be_copy: failed to lookup " | |
599 "BE_ATTR_ORIG_BE_NAME attribute\n")); | |
600 return (BE_ERR_INVAL); | |
601 } | |
602 | |
603 /* If original BE name not provided, use current BE */ | |
604 if (bt.obe_name == NULL) { | |
605 if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) { | |
606 return (ret); | |
607 } | |
608 } else { | |
609 /* Validate original BE name */ | |
610 if (!be_valid_be_name(bt.obe_name)) { | |
611 be_print_err(gettext("be_copy: " | |
612 "invalid BE name %s\n"), bt.obe_name); | |
613 return (BE_ERR_INVAL); | |
614 } | |
615 } | |
616 | |
617 /* Find which zpool obe_name lives in */ | |
618 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { | |
619 be_print_err(gettext("be_copy: failed to " | |
620 "find zpool for BE (%s)\n"), bt.obe_name); | |
621 return (BE_ERR_BE_NOENT); | |
622 } else if (zret < 0) { | |
623 be_print_err(gettext("be_copy: " | |
624 "zpool_iter failed: %s\n"), | |
625 libzfs_error_description(g_zfs)); | |
626 return (zfs_err_to_be_err(g_zfs)); | |
627 } | |
628 | |
629 /* Get snapshot name of original BE if one was provided */ | |
630 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
631 BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &bt.obe_snap_name, NULL) | |
632 != 0) { | |
633 be_print_err(gettext("be_copy: failed to lookup " | |
634 "BE_ATTR_SNAP_NAME attribute\n")); | |
635 return (BE_ERR_INVAL); | |
636 } | |
637 | |
638 /* Get new BE name */ | |
639 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
640 BE_ATTR_NEW_BE_NAME, DATA_TYPE_STRING, &bt.nbe_name, NULL) | |
641 != 0) { | |
642 be_print_err(gettext("be_copy: failed to lookup " | |
643 "BE_ATTR_NEW_BE_NAME attribute\n")); | |
644 return (BE_ERR_INVAL); | |
645 } | |
646 | |
647 /* Get zpool name to create new BE in */ | |
648 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
649 BE_ATTR_NEW_BE_POOL, DATA_TYPE_STRING, &bt.nbe_zpool, NULL) != 0) { | |
650 be_print_err(gettext("be_copy: failed to lookup " | |
651 "BE_ATTR_NEW_BE_POOL attribute\n")); | |
652 return (BE_ERR_INVAL); | |
653 } | |
654 | |
655 /* Get new BE's description if one was provided */ | |
656 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
657 BE_ATTR_NEW_BE_DESC, DATA_TYPE_STRING, &bt.nbe_desc, NULL) != 0) { | |
658 be_print_err(gettext("be_copy: failed to lookup " | |
659 "BE_ATTR_NEW_BE_DESC attribute\n")); | |
660 return (BE_ERR_INVAL); | |
661 } | |
662 | |
663 /* Get BE policy to create this snapshot under */ | |
664 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
665 BE_ATTR_POLICY, DATA_TYPE_STRING, &bt.policy, NULL) != 0) { | |
666 be_print_err(gettext("be_copy: failed to lookup " | |
667 "BE_ATTR_POLICY attribute\n")); | |
668 return (BE_ERR_INVAL); | |
669 } | |
670 | |
671 /* | |
672 * Create property list for new BE root dataset. If some | |
673 * zfs properties were already provided by the caller, dup | |
674 * that list. Otherwise initialize a new property list. | |
675 */ | |
676 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, | |
677 BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL) | |
678 != 0) { | |
679 be_print_err(gettext("be_copy: failed to lookup " | |
680 "BE_ATTR_ZFS_PROPERTIES attribute\n")); | |
681 return (BE_ERR_INVAL); | |
682 } | |
683 if (zfs_props != NULL) { | |
684 /* Make sure its a unique nvlist */ | |
685 if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) && | |
686 !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) { | |
687 be_print_err(gettext("be_copy: ZFS property list " | |
688 "not unique\n")); | |
689 return (BE_ERR_INVAL); | |
690 } | |
691 | |
692 /* Dup the list */ | |
693 if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) { | |
694 be_print_err(gettext("be_copy: " | |
695 "failed to dup ZFS property list\n")); | |
696 return (BE_ERR_NOMEM); | |
697 } | |
698 } else { | |
699 /* Initialize new nvlist */ | |
700 if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { | |
701 be_print_err(gettext("be_copy: internal " | |
702 "error: out of memory\n")); | |
703 return (BE_ERR_NOMEM); | |
704 } | |
705 } | |
706 | |
707 /* | |
708 * If new BE name provided, validate the BE name and then verify | |
709 * that new BE name doesn't already exist in some pool. | |
710 */ | |
711 if (bt.nbe_name) { | |
712 /* Validate original BE name */ | |
713 if (!be_valid_be_name(bt.nbe_name)) { | |
714 be_print_err(gettext("be_copy: " | |
715 "invalid BE name %s\n"), bt.nbe_name); | |
716 ret = BE_ERR_INVAL; | |
717 goto done; | |
718 } | |
719 | |
720 /* Verify it doesn't already exist */ | |
721 if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) | |
722 > 0) { | |
723 be_print_err(gettext("be_copy: BE (%s) already " | |
724 "exists\n"), bt.nbe_name); | |
725 ret = BE_ERR_BE_EXISTS; | |
726 goto done; | |
727 } else if (zret < 0) { | |
728 be_print_err(gettext("be_copy: zpool_iter failed: " | |
729 "%s\n"), libzfs_error_description(g_zfs)); | |
730 ret = zfs_err_to_be_err(g_zfs); | |
731 goto done; | |
732 } | |
733 } else { | |
734 /* | |
735 * If an auto named BE is desired, it must be in the same | |
736 * pool is the original BE. | |
737 */ | |
738 if (bt.nbe_zpool != NULL) { | |
739 be_print_err(gettext("be_copy: cannot specify pool " | |
740 "name when creating an auto named BE\n")); | |
741 ret = BE_ERR_INVAL; | |
742 goto done; | |
743 } | |
744 | |
745 /* | |
746 * Generate auto named BE | |
747 */ | |
748 if ((bt.nbe_name = be_auto_be_name(bt.obe_name)) | |
749 == NULL) { | |
750 be_print_err(gettext("be_copy: " | |
751 "failed to generate auto BE name\n")); | |
752 ret = BE_ERR_AUTONAME; | |
753 goto done; | |
754 } | |
755 | |
756 autoname = B_TRUE; | |
757 } | |
758 | |
759 /* | |
760 * If zpool name to create new BE in is not provided, | |
761 * create new BE in original BE's pool. | |
762 */ | |
763 if (bt.nbe_zpool == NULL) { | |
764 bt.nbe_zpool = bt.obe_zpool; | |
765 } | |
766 | |
767 /* Get root dataset names for obe_name and nbe_name */ | |
768 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, | |
769 sizeof (obe_root_ds)); | |
770 be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds, | |
771 sizeof (nbe_root_ds)); | |
772 | |
773 bt.obe_root_ds = obe_root_ds; | |
774 bt.nbe_root_ds = nbe_root_ds; | |
775 | |
776 /* | |
777 * If an existing snapshot name has been provided to create from, | |
778 * verify that it exists for the original BE's root dataset. | |
779 */ | |
780 if (bt.obe_snap_name != NULL) { | |
781 | |
782 /* Generate dataset name for snapshot to use. */ | |
783 (void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds, | |
784 bt.obe_snap_name); | |
785 | |
786 /* Verify snapshot exists */ | |
787 if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) { | |
788 be_print_err(gettext("be_copy: " | |
789 "snapshot does not exist (%s): %s\n"), ss, | |
790 libzfs_error_description(g_zfs)); | |
791 ret = BE_ERR_SS_NOENT; | |
792 goto done; | |
793 } | |
794 } else { | |
795 /* | |
796 * Else snapshot name was not provided, generate an | |
797 * auto named snapshot to use as its origin. | |
798 */ | |
799 if ((ret = _be_create_snapshot(bt.obe_name, | |
800 &bt.obe_snap_name, bt.policy)) != BE_SUCCESS) { | |
801 be_print_err(gettext("be_copy: " | |
802 "failed to create auto named snapshot\n")); | |
803 goto done; | |
804 } | |
805 | |
806 if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, | |
807 bt.obe_snap_name) != 0) { | |
808 be_print_err(gettext("be_copy: " | |
809 "failed to add snap name to be_attrs\n")); | |
810 ret = BE_ERR_NOMEM; | |
811 goto done; | |
812 } | |
813 } | |
814 | |
815 /* Get handle to original BE's root dataset. */ | |
816 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) | |
817 == NULL) { | |
818 be_print_err(gettext("be_copy: failed to " | |
819 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, | |
820 libzfs_error_description(g_zfs)); | |
821 ret = zfs_err_to_be_err(g_zfs); | |
822 goto done; | |
823 } | |
824 | |
825 /* If original BE is currently mounted, record its altroot. */ | |
826 if (zfs_is_mounted(zhp, &bt.obe_altroot) && bt.obe_altroot == NULL) { | |
827 be_print_err(gettext("be_copy: failed to " | |
828 "get altroot of mounted BE %s: %s\n"), | |
829 bt.obe_name, libzfs_error_description(g_zfs)); | |
830 ret = zfs_err_to_be_err(g_zfs); | |
831 goto done; | |
832 } | |
833 | |
834 if (strcmp(bt.obe_zpool, bt.nbe_zpool) == 0) { | |
835 | |
836 /* Do clone */ | |
837 | |
838 /* | |
839 * Iterate through original BE's datasets and clone | |
840 * them to create new BE. This call will end up closing | |
841 * the zfs handle passed in whether it succeeds for fails. | |
842 */ | |
843 if ((ret = be_clone_fs_callback(zhp, &bt)) != 0) { | |
844 zhp = NULL; | |
845 /* Creating clone BE failed */ | |
846 if (!autoname || ret != BE_ERR_BE_EXISTS) { | |
847 be_print_err(gettext("be_copy: " | |
848 "failed to clone new BE (%s) from " | |
849 "orig BE (%s)\n"), | |
850 bt.nbe_name, bt.obe_name); | |
851 ret = BE_ERR_CLONE; | |
852 goto done; | |
853 } | |
854 | |
855 /* | |
856 * We failed to create the new BE because a BE with | |
857 * the auto-name we generated above has since come | |
858 * into existence. Regenerate a new auto-name | |
859 * and retry. | |
860 */ | |
861 for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) { | |
862 | |
863 /* Sleep 1 before retrying */ | |
864 (void) sleep(1); | |
865 | |
866 /* Generate new auto BE name */ | |
867 free(bt.nbe_name); | |
868 if ((bt.nbe_name = be_auto_be_name(bt.obe_name)) | |
869 == NULL) { | |
870 be_print_err(gettext("be_copy: " | |
871 "failed to generate auto " | |
872 "BE name\n")); | |
873 ret = BE_ERR_AUTONAME; | |
874 goto done; | |
875 } | |
876 | |
877 /* | |
878 * Regenerate string for new BE's | |
879 * root dataset name | |
880 */ | |
881 be_make_root_ds(bt.nbe_zpool, bt.nbe_name, | |
882 nbe_root_ds, sizeof (nbe_root_ds)); | |
883 bt.nbe_root_ds = nbe_root_ds; | |
884 | |
885 /* | |
886 * Get handle to original BE's root dataset. | |
887 */ | |
888 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, | |
889 ZFS_TYPE_FILESYSTEM)) == NULL) { | |
890 be_print_err(gettext("be_copy: " | |
891 "failed to open BE root dataset " | |
892 "(%s): %s\n"), bt.obe_root_ds, | |
893 libzfs_error_description(g_zfs)); | |
894 ret = zfs_err_to_be_err(g_zfs); | |
895 goto done; | |
896 } | |
897 | |
898 /* | |
899 * Try to clone the BE again. This | |
900 * call will end up closing the zfs | |
901 * handle passed in whether it | |
902 * succeeds or fails. | |
903 */ | |
904 ret = be_clone_fs_callback(zhp, &bt); | |
905 zhp = NULL; | |
906 if (ret == 0) { | |
907 break; | |
908 } else if (ret != BE_ERR_BE_EXISTS) { | |
909 be_print_err(gettext("be_copy: " | |
910 "failed to clone new BE " | |
911 "(%s) from orig BE (%s)\n"), | |
912 bt.nbe_name, bt.obe_name); | |
913 ret = BE_ERR_CLONE; | |
914 goto done; | |
915 } | |
916 } | |
917 | |
918 /* | |
919 * If we've exhausted the maximum number of | |
920 * tries, free the auto BE name and return | |
921 * error. | |
922 */ | |
923 if (i == BE_AUTO_NAME_MAX_TRY) { | |
924 be_print_err(gettext("be_copy: failed " | |
925 "to create unique auto BE name\n")); | |
926 free(bt.nbe_name); | |
927 bt.nbe_name = NULL; | |
928 ret = BE_ERR_AUTONAME; | |
929 goto done; | |
930 } | |
931 } | |
932 zhp = NULL; | |
933 | |
934 } else { | |
935 | |
936 /* Do copy (i.e. send BE datasets via zfs_send/recv) */ | |
937 | |
938 /* | |
939 * Verify BE container dataset in nbe_zpool exists. | |
940 * If not, create it. | |
941 */ | |
942 if (!be_create_container_ds(bt.nbe_zpool)) { | |
943 ret = BE_ERR_CREATDS; | |
944 goto done; | |
945 } | |
946 | |
947 /* | |
948 * Iterate through original BE's datasets and send | |
949 * them to the other pool. This call will end up closing | |
950 * the zfs handle passed in whether it succeeds or fails. | |
951 */ | |
952 if ((ret = be_send_fs_callback(zhp, &bt)) != 0) { | |
953 be_print_err(gettext("be_copy: failed to " | |
954 "send BE (%s) to pool (%s)\n"), bt.obe_name, | |
955 bt.nbe_zpool); | |
956 ret = BE_ERR_COPY; | |
957 zhp = NULL; | |
958 goto done; | |
959 } | |
960 zhp = NULL; | |
961 } | |
962 | |
963 /* | |
964 * Set flag to note that the dataset(s) for the new BE have been | |
965 * successfully created so that if a failure happens from this point | |
966 * on, we know to cleanup these datasets. | |
967 */ | |
968 be_created = B_TRUE; | |
969 | |
970 /* | |
971 * Validate that the new BE is mountable. | |
972 * Do not attempt to mount non-global zone datasets | |
973 * since they are not cloned yet. | |
974 */ | |
975 if ((ret = _be_mount(bt.nbe_name, &new_mp, BE_MOUNT_FLAG_NO_ZONES)) | |
976 != BE_SUCCESS) { | |
977 be_print_err(gettext("be_copy: failed to " | |
978 "mount newly created BE\n")); | |
979 (void) _be_unmount(bt.nbe_name, 0); | |
980 goto done; | |
981 } | |
982 | |
983 /* Set UUID for new BE */ | |
984 if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) { | |
985 be_print_err(gettext("be_copy: failed to " | |
986 "set uuid for new BE\n")); | |
987 } | |
988 | |
989 /* | |
990 * Process zones outside of the private BE namespace. | |
991 * This has to be done here because we need the uuid set in the | |
992 * root dataset of the new BE. The uuid is use to set the parentbe | |
993 * property for the new zones datasets. | |
994 */ | |
995 if (getzoneid() == GLOBAL_ZONEID && | |
996 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { | |
997 if ((ret = be_copy_zones(bt.obe_name, bt.obe_root_ds, | |
998 bt.nbe_root_ds)) != BE_SUCCESS) { | |
999 be_print_err(gettext("be_copy: failed to process " | |
1000 "zones\n")); | |
1001 goto done; | |
1002 } | |
1003 } | |
1004 | |
1005 /* | |
1006 * Generate a list of file systems from the original BE that are | |
1007 * legacy mounted. We use this list to determine which entries in | |
1008 * vfstab we need to update for the new BE we've just created. | |
1009 */ | |
1010 if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL, | |
1011 &fld)) != BE_SUCCESS) { | |
1012 be_print_err(gettext("be_copy: failed to " | |
1013 "get legacy mounted file system list for %s\n"), | |
1014 bt.obe_name); | |
1015 goto done; | |
1016 } | |
1017 | |
1018 /* | |
1019 * Update new BE's vfstab. | |
1020 */ | |
1021 if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool, | |
1022 &fld, new_mp)) != BE_SUCCESS) { | |
1023 be_print_err(gettext("be_copy: failed to " | |
1024 "update new BE's vfstab (%s)\n"), bt.nbe_name); | |
1025 goto done; | |
1026 } | |
1027 | |
1028 /* Unmount the new BE */ | |
1029 if ((ret = _be_unmount(bt.nbe_name, 0)) != BE_SUCCESS) { | |
1030 be_print_err(gettext("be_copy: failed to " | |
1031 "unmount newly created BE\n")); | |
1032 goto done; | |
1033 } | |
1034 | |
1035 /* | |
1036 * Add boot menu entry for newly created clone | |
1037 */ | |
1038 if (getzoneid() == GLOBAL_ZONEID && | |
1039 (ret = be_append_menu(bt.nbe_name, bt.nbe_zpool, | |
1040 NULL, bt.obe_root_ds, bt.nbe_desc)) != BE_SUCCESS) { | |
1041 be_print_err(gettext("be_copy: failed to " | |
1042 "add BE (%s) to boot menu\n"), bt.nbe_name); | |
1043 goto done; | |
1044 } | |
1045 | |
1046 /* | |
1047 * If we succeeded in creating an auto named BE, set its policy | |
1048 * type and return the auto generated name to the caller by storing | |
1049 * it in the nvlist passed in by the caller. | |
1050 */ | |
1051 if (autoname) { | |
1052 /* Get handle to new BE's root dataset. */ | |
1053 if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds, | |
1054 ZFS_TYPE_FILESYSTEM)) == NULL) { | |
1055 be_print_err(gettext("be_copy: failed to " | |
1056 "open BE root dataset (%s): %s\n"), bt.nbe_root_ds, | |
1057 libzfs_error_description(g_zfs)); | |
1058 ret = zfs_err_to_be_err(g_zfs); | |
1059 goto done; | |
1060 } | |
1061 | |
1062 /* | |
1063 * Set the policy type property into the new BE's root dataset | |
1064 */ | |
1065 if (bt.policy == NULL) { | |
1066 /* If no policy type provided, use default type */ | |
1067 bt.policy = be_default_policy(); | |
1068 } | |
1069 | |
1070 if (zfs_prop_set(zhp, BE_POLICY_PROPERTY, bt.policy) != 0) { | |
1071 be_print_err(gettext("be_copy: failed to " | |
1072 "set BE policy for %s: %s\n"), bt.nbe_name, | |
1073 libzfs_error_description(g_zfs)); | |
1074 ret = zfs_err_to_be_err(g_zfs); | |
1075 goto done; | |
1076 } | |
1077 | |
1078 /* | |
1079 * Return the auto generated name to the caller | |
1080 */ | |
1081 if (bt.nbe_name) { | |
1082 if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, | |
1083 bt.nbe_name) != 0) { | |
1084 be_print_err(gettext("be_copy: failed to " | |
1085 "add snap name to be_attrs\n")); | |
1086 } | |
1087 } | |
1088 } | |
1089 | |
1090 done: | |
1091 ZFS_CLOSE(zhp); | |
1092 be_free_fs_list(&fld); | |
1093 | |
1094 if (bt.nbe_zfs_props != NULL) | |
1095 nvlist_free(bt.nbe_zfs_props); | |
1096 | |
1097 free(bt.obe_altroot); | |
1098 free(new_mp); | |
1099 | |
1100 /* | |
1101 * If a failure occurred and we already created the datasets for | |
1102 * the new boot environment, destroy them. | |
1103 */ | |
1104 if (ret != BE_SUCCESS && be_created) { | |
1105 be_destroy_data_t cdd = { 0 }; | |
1106 | |
1107 cdd.force_unmount = B_TRUE; | |
1108 | |
1109 be_print_err(gettext("be_copy: " | |
1110 "destroying partially created boot environment\n")); | |
1111 | |
1112 if (getzoneid() == GLOBAL_ZONEID && be_get_uuid(bt.nbe_root_ds, | |
1113 &cdd.gz_be_uuid) == 0) | |
1114 (void) be_destroy_zones(bt.nbe_name, bt.nbe_root_ds, | |
1115 &cdd); | |
1116 | |
1117 (void) _be_destroy(bt.nbe_root_ds, &cdd); | |
1118 } | |
1119 | |
1120 be_zfs_fini(); | |
1121 | |
1122 return (ret); | |
1123 } | |
1124 | |
1125 /* ******************************************************************** */ | |
1126 /* Semi-Private Functions */ | |
1127 /* ******************************************************************** */ | |
1128 | |
1129 /* | |
1130 * Function: be_find_zpool_callback | |
1131 * Description: Callback function used to find the pool that a BE lives in. | |
1132 * Parameters: | |
1133 * zlp - zpool_handle_t pointer for the current pool being | |
1134 * looked at. | |
1135 * data - be_transaction_data_t pointer providing information | |
1136 * about the BE that's being searched for. | |
1137 * This function uses the obe_name member of this | |
1138 * parameter to use as the BE name to search for. | |
1139 * Upon successfully locating the BE, it populates | |
1140 * obe_zpool with the pool name that the BE is found in. | |
1141 * Returns: | |
1142 * 1 - BE exists in this pool. | |
1143 * 0 - BE does not exist in this pool. | |
1144 * Scope: | |
1145 * Semi-private (library wide use only) | |
1146 */ | |
1147 int | |
1148 be_find_zpool_callback(zpool_handle_t *zlp, void *data) | |
1149 { | |
1150 be_transaction_data_t *bt = data; | |
1151 const char *zpool = zpool_get_name(zlp); | |
1152 char be_root_ds[MAXPATHLEN]; | |
1153 | |
1154 /* | |
1155 * Generate string for the BE's root dataset | |
1156 */ | |
1157 be_make_root_ds(zpool, bt->obe_name, be_root_ds, sizeof (be_root_ds)); | |
1158 | |
1159 /* | |
1160 * Check if dataset exists | |
1161 */ | |
1162 if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) { | |
1163 /* BE's root dataset exists in zpool */ | |
1164 bt->obe_zpool = strdup(zpool); | |
1165 zpool_close(zlp); | |
1166 return (1); | |
1167 } | |
1168 | |
1169 zpool_close(zlp); | |
1170 return (0); | |
1171 } | |
1172 | |
1173 /* | |
1174 * Function: be_exists_callback | |
1175 * Description: Callback function used to find out if a BE exists. | |
1176 * Parameters: | |
1177 * zlp - zpool_handle_t pointer to the current pool being | |
1178 * looked at. | |
1179 * data - BE name to look for. | |
1180 * Return: | |
1181 * 1 - BE exists in this pool. | |
1182 * 0 - BE does not exist in this pool. | |
1183 * Scope: | |
1184 * Semi-private (library wide use only) | |
1185 */ | |
1186 int | |
1187 be_exists_callback(zpool_handle_t *zlp, void *data) | |
1188 { | |
1189 const char *zpool = zpool_get_name(zlp); | |
1190 char *be_name = data; | |
1191 char be_root_ds[MAXPATHLEN]; | |
1192 | |
1193 /* | |
1194 * Generate string for the BE's root dataset | |
1195 */ | |
1196 be_make_root_ds(zpool, be_name, be_root_ds, sizeof (be_root_ds)); | |
1197 | |
1198 /* | |
1199 * Check if dataset exists | |
1200 */ | |
1201 if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) { | |
1202 /* BE's root dataset exists in zpool */ | |
1203 zpool_close(zlp); | |
1204 return (1); | |
1205 } | |
1206 | |
1207 zpool_close(zlp); | |
1208 return (0); | |
1209 } | |
1210 | |
1211 /* | |
1212 * Function: be_set_uuid | |
1213 * Description: This function generates a uuid, unparses it into | |
1214 * string representation, and sets that string into | |
1215 * a zfs user property for a root dataset of a BE. | |
1216 * The name of the user property used to store the | |
1217 * uuid is org.opensolaris.libbe:uuid | |
1218 * | |
1219 * Parameters: | |
1220 * root_ds - Root dataset of the BE to set a uuid on. | |
1221 * Return: | |
1222 * be_errno_t - Failure | |
1223 * BE_SUCCESS - Success | |
1224 * Scope: | |
1225 * Semi-private (library wide ues only) | |
1226 */ | |
1227 int | |
1228 be_set_uuid(char *root_ds) | |
1229 { | |
1230 zfs_handle_t *zhp = NULL; | |
1231 uuid_t uu = { 0 }; | |
1232 char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 }; | |
1233 int ret = BE_SUCCESS; | |
1234 | |
1235 /* Generate a UUID and unparse it into string form */ | |
1236 uuid_generate(uu); | |
1237 if (uuid_is_null(uu) != 0) { | |
1238 be_print_err(gettext("be_set_uuid: failed to " | |
1239 "generate uuid\n")); | |
1240 return (BE_ERR_GEN_UUID); | |
1241 } | |
1242 uuid_unparse(uu, uu_string); | |
1243 | |
1244 /* Get handle to the BE's root dataset. */ | |
1245 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { | |
1246 be_print_err(gettext("be_set_uuid: failed to " | |
1247 "open BE root dataset (%s): %s\n"), root_ds, | |
1248 libzfs_error_description(g_zfs)); | |
1249 return (zfs_err_to_be_err(g_zfs)); | |
1250 } | |
1251 | |
1252 /* Set uuid property for the BE */ | |
1253 if (zfs_prop_set(zhp, BE_UUID_PROPERTY, uu_string) != 0) { | |
1254 be_print_err(gettext("be_set_uuid: failed to " | |
1255 "set uuid property for BE: %s\n"), | |
1256 libzfs_error_description(g_zfs)); | |
1257 ret = zfs_err_to_be_err(g_zfs); | |
1258 } | |
1259 | |
1260 ZFS_CLOSE(zhp); | |
1261 | |
1262 return (ret); | |
1263 } | |
1264 | |
1265 /* | |
1266 * Function: be_get_uuid | |
1267 * Description: This function gets the uuid string from a BE root | |
1268 * dataset, parses it into internal format, and returns | |
1269 * it the caller via a reference pointer passed in. | |
1270 * | |
1271 * Parameters: | |
1272 * rootds - Root dataset of the BE to get the uuid from. | |
1273 * uu - reference pointer to a uuid_t to return uuid in. | |
1274 * Return: | |
1275 * be_errno_t - Failure | |
1276 * BE_SUCCESS - Success | |
1277 * Scope: | |
1278 * Semi-private (library wide use only) | |
1279 */ | |
1280 int | |
1281 be_get_uuid(const char *root_ds, uuid_t *uu) | |
1282 { | |
1283 zfs_handle_t *zhp = NULL; | |
1284 nvlist_t *userprops = NULL; | |
1285 nvlist_t *propname = NULL; | |
1286 char *uu_string = NULL; | |
1287 int ret = BE_SUCCESS; | |
1288 | |
1289 /* Get handle to the BE's root dataset. */ | |
1290 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { | |
1291 be_print_err(gettext("be_get_uuid: failed to " | |
1292 "open BE root dataset (%s): %s\n"), root_ds, | |
1293 libzfs_error_description(g_zfs)); | |
1294 return (zfs_err_to_be_err(g_zfs)); | |
1295 } | |
1296 | |
1297 /* Get user properties for BE's root dataset */ | |
1298 if ((userprops = zfs_get_user_props(zhp)) == NULL) { | |
1299 be_print_err(gettext("be_get_uuid: failed to " | |
1300 "get user properties for BE root dataset (%s): %s\n"), | |
1301 root_ds, libzfs_error_description(g_zfs)); | |
1302 ret = zfs_err_to_be_err(g_zfs); | |
1303 goto done; | |
1304 } | |
1305 | |
1306 /* Get UUID string from BE's root dataset user properties */ | |
1307 if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propname) != 0 || | |
1308 nvlist_lookup_string(propname, ZPROP_VALUE, &uu_string) != 0) { | |
1309 /* | |
1310 * This probably just means that the BE is simply too old | |
1311 * to have a uuid or that we haven't created a uuid for | |
1312 * this BE yet. | |
1313 */ | |
1314 be_print_err(gettext("be_get_uuid: failed to " | |
1315 "get uuid property from BE root dataset user " | |
1316 "properties.\n")); | |
1317 ret = BE_ERR_NO_UUID; | |
1318 goto done; | |
1319 } | |
1320 /* Parse uuid string into internal format */ | |
1321 if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) { | |
1322 be_print_err(gettext("be_get_uuid: failed to " | |
1323 "parse uuid\n")); | |
1324 ret = BE_ERR_PARSE_UUID; | |
1325 goto done; | |
1326 } | |
1327 | |
1328 done: | |
1329 ZFS_CLOSE(zhp); | |
1330 return (ret); | |
1331 } | |
1332 | |
1333 /* ******************************************************************** */ | |
1334 /* Private Functions */ | |
1335 /* ******************************************************************** */ | |
1336 | |
1337 /* | |
1338 * Function: _be_destroy | |
1339 * Description: Destroy a BE and all of its children datasets and snapshots. | |
1340 * This function is called for both global BEs and non-global BEs. | |
1341 * The root dataset of either the global BE or non-global BE to be | |
1342 * destroyed is passed in. | |
1343 * Parameters: | |
1344 * root_ds - pointer to the name of the root dataset of the | |
1345 * BE to destroy. | |
1346 * dd - pointer to a be_destroy_data_t structure. | |
1347 * | |
1348 * Return: | |
1349 * BE_SUCCESS - Success | |
1350 * be_errno_t - Failure | |
1351 * Scope: | |
1352 * Private | |
1353 */ | |
1354 static int | |
1355 _be_destroy(const char *root_ds, be_destroy_data_t *dd) | |
1356 { | |
1357 zfs_handle_t *zhp = NULL; | |
1358 char origin[MAXPATHLEN]; | |
1359 char parent[MAXPATHLEN]; | |
1360 char *snap = NULL; | |
1361 boolean_t has_origin = B_FALSE; | |
1362 int ret = BE_SUCCESS; | |
1363 | |
1364 /* Get handle to BE's root dataset */ | |
1365 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == | |
1366 NULL) { | |
1367 be_print_err(gettext("be_destroy: failed to " | |
1368 "open BE root dataset (%s): %s\n"), root_ds, | |
1369 libzfs_error_description(g_zfs)); | |
1370 return (zfs_err_to_be_err(g_zfs)); | |
1371 } | |
1372 | |
1373 /* | |
1374 * Demote this BE in case it has dependent clones. This call | |
1375 * will end up closing the zfs handle passed in whether it | |
1376 * succeeds or fails. | |
1377 */ | |
1378 if (be_demote_callback(zhp, NULL) != 0) { | |
1379 be_print_err(gettext("be_destroy: " | |
1380 "failed to demote BE %s\n"), root_ds); | |
1381 return (BE_ERR_DEMOTE); | |
1382 } | |
1383 | |
1384 /* Get handle to BE's root dataset */ | |
1385 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == | |
1386 NULL) { | |
1387 be_print_err(gettext("be_destroy: failed to " | |
1388 "open BE root dataset (%s): %s\n"), root_ds, | |
1389 libzfs_error_description(g_zfs)); | |
1390 return (zfs_err_to_be_err(g_zfs)); | |
1391 } | |
1392 | |
1393 /* | |
1394 * Get the origin of this BE's root dataset. This will be used | |
1395 * later to destroy the snapshots originally used to create this BE. | |
1396 */ | |
1397 if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, | |
1398 NULL, 0, B_FALSE) == 0) { | |
1399 (void) strlcpy(parent, origin, sizeof (parent)); | |
1400 if (be_get_snap(parent, &snap) != BE_SUCCESS) { | |
1401 ZFS_CLOSE(zhp); | |
1402 be_print_err(gettext("be_destroy: failed to " | |
1403 "get snapshot name from origin %s\n"), origin); | |
1404 return (BE_ERR_INVAL); | |
1405 } | |
1406 has_origin = B_TRUE; | |
1407 } | |
1408 | |
1409 /* | |
1410 * Destroy the BE's root and its hierarchical children. This call | |
1411 * will end up closing the zfs handle passed in whether it succeeds | |
1412 * or fails. | |
1413 */ | |
1414 if (be_destroy_callback(zhp, dd) != 0) { | |
1415 be_print_err(gettext("be_destroy: failed to " | |
1416 "destroy BE %s\n"), root_ds); | |
1417 return (BE_ERR_DESTROY); | |
1418 } | |
1419 | |
1420 /* If BE has an origin */ | |
1421 if (has_origin) { | |
1422 | |
1423 /* | |
1424 * If origin snapshot doesn't have any other | |
1425 * dependents, delete the origin. | |
1426 */ | |
1427 if ((zhp = zfs_open(g_zfs, origin, ZFS_TYPE_SNAPSHOT)) == | |
1428 NULL) { | |
1429 be_print_err(gettext("be_destroy: failed to " | |
1430 "open BE's origin (%s): %s\n"), origin, | |
1431 libzfs_error_description(g_zfs)); | |
1432 ret = zfs_err_to_be_err(g_zfs); | |
1433 return (ret); | |
1434 } | |
1435 | |
1436 /* If origin has dependents, don't delete it. */ | |
1437 if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) { | |
1438 ZFS_CLOSE(zhp); | |
1439 return (ret); | |
1440 } | |
1441 ZFS_CLOSE(zhp); | |
1442 | |
1443 /* Get handle to BE's parent's root dataset */ | |
1444 if ((zhp = zfs_open(g_zfs, parent, ZFS_TYPE_FILESYSTEM)) == | |
1445 NULL) { | |
1446 be_print_err(gettext("be_destroy: failed to " | |
1447 "open BE's parent root dataset (%s): %s\n"), parent, | |
1448 libzfs_error_description(g_zfs)); | |
1449 ret = zfs_err_to_be_err(g_zfs); | |
1450 return (ret); | |
1451 } | |
1452 | |
1453 /* Destroy the snapshot origin used to create this BE. */ | |
1454 /* | |
1455 * The boolean set to B_FALSE and passed to zfs_destroy_snaps() | |
1456 * tells zfs to process and destroy the snapshots now. | |
1457 * Otherwise the call will potentially return where the | |
1458 * snapshot isn't actually destroyed yet, and ZFS is waiting | |
1459 * until all the references to the snapshot have been | |
1460 * released before actually destroying the snapshot. | |
1461 */ | |
1462 if (zfs_destroy_snaps(zhp, snap, B_FALSE) != 0) { | |
1463 be_print_err(gettext("be_destroy: failed to " | |
1464 "destroy original snapshots used to create " | |
1465 "BE: %s\n"), libzfs_error_description(g_zfs)); | |
1466 | |
1467 /* | |
1468 * If a failure happened because a clone exists, | |
1469 * don't return a failure to the user. Above, we're | |
1470 * only checking that the root dataset's origin | |
1471 * snapshot doesn't have dependent clones, but its | |
1472 * possible that a subordinate dataset origin snapshot | |
1473 * has a clone. We really need to check for that | |
1474 * before trying to destroy the origin snapshot. | |
1475 */ | |
1476 if (libzfs_errno(g_zfs) != EZFS_EXISTS) { | |
1477 ret = zfs_err_to_be_err(g_zfs); | |
1478 ZFS_CLOSE(zhp); | |
1479 return (ret); | |
1480 } | |
1481 } | |
1482 ZFS_CLOSE(zhp); | |
1483 } | |
1484 | |
1485 return (ret); | |
1486 } | |
1487 | |
1488 /* | |
1489 * Function: be_destroy_zones | |
1490 * Description: Find valid zone's and call be_destroy_zone_roots to destroy its | |
1491 * corresponding dataset and all of its children datasets | |
1492 * and snapshots. | |
1493 * Parameters: | |
1494 * be_name - name of global boot environment being destroyed | |
1495 * be_root_ds - root dataset of global boot environment being | |
1496 * destroyed. | |
1497 * dd - be_destroy_data_t pointer | |
1498 * Return: | |
1499 * BE_SUCCESS - Success | |
1500 * be_errno_t - Failure | |
1501 * Scope: | |
1502 * Private | |
1503 * | |
1504 * NOTES - Requires that the BE being deleted has no dependent BEs. If it | |
1505 * does, the destroy will fail. | |
1506 */ | |
1507 static int | |
1508 be_destroy_zones(char *be_name, char *be_root_ds, be_destroy_data_t *dd) | |
1509 { | |
1510 int i; | |
1511 int ret = BE_SUCCESS; | |
1512 int force_umnt = BE_UNMOUNT_FLAG_NULL; | |
1513 char *zonepath = NULL; | |
1514 char *zonename = NULL; | |
1515 char *zonepath_ds = NULL; | |
1516 char *mp = NULL; | |
1517 zoneList_t zlist = NULL; | |
1518 zoneBrandList_t *brands = NULL; | |
1519 zfs_handle_t *zhp = NULL; | |
1520 | |
1521 /* If zones are not implemented, then get out. */ | |
1522 if (!z_zones_are_implemented()) { | |
1523 return (BE_SUCCESS); | |
1524 } | |
1525 | |
1526 /* Get list of supported brands */ | |
1527 if ((brands = be_get_supported_brandlist()) == NULL) { | |
1528 be_print_err(gettext("be_destroy_zones: " | |
1529 "no supported brands\n")); | |
1530 return (BE_SUCCESS); | |
1531 } | |
1532 | |
1533 /* Get handle to BE's root dataset */ | |
1534 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) == | |
1535 NULL) { | |
1536 be_print_err(gettext("be_destroy_zones: failed to " | |
1537 "open BE root dataset (%s): %s\n"), be_root_ds, | |
1538 libzfs_error_description(g_zfs)); | |
1539 z_free_brand_list(brands); | |
1540 return (zfs_err_to_be_err(g_zfs)); | |
1541 } | |
1542 | |
1543 /* | |
1544 * If the global BE is not mounted, we must mount it here to | |
1545 * gather data about the non-global zones in it. | |
1546 */ | |
1547 if (!zfs_is_mounted(zhp, &mp)) { | |
1548 if ((ret = _be_mount(be_name, &mp, | |
1549 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { | |
1550 be_print_err(gettext("be_destroy_zones: failed to " | |
1551 "mount the BE (%s) for zones processing.\n"), | |
1552 be_name); | |
1553 ZFS_CLOSE(zhp); | |
1554 z_free_brand_list(brands); | |
1555 return (ret); | |
1556 } | |
1557 } | |
1558 ZFS_CLOSE(zhp); | |
1559 | |
1560 z_set_zone_root(mp); | |
1561 free(mp); | |
1562 | |
1563 /* Get list of supported zones. */ | |
1564 if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { | |
1565 z_free_brand_list(brands); | |
1566 return (BE_SUCCESS); | |
1567 } | |
1568 | |
1569 /* Unmount the BE before destroying the zones in it. */ | |
1570 if (dd->force_unmount) | |
1571 force_umnt = BE_UNMOUNT_FLAG_FORCE; | |
1572 if ((ret = _be_unmount(be_name, force_umnt)) != BE_SUCCESS) { | |
1573 be_print_err(gettext("be_destroy_zones: failed to " | |
1574 "unmount the BE (%s)\n"), be_name); | |
1575 goto done; | |
1576 } | |
1577 | |
1578 /* Iterate through the zones and destroy them. */ | |
1579 for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) { | |
1580 | |
1581 /* Skip zones that aren't at least installed */ | |
1582 if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED) | |
1583 continue; | |
1584 | |
1585 zonepath = z_zlist_get_zonepath(zlist, i); | |
1586 | |
1587 /* | |
1588 * Get the dataset of this zonepath. If its not | |
1589 * a dataset, skip it. | |
1590 */ | |
1591 if ((zonepath_ds = be_get_ds_from_dir(zonepath)) == NULL) | |
1592 continue; | |
1593 | |
1594 /* | |
1595 * Check if this zone is supported based on the | |
1596 * dataset of its zonepath. | |
1597 */ | |
1598 if (!be_zone_supported(zonepath_ds)) { | |
1599 free(zonepath_ds); | |
1600 continue; | |
1601 } | |
1602 | |
1603 /* Find the zone BE root datasets for this zone. */ | |
1604 if ((ret = be_destroy_zone_roots(zonepath_ds, dd)) | |
1605 != BE_SUCCESS) { | |
1606 be_print_err(gettext("be_destroy_zones: failed to " | |
1607 "find and destroy zone roots for zone %s\n"), | |
1608 zonename); | |
1609 free(zonepath_ds); | |
1610 goto done; | |
1611 } | |
1612 free(zonepath_ds); | |
1613 } | |
1614 | |
1615 done: | |
1616 z_free_brand_list(brands); | |
1617 z_free_zone_list(zlist); | |
1618 | |
1619 return (ret); | |
1620 } | |
1621 | |
1622 /* | |
1623 * Function: be_destroy_zone_roots | |
1624 * Description: This function will open the zone's root container dataset | |
1625 * and iterate the datasets within, looking for roots that | |
1626 * belong to the given global BE and destroying them. | |
1627 * If no other zone roots remain in the zone's root container | |
1628 * dataset, the function will destroy it and the zone's | |
1629 * zonepath dataset as well. | |
1630 * Parameters: | |
1631 * zonepath_ds - pointer to zone's zonepath dataset. | |
1632 * dd - pointer to a linked destroy data. | |
1633 * Returns: | |
1634 * BE_SUCCESS - Success | |
1635 * be_errno_t - Failure | |
1636 * Scope: | |
1637 * Private | |
1638 */ | |
1639 static int | |
1640 be_destroy_zone_roots(char *zonepath_ds, be_destroy_data_t *dd) | |
1641 { | |
1642 zfs_handle_t *zhp; | |
1643 char zone_container_ds[MAXPATHLEN]; | |
1644 int ret = BE_SUCCESS; | |
1645 | |
1646 /* Generate string for the root container dataset for this zone. */ | |
1647 be_make_container_ds(zonepath_ds, zone_container_ds, | |
1648 sizeof (zone_container_ds)); | |
1649 | |
1650 /* Get handle to this zone's root container dataset. */ | |
1651 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) | |
1652 == NULL) { | |
1653 be_print_err(gettext("be_destroy_zone_roots: failed to " | |
1654 "open zone root container dataset (%s): %s\n"), | |
1655 zone_container_ds, libzfs_error_description(g_zfs)); | |
1656 return (zfs_err_to_be_err(g_zfs)); | |
1657 } | |
1658 | |
1659 /* | |
1660 * Iterate through all of this zone's BEs, destroying the ones | |
1661 * that belong to the parent global BE. | |
1662 */ | |
1663 if ((ret = zfs_iter_filesystems(zhp, be_destroy_zone_roots_callback, | |
1664 dd)) != 0) { | |
1665 be_print_err(gettext("be_destroy_zone_roots: failed to " | |
1666 "destroy zone roots under zonepath dataset %s: %s\n"), | |
1667 zonepath_ds, libzfs_error_description(g_zfs)); | |
1668 ZFS_CLOSE(zhp); | |
1669 return (ret); | |
1670 } | |
1671 ZFS_CLOSE(zhp); | |
1672 | |
1673 /* Get handle to this zone's root container dataset. */ | |
1674 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) | |
1675 == NULL) { | |
1676 be_print_err(gettext("be_destroy_zone_roots: failed to " | |
1677 "open zone root container dataset (%s): %s\n"), | |
1678 zone_container_ds, libzfs_error_description(g_zfs)); | |
1679 return (zfs_err_to_be_err(g_zfs)); | |
1680 } | |
1681 | |
1682 /* | |
1683 * If there are no more zone roots in this zone's root container, | |
1684 * dataset, destroy it and the zonepath dataset as well. | |
1685 */ | |
1686 if (zfs_iter_filesystems(zhp, be_zone_root_exists_callback, NULL) | |
1687 == 0) { | |
1688 /* Destroy the zone root container dataset */ | |
1689 if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 || | |
1690 zfs_destroy(zhp, B_FALSE) != 0) { | |
1691 be_print_err(gettext("be_destroy_zone_roots: failed to " | |
1692 "destroy zone root container dataset (%s): %s\n"), | |
1693 zone_container_ds, libzfs_error_description(g_zfs)); | |
1694 goto done; | |
1695 } | |
1696 ZFS_CLOSE(zhp); | |
1697 | |
1698 /* Get handle to zonepath dataset */ | |
1699 if ((zhp = zfs_open(g_zfs, zonepath_ds, ZFS_TYPE_FILESYSTEM)) | |
1700 == NULL) { | |
1701 be_print_err(gettext("be_destroy_zone_roots: failed to " | |
1702 "open zonepath dataset (%s): %s\n"), | |
1703 zonepath_ds, libzfs_error_description(g_zfs)); | |
1704 goto done; | |
1705 } | |
1706 | |
1707 /* Destroy zonepath dataset */ | |
1708 if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 || | |
1709 zfs_destroy(zhp, B_FALSE) != 0) { | |
1710 be_print_err(gettext("be_destroy_zone_roots: " | |
1711 "failed to destroy zonepath dataest %s: %s\n"), | |
1712 zonepath_ds, libzfs_error_description(g_zfs)); | |
1713 goto done; | |
1714 } | |
1715 } | |
1716 | |
1717 done: | |
1718 ZFS_CLOSE(zhp); | |
1719 return (ret); | |
1720 } | |
1721 | |
1722 /* | |
1723 * Function: be_destroy_zone_roots_callback | |
1724 * Description: This function is used as a callback to iterate over all of | |
1725 * a zone's root datasets, finding the one's that | |
1726 * correspond to the current BE. The name's | |
1727 * of the zone root datasets are then destroyed by _be_destroy(). | |
1728 * Parameters: | |
1729 * zhp - zfs_handle_t pointer to current dataset being processed | |
1730 * data - be_destroy_data_t pointer | |
1731 * Returns: | |
1732 * 0 - Success | |
1733 * be_errno_t - Failure | |
1734 * Scope: | |
1735 * Private | |
1736 */ | |
1737 static int | |
1738 be_destroy_zone_roots_callback(zfs_handle_t *zhp, void *data) | |
1739 { | |
1740 be_destroy_data_t *dd = data; | |
1741 uuid_t parent_uuid = { 0 }; | |
1742 int ret = 0; | |
1743 | |
1744 if (be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid) | |
1745 != BE_SUCCESS) { | |
1746 be_print_err(gettext("be_destroy_zone_roots_callback: " | |
1747 "could not get parentuuid for zone root dataset %s\n"), | |
1748 zfs_get_name(zhp)); | |
1749 ZFS_CLOSE(zhp); | |
1750 return (0); | |
1751 } | |
1752 | |
1753 if (uuid_compare(dd->gz_be_uuid, parent_uuid) == 0) { | |
1754 /* | |
1755 * Found a zone root dataset belonging to the parent | |
1756 * BE being destroyed. Destroy this zone BE. | |
1757 */ | |
1758 if ((ret = _be_destroy(zfs_get_name(zhp), dd)) != BE_SUCCESS) { | |
1759 be_print_err(gettext("be_destroy_zone_root_callback: " | |
1760 "failed to destroy zone root %s\n"), | |
1761 zfs_get_name(zhp)); | |
1762 ZFS_CLOSE(zhp); | |
1763 return (ret); | |
1764 } | |
1765 } | |
1766 ZFS_CLOSE(zhp); | |
1767 | |
1768 return (ret); | |
1769 } | |
1770 | |
1771 /* | |
1772 * Function: be_copy_zones | |
1773 * Description: Find valid zones and clone them to create their | |
1774 * corresponding datasets for the BE being created. | |
1775 * Parameters: | |
1776 * obe_name - name of source global BE being copied. | |
1777 * obe_root_ds - root dataset of source global BE being copied. | |
1778 * nbe_root_ds - root dataset of target global BE. | |
1779 * Return: | |
1780 * BE_SUCCESS - Success | |
1781 * be_errno_t - Failure | |
1782 * Scope: | |
1783 * Private | |
1784 */ | |
1785 static int | |
1786 be_copy_zones(char *obe_name, char *obe_root_ds, char *nbe_root_ds) | |
1787 { | |
1788 int i, num_retries; | |
1789 int ret = BE_SUCCESS; | |
1790 int iret = 0; | |
1791 char *zonename = NULL; | |
1792 char *zonepath = NULL; | |
1793 char *zone_be_name = NULL; | |
1794 char *temp_mntpt = NULL; | |
1795 char *new_zone_be_name = NULL; | |
1796 char zoneroot[MAXPATHLEN]; | |
1797 char zoneroot_ds[MAXPATHLEN]; | |
1798 char zone_container_ds[MAXPATHLEN]; | |
1799 char new_zoneroot_ds[MAXPATHLEN]; | |
1800 char ss[MAXPATHLEN]; | |
1801 uuid_t uu = { 0 }; | |
1802 char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 }; | |
1803 be_transaction_data_t bt = { 0 }; | |
1804 zfs_handle_t *obe_zhp = NULL; | |
1805 zfs_handle_t *nbe_zhp = NULL; | |
1806 zfs_handle_t *z_zhp = NULL; | |
1807 zoneList_t zlist = NULL; | |
1808 zoneBrandList_t *brands = NULL; | |
1809 boolean_t mounted_here = B_FALSE; | |
1810 char *snap_name = NULL; | |
1811 | |
1812 /* If zones are not implemented, then get out. */ | |
1813 if (!z_zones_are_implemented()) { | |
1814 return (BE_SUCCESS); | |
1815 } | |
1816 | |
1817 /* Get list of supported brands */ | |
1818 if ((brands = be_get_supported_brandlist()) == NULL) { | |
1819 be_print_err(gettext("be_copy_zones: " | |
1820 "no supported brands\n")); | |
1821 return (BE_SUCCESS); | |
1822 } | |
1823 | |
1824 /* Get handle to origin BE's root dataset */ | |
1825 if ((obe_zhp = zfs_open(g_zfs, obe_root_ds, ZFS_TYPE_FILESYSTEM)) | |
1826 == NULL) { | |
1827 be_print_err(gettext("be_copy_zones: failed to open " | |
1828 "the origin BE root dataset (%s) for zones processing: " | |
1829 "%s\n"), obe_root_ds, libzfs_error_description(g_zfs)); | |
1830 return (zfs_err_to_be_err(g_zfs)); | |
1831 } | |
1832 | |
1833 /* Get handle to newly cloned BE's root dataset */ | |
1834 if ((nbe_zhp = zfs_open(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM)) | |
1835 == NULL) { | |
1836 be_print_err(gettext("be_copy_zones: failed to open " | |
1837 "the new BE root dataset (%s): %s\n"), nbe_root_ds, | |
1838 libzfs_error_description(g_zfs)); | |
1839 ZFS_CLOSE(obe_zhp); | |
1840 return (zfs_err_to_be_err(g_zfs)); | |
1841 } | |
1842 | |
1843 /* Get the uuid of the newly cloned parent BE. */ | |
1844 if (be_get_uuid(zfs_get_name(nbe_zhp), &uu) != BE_SUCCESS) { | |
1845 be_print_err(gettext("be_copy_zones: " | |
1846 "failed to get uuid for BE root " | |
1847 "dataset %s\n"), zfs_get_name(nbe_zhp)); | |
1848 ZFS_CLOSE(nbe_zhp); | |
1849 goto done; | |
1850 } | |
1851 ZFS_CLOSE(nbe_zhp); | |
1852 uuid_unparse(uu, uu_string); | |
1853 | |
1854 /* | |
1855 * If the origin BE is not mounted, we must mount it here to | |
1856 * gather data about the non-global zones in it. | |
1857 */ | |
1858 if (!zfs_is_mounted(obe_zhp, &temp_mntpt)) { | |
1859 if ((ret = _be_mount(obe_name, &temp_mntpt, | |
1860 BE_MOUNT_FLAG_NULL)) != BE_SUCCESS) { | |
1861 be_print_err(gettext("be_copy_zones: failed to " | |
1862 "mount the BE (%s) for zones procesing.\n"), | |
1863 obe_name); | |
1864 goto done; | |
1865 } | |
1866 mounted_here = B_TRUE; | |
1867 } | |
1868 | |
1869 z_set_zone_root(temp_mntpt); | |
1870 | |
1871 /* Get list of supported zones. */ | |
1872 if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { | |
1873 ret = BE_SUCCESS; | |
1874 goto done; | |
1875 } | |
1876 | |
1877 for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) { | |
1878 | |
1879 be_fs_list_data_t fld = { 0 }; | |
1880 char zonepath_ds[MAXPATHLEN]; | |
1881 char *ds = NULL; | |
1882 | |
1883 /* Get zonepath of zone */ | |
1884 zonepath = z_zlist_get_zonepath(zlist, i); | |
1885 | |
1886 /* Skip zones that aren't at least installed */ | |
1887 if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED) | |
1888 continue; | |
1889 | |
1890 /* | |
1891 * Get the dataset of this zonepath. If its not | |
1892 * a dataset, skip it. | |
1893 */ | |
1894 if ((ds = be_get_ds_from_dir(zonepath)) == NULL) | |
1895 continue; | |
1896 | |
1897 (void) strlcpy(zonepath_ds, ds, sizeof (zonepath_ds)); | |
1898 free(ds); | |
1899 ds = NULL; | |
1900 | |
1901 /* Get zoneroot directory */ | |
1902 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot)); | |
1903 | |
1904 /* If zonepath dataset not supported, skip it. */ | |
1905 if (!be_zone_supported(zonepath_ds)) { | |
1906 continue; | |
1907 } | |
1908 | |
1909 if ((ret = be_find_active_zone_root(obe_zhp, zonepath_ds, | |
1910 zoneroot_ds, sizeof (zoneroot_ds))) != BE_SUCCESS) { | |
1911 be_print_err(gettext("be_copy_zones: " | |
1912 "failed to find active zone root for zone %s " | |
1913 "in BE %s\n"), zonename, obe_name); | |
1914 goto done; | |
1915 } | |
1916 | |
1917 be_make_container_ds(zonepath_ds, zone_container_ds, | |
1918 sizeof (zone_container_ds)); | |
1919 | |
1920 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, | |
1921 ZFS_TYPE_FILESYSTEM)) == NULL) { | |
1922 be_print_err(gettext("be_copy_zones: " | |
1923 "failed to open zone root dataset (%s): %s\n"), | |
1924 zoneroot_ds, libzfs_error_description(g_zfs)); | |
1925 ret = zfs_err_to_be_err(g_zfs); | |
1926 goto done; | |
1927 } | |
1928 | |
1929 zone_be_name = | |
1930 be_get_zone_be_name(zoneroot_ds, zone_container_ds); | |
1931 | |
1932 if ((new_zone_be_name = be_auto_zone_be_name(zone_container_ds, | |
1933 zone_be_name)) == NULL) { | |
1934 be_print_err(gettext("be_copy_zones: failed " | |
1935 "to generate auto name for zone BE.\n")); | |
1936 ret = BE_ERR_AUTONAME; | |
1937 goto done; | |
1938 } | |
1939 | |
1940 if ((snap_name = be_auto_snap_name()) == NULL) { | |
1941 be_print_err(gettext("be_copy_zones: failed to " | |
1942 "generate snapshot name for zone BE.\n")); | |
1943 ret = BE_ERR_AUTONAME; | |
1944 goto done; | |
1945 } | |
1946 | |
1947 (void) snprintf(ss, sizeof (ss), "%s@%s", zoneroot_ds, | |
1948 snap_name); | |
1949 | |
1950 if (zfs_snapshot(g_zfs, ss, B_TRUE, NULL) != 0) { | |
1951 be_print_err(gettext("be_copy_zones: " | |
1952 "failed to snapshot zone BE (%s): %s\n"), | |
1953 ss, libzfs_error_description(g_zfs)); | |
1954 if (libzfs_errno(g_zfs) == EZFS_EXISTS) | |
1955 ret = BE_ERR_ZONE_SS_EXISTS; | |
1956 else | |
1957 ret = zfs_err_to_be_err(g_zfs); | |
1958 | |
1959 goto done; | |
1960 } | |
1961 | |
1962 (void) snprintf(new_zoneroot_ds, sizeof (new_zoneroot_ds), | |
1963 "%s/%s", zone_container_ds, new_zone_be_name); | |
1964 | |
1965 bt.obe_name = zone_be_name; | |
1966 bt.obe_root_ds = zoneroot_ds; | |
1967 bt.obe_snap_name = snap_name; | |
1968 bt.obe_altroot = temp_mntpt; | |
1969 bt.nbe_name = new_zone_be_name; | |
1970 bt.nbe_root_ds = new_zoneroot_ds; | |
1971 | |
1972 if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { | |
1973 be_print_err(gettext("be_copy_zones: " | |
1974 "internal error: out of memory\n")); | |
1975 ret = BE_ERR_NOMEM; | |
1976 goto done; | |
1977 } | |
1978 | |
1979 /* | |
1980 * The call to be_clone_fs_callback always closes the | |
1981 * zfs_handle so there's no need to close z_zhp. | |
1982 */ | |
1983 if ((iret = be_clone_fs_callback(z_zhp, &bt)) != 0) { | |
1984 z_zhp = NULL; | |
1985 if (iret != BE_ERR_BE_EXISTS) { | |
1986 be_print_err(gettext("be_copy_zones: " | |
1987 "failed to create zone BE clone for new " | |
1988 "zone BE %s\n"), new_zone_be_name); | |
1989 ret = iret; | |
1990 if (bt.nbe_zfs_props != NULL) | |
1991 nvlist_free(bt.nbe_zfs_props); | |
1992 goto done; | |
1993 } | |
1994 /* | |
1995 * We failed to create the new zone BE because a zone | |
1996 * BE with the auto-name we generated above has since | |
1997 * come into existence. Regenerate a new auto-name | |
1998 * and retry. | |
1999 */ | |
2000 for (num_retries = 1; | |
2001 num_retries < BE_AUTO_NAME_MAX_TRY; | |
2002 num_retries++) { | |
2003 | |
2004 /* Sleep 1 before retrying */ | |
2005 (void) sleep(1); | |
2006 | |
2007 /* Generate new auto zone BE name */ | |
2008 free(new_zone_be_name); | |
2009 if ((new_zone_be_name = be_auto_zone_be_name( | |
2010 zone_container_ds, | |
2011 zone_be_name)) == NULL) { | |
2012 be_print_err(gettext("be_copy_zones: " | |
2013 "failed to generate auto name " | |
2014 "for zone BE.\n")); | |
2015 ret = BE_ERR_AUTONAME; | |
2016 if (bt.nbe_zfs_props != NULL) | |
2017 nvlist_free(bt.nbe_zfs_props); | |
2018 goto done; | |
2019 } | |
2020 | |
2021 (void) snprintf(new_zoneroot_ds, | |
2022 sizeof (new_zoneroot_ds), | |
2023 "%s/%s", zone_container_ds, | |
2024 new_zone_be_name); | |
2025 bt.nbe_name = new_zone_be_name; | |
2026 bt.nbe_root_ds = new_zoneroot_ds; | |
2027 | |
2028 /* | |
2029 * Get handle to original zone BE's root | |
2030 * dataset. | |
2031 */ | |
2032 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, | |
2033 ZFS_TYPE_FILESYSTEM)) == NULL) { | |
2034 be_print_err(gettext("be_copy_zones: " | |
2035 "failed to open zone root " | |
2036 "dataset (%s): %s\n"), | |
2037 zoneroot_ds, | |
2038 libzfs_error_description(g_zfs)); | |
2039 ret = zfs_err_to_be_err(g_zfs); | |
2040 if (bt.nbe_zfs_props != NULL) | |
2041 nvlist_free(bt.nbe_zfs_props); | |
2042 goto done; | |
2043 } | |
2044 | |
2045 /* | |
2046 * Try to clone the zone BE again. This | |
2047 * call will end up closing the zfs | |
2048 * handle passed in whether it | |
2049 * succeeds or fails. | |
2050 */ | |
2051 iret = be_clone_fs_callback(z_zhp, &bt); | |
2052 z_zhp = NULL; | |
2053 if (iret == 0) { | |
2054 break; | |
2055 } else if (iret != BE_ERR_BE_EXISTS) { | |
2056 be_print_err(gettext("be_copy_zones: " | |
2057 "failed to create zone BE clone " | |
2058 "for new zone BE %s\n"), | |
2059 new_zone_be_name); | |
2060 ret = iret; | |
2061 if (bt.nbe_zfs_props != NULL) | |
2062 nvlist_free(bt.nbe_zfs_props); | |
2063 goto done; | |
2064 } | |
2065 } | |
2066 /* | |
2067 * If we've exhausted the maximum number of | |
2068 * tries, free the auto zone BE name and return | |
2069 * error. | |
2070 */ | |
2071 if (num_retries == BE_AUTO_NAME_MAX_TRY) { | |
2072 be_print_err(gettext("be_copy_zones: failed " | |
2073 "to create a unique auto zone BE name\n")); | |
2074 free(bt.nbe_name); | |
2075 bt.nbe_name = NULL; | |
2076 ret = BE_ERR_AUTONAME; | |
2077 if (bt.nbe_zfs_props != NULL) | |
2078 nvlist_free(bt.nbe_zfs_props); | |
2079 goto done; | |
2080 } | |
2081 } | |
2082 | |
2083 if (bt.nbe_zfs_props != NULL) | |
2084 nvlist_free(bt.nbe_zfs_props); | |
2085 | |
2086 z_zhp = NULL; | |
2087 | |
2088 if ((z_zhp = zfs_open(g_zfs, new_zoneroot_ds, | |
2089 ZFS_TYPE_FILESYSTEM)) == NULL) { | |
2090 be_print_err(gettext("be_copy_zones: " | |
2091 "failed to open the new zone BE root dataset " | |
2092 "(%s): %s\n"), new_zoneroot_ds, | |
2093 libzfs_error_description(g_zfs)); | |
2094 ret = zfs_err_to_be_err(g_zfs); | |
2095 goto done; | |
2096 } | |
2097 | |
2098 if (zfs_prop_set(z_zhp, BE_ZONE_PARENTBE_PROPERTY, | |
2099 uu_string) != 0) { | |
2100 be_print_err(gettext("be_copy_zones: " | |
2101 "failed to set parentbe property\n")); | |
2102 ZFS_CLOSE(z_zhp); | |
2103 ret = zfs_err_to_be_err(g_zfs); | |
2104 goto done; | |
2105 } | |
2106 | |
2107 if (zfs_prop_set(z_zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) { | |
2108 be_print_err(gettext("be_copy_zones: " | |
2109 "failed to set active property\n")); | |
2110 ZFS_CLOSE(z_zhp); | |
2111 ret = zfs_err_to_be_err(g_zfs); | |
2112 goto done; | |
2113 } | |
2114 | |
2115 /* | |
2116 * Generate a list of file systems from the original | |
2117 * zone BE that are legacy mounted. We use this list | |
2118 * to determine which entries in the vfstab we need to | |
2119 * update for the new zone BE we've just created. | |
2120 */ | |
2121 if ((ret = be_get_legacy_fs(obe_name, obe_root_ds, | |
2122 zoneroot_ds, zoneroot, &fld)) != BE_SUCCESS) { | |
2123 be_print_err(gettext("be_copy_zones: " | |
2124 "failed to get legacy mounted file system " | |
2125 "list for zone %s\n"), zonename); | |
2126 ZFS_CLOSE(z_zhp); | |
2127 goto done; | |
2128 } | |
2129 | |
2130 /* | |
2131 * Update new zone BE's vfstab. | |
2132 */ | |
2133 if ((ret = be_update_zone_vfstab(z_zhp, bt.nbe_name, | |
2134 zonepath_ds, zonepath_ds, &fld)) != BE_SUCCESS) { | |
2135 be_print_err(gettext("be_copy_zones: " | |
2136 "failed to update new BE's vfstab (%s)\n"), | |
2137 bt.nbe_name); | |
2138 ZFS_CLOSE(z_zhp); | |
2139 be_free_fs_list(&fld); | |
2140 goto done; | |
2141 } | |
2142 | |
2143 be_free_fs_list(&fld); | |
2144 ZFS_CLOSE(z_zhp); | |
2145 } | |
2146 | |
2147 done: | |
2148 free(snap_name); | |
2149 if (brands != NULL) | |
2150 z_free_brand_list(brands); | |
2151 if (zlist != NULL) | |
2152 z_free_zone_list(zlist); | |
2153 | |
2154 if (mounted_here) | |
2155 (void) _be_unmount(obe_name, 0); | |
2156 | |
2157 ZFS_CLOSE(obe_zhp); | |
2158 return (ret); | |
2159 } | |
2160 | |
2161 /* | |
2162 * Function: be_clone_fs_callback | |
2163 * Description: Callback function used to iterate through a BE's filesystems | |
2164 * to clone them for the new BE. | |
2165 * Parameters: | |
2166 * zhp - zfs_handle_t pointer for the filesystem being processed. | |
2167 * data - be_transaction_data_t pointer providing information | |
2168 * about original BE and new BE. | |
2169 * Return: | |
2170 * 0 - Success | |
2171 * be_errno_t - Failure | |
2172 * Scope: | |
2173 * Private | |
2174 */ | |
2175 static int | |
2176 be_clone_fs_callback(zfs_handle_t *zhp, void *data) | |
2177 { | |
2178 be_transaction_data_t *bt = data; | |
2179 zfs_handle_t *zhp_ss = NULL; | |
2180 char prop_buf[MAXPATHLEN]; | |
2181 char zhp_name[ZFS_MAXNAMELEN]; | |
2182 char clone_ds[MAXPATHLEN]; | |
2183 char ss[MAXPATHLEN]; | |
2184 int ret = 0; | |
2185 | |
2186 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf, | |
2187 ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) != 0) { | |
2188 be_print_err(gettext("be_clone_fs_callback: " | |
2189 "failed to get dataset mountpoint (%s): %s\n"), | |
2190 zfs_get_name(zhp), libzfs_error_description(g_zfs)); | |
2191 ret = zfs_err_to_be_err(g_zfs); | |
2192 ZFS_CLOSE(zhp); | |
2193 return (ret); | |
2194 } | |
2195 | |
2196 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) != 0 && | |
2197 strcmp(prop_buf, "legacy") != 0) { | |
2198 /* | |
2199 * Since zfs can't currently handle setting the | |
2200 * mountpoint for a zoned dataset we'll have to skip | |
2201 * this dataset. This is because the mountpoint is not | |
2202 * set to "legacy". | |
2203 */ | |
2204 goto zoned; | |
2205 } | |
2206 /* | |
2207 * Get a copy of the dataset name from the zfs handle | |
2208 */ | |
2209 (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); | |
2210 | |
2211 /* | |
2212 * Get the clone dataset name and prepare the zfs properties for it. | |
2213 */ | |
2214 if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds, | |
2215 sizeof (clone_ds))) != BE_SUCCESS) { | |
2216 ZFS_CLOSE(zhp); | |
2217 return (ret); | |
2218 } | |
2219 | |
2220 /* | |
2221 * Generate the name of the snapshot to use. | |
2222 */ | |
2223 (void) snprintf(ss, sizeof (ss), "%s@%s", zhp_name, | |
2224 bt->obe_snap_name); | |
2225 | |
2226 /* | |
2227 * Get handle to snapshot. | |
2228 */ | |
2229 if ((zhp_ss = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) { | |
2230 be_print_err(gettext("be_clone_fs_callback: " | |
2231 "failed to get handle to snapshot (%s): %s\n"), ss, | |
2232 libzfs_error_description(g_zfs)); | |
2233 ret = zfs_err_to_be_err(g_zfs); | |
2234 ZFS_CLOSE(zhp); | |
2235 return (ret); | |
2236 } | |
2237 | |
2238 /* | |
2239 * Clone the dataset. | |
2240 */ | |
2241 if (zfs_clone(zhp_ss, clone_ds, bt->nbe_zfs_props) != 0) { | |
2242 be_print_err(gettext("be_clone_fs_callback: " | |
2243 "failed to create clone dataset (%s): %s\n"), | |
2244 clone_ds, libzfs_error_description(g_zfs)); | |
2245 | |
2246 ZFS_CLOSE(zhp_ss); | |
2247 ZFS_CLOSE(zhp); | |
2248 | |
2249 return (zfs_err_to_be_err(g_zfs)); | |
2250 } | |
2251 | |
2252 ZFS_CLOSE(zhp_ss); | |
2253 | |
2254 zoned: | |
2255 /* | |
2256 * Iterate through zhp's children datasets (if any) | |
2257 * and clone them accordingly. | |
2258 */ | |
2259 if ((ret = zfs_iter_filesystems(zhp, be_clone_fs_callback, bt)) != 0) { | |
2260 /* | |
2261 * Error occurred while processing a child dataset. | |
2262 * Destroy this dataset and return error. | |
2263 */ | |
2264 zfs_handle_t *d_zhp = NULL; | |
2265 | |
2266 ZFS_CLOSE(zhp); | |
2267 | |
2268 if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM)) | |
2269 == NULL) { | |
2270 return (ret); | |
2271 } | |
2272 | |
2273 (void) zfs_destroy(d_zhp, B_FALSE); | |
2274 ZFS_CLOSE(d_zhp); | |
2275 return (ret); | |
2276 } | |
2277 | |
2278 ZFS_CLOSE(zhp); | |
2279 return (0); | |
2280 } | |
2281 | |
2282 /* | |
2283 * Function: be_send_fs_callback | |
2284 * Description: Callback function used to iterate through a BE's filesystems | |
2285 * to copy them for the new BE. | |
2286 * Parameters: | |
2287 * zhp - zfs_handle_t pointer for the filesystem being processed. | |
2288 * data - be_transaction_data_t pointer providing information | |
2289 * about original BE and new BE. | |
2290 * Return: | |
2291 * 0 - Success | |
2292 * be_errnot_t - Failure | |
2293 * Scope: | |
2294 * Private | |
2295 */ | |
2296 static int | |
2297 be_send_fs_callback(zfs_handle_t *zhp, void *data) | |
2298 { | |
2299 be_transaction_data_t *bt = data; | |
2300 recvflags_t flags = { 0 }; | |
2301 char zhp_name[ZFS_MAXNAMELEN]; | |
2302 char clone_ds[MAXPATHLEN]; | |
2303 sendflags_t send_flags = { 0 }; | |
2304 int pid, status, retval; | |
2305 int srpipe[2]; | |
2306 int ret = 0; | |
2307 | |
2308 /* | |
2309 * Get a copy of the dataset name from the zfs handle | |
2310 */ | |
2311 (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); | |
2312 | |
2313 /* | |
2314 * Get the clone dataset name and prepare the zfs properties for it. | |
2315 */ | |
2316 if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds, | |
2317 sizeof (clone_ds))) != BE_SUCCESS) { | |
2318 ZFS_CLOSE(zhp); | |
2319 return (ret); | |
2320 } | |
2321 | |
2322 /* | |
2323 * Create the new dataset. | |
2324 */ | |
2325 if (zfs_create(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM, bt->nbe_zfs_props) | |
2326 != 0) { | |
2327 be_print_err(gettext("be_send_fs_callback: " | |
2328 "failed to create new dataset '%s': %s\n"), | |
2329 clone_ds, libzfs_error_description(g_zfs)); | |
2330 ret = zfs_err_to_be_err(g_zfs); | |
2331 ZFS_CLOSE(zhp); | |
2332 return (ret); | |
2333 } | |
2334 | |
2335 /* | |
2336 * Destination file system is already created | |
2337 * hence we need to set the force flag on | |
2338 */ | |
2339 flags.force = B_TRUE; | |
2340 | |
2341 /* | |
2342 * Initiate the pipe to be used for the send and recv | |
2343 */ | |
2344 if (pipe(srpipe) != 0) { | |
2345 int err = errno; | |
2346 be_print_err(gettext("be_send_fs_callback: failed to " | |
2347 "open pipe\n")); | |
2348 ZFS_CLOSE(zhp); | |
2349 return (errno_to_be_err(err)); | |
2350 } | |
2351 | |
2352 /* | |
2353 * Fork off a child to send the dataset | |
2354 */ | |
2355 if ((pid = fork()) == -1) { | |
2356 int err = errno; | |
2357 be_print_err(gettext("be_send_fs_callback: failed to fork\n")); | |
2358 (void) close(srpipe[0]); | |
2359 (void) close(srpipe[1]); | |
2360 ZFS_CLOSE(zhp); | |
2361 return (errno_to_be_err(err)); | |
2362 } else if (pid == 0) { /* child process */ | |
2363 (void) close(srpipe[0]); | |
2364 | |
2365 /* Send dataset */ | |
2366 if (zfs_send(zhp, NULL, bt->obe_snap_name, send_flags, | |
2367 srpipe[1], NULL, NULL, NULL) != 0) { | |
2368 _exit(1); | |
2369 } | |
2370 ZFS_CLOSE(zhp); | |
2371 | |
2372 _exit(0); | |
2373 } | |
2374 | |
2375 (void) close(srpipe[1]); | |
2376 | |
2377 /* Receive dataset */ | |
2378 if (zfs_receive(g_zfs, clone_ds, flags, srpipe[0], NULL) != 0) { | |
2379 be_print_err(gettext("be_send_fs_callback: failed to " | |
2380 "recv dataset (%s)\n"), clone_ds); | |
2381 } | |
2382 (void) close(srpipe[0]); | |
2383 | |
2384 /* wait for child to exit */ | |
2385 do { | |
2386 retval = waitpid(pid, &status, 0); | |
2387 if (retval == -1) { | |
2388 status = 0; | |
2389 } | |
2390 } while (retval != pid); | |
2391 | |
2392 if (WEXITSTATUS(status) != 0) { | |
2393 be_print_err(gettext("be_send_fs_callback: failed to " | |
2394 "send dataset (%s)\n"), zhp_name); | |
2395 ZFS_CLOSE(zhp); | |
2396 return (BE_ERR_ZFS); | |
2397 } | |
2398 | |
2399 | |
2400 /* | |
2401 * Iterate through zhp's children datasets (if any) | |
2402 * and send them accordingly. | |
2403 */ | |
2404 if ((ret = zfs_iter_filesystems(zhp, be_send_fs_callback, bt)) != 0) { | |
2405 /* | |
2406 * Error occurred while processing a child dataset. | |
2407 * Destroy this dataset and return error. | |
2408 */ | |
2409 zfs_handle_t *d_zhp = NULL; | |
2410 | |
2411 ZFS_CLOSE(zhp); | |
2412 | |
2413 if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM)) | |
2414 == NULL) { | |
2415 return (ret); | |
2416 } | |
2417 | |
2418 (void) zfs_destroy(d_zhp, B_FALSE); | |
2419 ZFS_CLOSE(d_zhp); | |
2420 return (ret); | |
2421 } | |
2422 | |
2423 ZFS_CLOSE(zhp); | |
2424 return (0); | |
2425 } | |
2426 | |
2427 /* | |
2428 * Function: be_destroy_callback | |
2429 * Description: Callback function used to destroy a BEs children datasets | |
2430 * and snapshots. | |
2431 * Parameters: | |
2432 * zhp - zfs_handle_t pointer to the filesystem being processed. | |
2433 * data - Not used. | |
2434 * Returns: | |
2435 * 0 - Success | |
2436 * be_errno_t - Failure | |
2437 * Scope: | |
2438 * Private | |
2439 */ | |
2440 static int | |
2441 be_destroy_callback(zfs_handle_t *zhp, void *data) | |
2442 { | |
2443 be_destroy_data_t *dd = data; | |
2444 int ret = 0; | |
2445 | |
2446 /* | |
2447 * Iterate down this file system's hierarchical children | |
2448 * and destroy them first. | |
2449 */ | |
2450 if ((ret = zfs_iter_filesystems(zhp, be_destroy_callback, dd)) != 0) { | |
2451 ZFS_CLOSE(zhp); | |
2452 return (ret); | |
2453 } | |
2454 | |
2455 if (dd->destroy_snaps) { | |
2456 /* | |
2457 * Iterate through this file system's snapshots and | |
2458 * destroy them before destroying the file system itself. | |
2459 */ | |
2460 if ((ret = zfs_iter_snapshots(zhp, be_destroy_callback, dd)) | |
2461 != 0) { | |
2462 ZFS_CLOSE(zhp); | |
2463 return (ret); | |
2464 } | |
2465 } | |
2466 | |
2467 /* Attempt to unmount the dataset before destroying it */ | |
2468 if (dd->force_unmount) { | |
2469 if ((ret = zfs_unmount(zhp, NULL, MS_FORCE)) != 0) { | |
2470 be_print_err(gettext("be_destroy_callback: " | |
2471 "failed to unmount %s: %s\n"), zfs_get_name(zhp), | |
2472 libzfs_error_description(g_zfs)); | |
2473 ret = zfs_err_to_be_err(g_zfs); | |
2474 ZFS_CLOSE(zhp); | |
2475 return (ret); | |
2476 } | |
2477 } | |
2478 | |
2479 if (zfs_destroy(zhp, B_FALSE) != 0) { | |
2480 be_print_err(gettext("be_destroy_callback: " | |
2481 "failed to destroy %s: %s\n"), zfs_get_name(zhp), | |
2482 libzfs_error_description(g_zfs)); | |
2483 ret = zfs_err_to_be_err(g_zfs); | |
2484 ZFS_CLOSE(zhp); | |
2485 return (ret); | |
2486 } | |
2487 | |
2488 ZFS_CLOSE(zhp); | |
2489 return (0); | |
2490 } | |
2491 | |
2492 /* | |
2493 * Function: be_demote_callback | |
2494 * Description: This callback function is used to iterate through the file | |
2495 * systems of a BE, looking for the right clone to promote such | |
2496 * that this file system is left without any dependent clones. | |
2497 * If the file system has no dependent clones, it doesn't need | |
2498 * to get demoted, and the function will return success. | |
2499 * | |
2500 * The demotion will be done in two passes. The first pass | |
2501 * will attempt to find the youngest snapshot that has a clone | |
2502 * that is part of some other BE. The second pass will attempt | |
2503 * to find the youngest snapshot that has a clone that is not | |
2504 * part of a BE. Doing this helps ensure the aggregated set of | |
2505 * file systems that compose a BE stay coordinated wrt BE | |
2506 * snapshots and BE dependents. It also prevents a random user | |
2507 * generated clone of a BE dataset to become the parent of other | |
2508 * BE datasets after demoting this dataset. | |
2509 * | |
2510 * Parameters: | |
2511 * zhp - zfs_handle_t pointer to the current file system being | |
2512 * processed. | |
2513 * data - not used. | |
2514 * Return: | |
2515 * 0 - Success | |
2516 * be_errno_t - Failure | |
2517 * Scope: | |
2518 * Private | |
2519 */ | |
2520 static int | |
2521 /* LINTED */ | |
2522 be_demote_callback(zfs_handle_t *zhp, void *data) | |
2523 { | |
2524 be_demote_data_t dd = { 0 }; | |
2525 int i, ret = 0; | |
2526 | |
2527 /* | |
2528 * Initialize be_demote_data for the first pass - this will find a | |
2529 * clone in another BE, if one exists. | |
2530 */ | |
2531 dd.find_in_BE = B_TRUE; | |
2532 | |
2533 for (i = 0; i < 2; i++) { | |
2534 | |
2535 if (zfs_iter_snapshots(zhp, be_demote_find_clone_callback, &dd) | |
2536 != 0) { | |
2537 be_print_err(gettext("be_demote_callback: " | |
2538 "failed to iterate snapshots for %s: %s\n"), | |
2539 zfs_get_name(zhp), libzfs_error_description(g_zfs)); | |
2540 ret = zfs_err_to_be_err(g_zfs); | |
2541 ZFS_CLOSE(zhp); | |
2542 return (ret); | |
2543 } | |
2544 if (dd.clone_zhp != NULL) { | |
2545 /* Found the clone to promote. Promote it. */ | |
2546 if (zfs_promote(dd.clone_zhp) != 0) { | |
2547 be_print_err(gettext("be_demote_callback: " | |
2548 "failed to promote %s: %s\n"), | |
2549 zfs_get_name(dd.clone_zhp), | |
2550 libzfs_error_description(g_zfs)); | |
2551 ret = zfs_err_to_be_err(g_zfs); | |
2552 ZFS_CLOSE(dd.clone_zhp); | |
2553 ZFS_CLOSE(zhp); | |
2554 return (ret); | |
2555 } | |
2556 | |
2557 ZFS_CLOSE(dd.clone_zhp); | |
2558 } | |
2559 | |
2560 /* | |
2561 * Reinitialize be_demote_data for the second pass. | |
2562 * This will find a user created clone outside of any BE | |
2563 * namespace, if one exists. | |
2564 */ | |
2565 dd.clone_zhp = NULL; | |
2566 dd.origin_creation = 0; | |
2567 dd.snapshot = NULL; | |
2568 dd.find_in_BE = B_FALSE; | |
2569 } | |
2570 | |
2571 /* Iterate down this file system's children and demote them */ | |
2572 if ((ret = zfs_iter_filesystems(zhp, be_demote_callback, NULL)) != 0) { | |
2573 ZFS_CLOSE(zhp); | |
2574 return (ret); | |
2575 } | |
2576 | |
2577 ZFS_CLOSE(zhp); | |
2578 return (0); | |
2579 } | |
2580 | |
2581 /* | |
2582 * Function: be_demote_find_clone_callback | |
2583 * Description: This callback function is used to iterate through the | |
2584 * snapshots of a dataset, looking for the youngest snapshot | |
2585 * that has a clone. If found, it returns a reference to the | |
2586 * clone back to the caller in the callback data. | |
2587 * Parameters: | |
2588 * zhp - zfs_handle_t pointer to current snapshot being looked at | |
2589 * data - be_demote_data_t pointer used to store the clone that | |
2590 * is found. | |
2591 * Returns: | |
2592 * 0 - Successfully iterated through all snapshots. | |
2593 * 1 - Failed to iterate through all snapshots. | |
2594 * Scope: | |
2595 * Private | |
2596 */ | |
2597 static int | |
2598 be_demote_find_clone_callback(zfs_handle_t *zhp, void *data) | |
2599 { | |
2600 be_demote_data_t *dd = data; | |
2601 time_t snap_creation; | |
2602 int zret = 0; | |
2603 | |
2604 /* If snapshot has no clones, no need to look at it */ | |
2605 if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) { | |
2606 ZFS_CLOSE(zhp); | |
2607 return (0); | |
2608 } | |
2609 | |
2610 dd->snapshot = zfs_get_name(zhp); | |
2611 | |
2612 /* Get the creation time of this snapshot */ | |
2613 snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); | |
2614 | |
2615 /* | |
2616 * If this snapshot's creation time is greater than (or younger than) | |
2617 * the current youngest snapshot found, iterate this snapshot to | |
2618 * check if it has a clone that we're looking for. | |
2619 */ | |
2620 if (snap_creation >= dd->origin_creation) { | |
2621 /* | |
2622 * Iterate the dependents of this snapshot to find a | |
2623 * a clone that's a direct dependent. | |
2624 */ | |
2625 if ((zret = zfs_iter_dependents(zhp, B_FALSE, | |
2626 be_demote_get_one_clone, dd)) == -1) { | |
2627 be_print_err(gettext("be_demote_find_clone_callback: " | |
2628 "failed to iterate dependents of %s\n"), | |
2629 zfs_get_name(zhp)); | |
2630 ZFS_CLOSE(zhp); | |
2631 return (1); | |
2632 } else if (zret == 1) { | |
2633 /* | |
2634 * Found a clone, update the origin_creation time | |
2635 * in the callback data. | |
2636 */ | |
2637 dd->origin_creation = snap_creation; | |
2638 } | |
2639 } | |
2640 | |
2641 ZFS_CLOSE(zhp); | |
2642 return (0); | |
2643 } | |
2644 | |
2645 /* | |
2646 * Function: be_demote_get_one_clone | |
2647 * Description: This callback function is used to iterate through a | |
2648 * snapshot's dependencies to find a filesystem that is a | |
2649 * direct clone of the snapshot being iterated. | |
2650 * Parameters: | |
2651 * zhp - zfs_handle_t pointer to current dataset being looked at | |
2652 * data - be_demote_data_t pointer used to store the clone | |
2653 * that is found, and also provides flag to note | |
2654 * whether or not the clone filesystem being searched | |
2655 * for needs to be found in a BE dataset hierarchy. | |
2656 * Return: | |
2657 * 1 - Success, found clone and its also a BE's root dataset. | |
2658 * 0 - Failure, clone not found. | |
2659 * Scope: | |
2660 * Private | |
2661 */ | |
2662 static int | |
2663 be_demote_get_one_clone(zfs_handle_t *zhp, void *data) | |
2664 { | |
2665 be_demote_data_t *dd = data; | |
2666 char origin[ZFS_MAXNAMELEN]; | |
2667 char ds_path[ZFS_MAXNAMELEN]; | |
2668 | |
2669 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { | |
2670 ZFS_CLOSE(zhp); | |
2671 return (0); | |
2672 } | |
2673 | |
2674 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path)); | |
2675 | |
2676 /* | |
2677 * Make sure this is a direct clone of the snapshot | |
2678 * we're iterating. | |
2679 */ | |
2680 if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, | |
2681 NULL, 0, B_FALSE) != 0) { | |
2682 be_print_err(gettext("be_demote_get_one_clone: " | |
2683 "failed to get origin of %s: %s\n"), ds_path, | |
2684 libzfs_error_description(g_zfs)); | |
2685 ZFS_CLOSE(zhp); | |
2686 return (0); | |
2687 } | |
2688 if (strcmp(origin, dd->snapshot) != 0) { | |
2689 ZFS_CLOSE(zhp); | |
2690 return (0); | |
2691 } | |
2692 | |
2693 if (dd->find_in_BE) { | |
2694 if ((zpool_iter(g_zfs, be_check_be_roots_callback, ds_path)) | |
2695 > 0) { | |
2696 if (dd->clone_zhp != NULL) | |
2697 ZFS_CLOSE(dd->clone_zhp); | |
2698 dd->clone_zhp = zhp; | |
2699 return (1); | |
2700 } | |
2701 | |
2702 ZFS_CLOSE(zhp); | |
2703 return (0); | |
2704 } | |
2705 | |
2706 if (dd->clone_zhp != NULL) | |
2707 ZFS_CLOSE(dd->clone_zhp); | |
2708 | |
2709 dd->clone_zhp = zhp; | |
2710 return (1); | |
2711 } | |
2712 | |
2713 /* | |
2714 * Function: be_get_snap | |
2715 * Description: This function takes a snapshot dataset name and separates | |
2716 * out the parent dataset portion from the snapshot name. | |
2717 * I.e. it finds the '@' in the snapshot dataset name and | |
2718 * replaces it with a '\0'. | |
2719 * Parameters: | |
2720 * origin - char pointer to a snapshot dataset name. Its | |
2721 * contents will be modified by this function. | |
2722 * *snap - pointer to a char pointer. Will be set to the | |
2723 * snapshot name portion upon success. | |
2724 * Return: | |
2725 * BE_SUCCESS - Success | |
2726 * 1 - Failure | |
2727 * Scope: | |
2728 * Private | |
2729 */ | |
2730 static int | |
2731 be_get_snap(char *origin, char **snap) | |
2732 { | |
2733 char *cp; | |
2734 | |
2735 /* | |
2736 * Separate out the origin's dataset and snapshot portions by | |
2737 * replacing the @ with a '\0' | |
2738 */ | |
2739 cp = strrchr(origin, '@'); | |
2740 if (cp != NULL) { | |
2741 if (cp[1] != NULL && cp[1] != '\0') { | |
2742 cp[0] = '\0'; | |
2743 *snap = cp+1; | |
2744 } else { | |
2745 return (1); | |
2746 } | |
2747 } else { | |
2748 return (1); | |
2749 } | |
2750 | |
2751 return (BE_SUCCESS); | |
2752 } | |
2753 | |
2754 /* | |
2755 * Function: be_create_container_ds | |
2756 * Description: This function checks that the zpool passed has the BE | |
2757 * container dataset, and if not, then creates it. | |
2758 * Parameters: | |
2759 * zpool - name of pool to create BE container dataset in. | |
2760 * Return: | |
2761 * B_TRUE - Successfully created BE container dataset, or it | |
2762 * already existed. | |
2763 * B_FALSE - Failed to create container dataset. | |
2764 * Scope: | |
2765 * Private | |
2766 */ | |
2767 static boolean_t | |
2768 be_create_container_ds(char *zpool) | |
2769 { | |
2770 nvlist_t *props = NULL; | |
2771 char be_container_ds[MAXPATHLEN]; | |
2772 | |
2773 /* Generate string for BE container dataset for this pool */ | |
2774 be_make_container_ds(zpool, be_container_ds, | |
2775 sizeof (be_container_ds)); | |
2776 | |
2777 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) { | |
2778 | |
2779 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { | |
2780 be_print_err(gettext("be_create_container_ds: " | |
2781 "nvlist_alloc failed\n")); | |
2782 return (B_FALSE); | |
2783 } | |
2784 | |
2785 if (nvlist_add_string(props, | |
2786 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), | |
2787 ZFS_MOUNTPOINT_LEGACY) != 0) { | |
2788 be_print_err(gettext("be_create_container_ds: " | |
2789 "internal error: out of memory\n")); | |
2790 nvlist_free(props); | |
2791 return (B_FALSE); | |
2792 } | |
2793 | |
2794 if (nvlist_add_string(props, | |
2795 zfs_prop_to_name(ZFS_PROP_CANMOUNT), "off") != 0) { | |
2796 be_print_err(gettext("be_create_container_ds: " | |
2797 "internal error: out of memory\n")); | |
2798 nvlist_free(props); | |
2799 return (B_FALSE); | |
2800 } | |
2801 | |
2802 if (zfs_create(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM, | |
2803 props) != 0) { | |
2804 be_print_err(gettext("be_create_container_ds: " | |
2805 "failed to create container dataset (%s): %s\n"), | |
2806 be_container_ds, libzfs_error_description(g_zfs)); | |
2807 nvlist_free(props); | |
2808 return (B_FALSE); | |
2809 } | |
2810 | |
2811 nvlist_free(props); | |
2812 } | |
2813 | |
2814 return (B_TRUE); | |
2815 } | |
2816 | |
2817 /* | |
2818 * Function: be_prep_clone_send_fs | |
2819 * Description: This function takes a zfs handle to a dataset from the | |
2820 * original BE, and generates the name of the clone dataset | |
2821 * to create for the new BE. It also prepares the zfs | |
2822 * properties to be used for the new BE. | |
2823 * Parameters: | |
2824 * zhp - pointer to zfs_handle_t of the file system being | |
2825 * cloned/copied. | |
2826 * bt - be_transaction_data pointer providing information | |
2827 * about the original BE and new BE. | |
2828 * clone_ds - buffer to store the name of the dataset | |
2829 * for the new BE. | |
2830 * clone_ds_len - length of clone_ds buffer | |
2831 * Return: | |
2832 * BE_SUCCESS - Success | |
2833 * be_errno_t - Failure | |
2834 * Scope: | |
2835 * Private | |
2836 */ | |
2837 static int | |
2838 be_prep_clone_send_fs(zfs_handle_t *zhp, be_transaction_data_t *bt, | |
2839 char *clone_ds, int clone_ds_len) | |
2840 { | |
2841 zprop_source_t sourcetype; | |
2842 char source[ZFS_MAXNAMELEN]; | |
2843 char zhp_name[ZFS_MAXNAMELEN]; | |
2844 char mountpoint[MAXPATHLEN]; | |
2845 char *child_fs = NULL; | |
2846 char *zhp_mountpoint = NULL; | |
2847 int err = 0; | |
2848 | |
2849 /* | |
2850 * Get a copy of the dataset name zfs_name from zhp | |
2851 */ | |
2852 (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); | |
2853 | |
2854 /* | |
2855 * Get file system name relative to the root. | |
2856 */ | |
2857 if (strncmp(zhp_name, bt->obe_root_ds, strlen(bt->obe_root_ds)) | |
2858 == 0) { | |
2859 child_fs = zhp_name + strlen(bt->obe_root_ds); | |
2860 | |
2861 /* | |
2862 * if child_fs is NULL, this means we're processing the | |
2863 * root dataset itself; set child_fs to the empty string. | |
2864 */ | |
2865 if (child_fs == NULL) | |
2866 child_fs = ""; | |
2867 } else { | |
2868 return (BE_ERR_INVAL); | |
2869 } | |
2870 | |
2871 /* | |
2872 * Generate the name of the clone file system. | |
2873 */ | |
2874 (void) snprintf(clone_ds, clone_ds_len, "%s%s", bt->nbe_root_ds, | |
2875 child_fs); | |
2876 | |
2877 /* Get the mountpoint and source properties of the existing dataset */ | |
2878 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, | |
2879 sizeof (mountpoint), &sourcetype, source, sizeof (source), | |
2880 B_FALSE) != 0) { | |
2881 be_print_err(gettext("be_prep_clone_send_fs: " | |
2882 "failed to get mountpoint for (%s): %s\n"), | |
2883 zhp_name, libzfs_error_description(g_zfs)); | |
2884 return (zfs_err_to_be_err(g_zfs)); | |
2885 } | |
2886 | |
2887 /* | |
2888 * Workaround for 6668667 where a mountpoint property of "/" comes | |
2889 * back as "". | |
2890 */ | |
2891 if (strcmp(mountpoint, "") == 0) { | |
2892 (void) snprintf(mountpoint, sizeof (mountpoint), "/"); | |
2893 } | |
2894 | |
2895 /* | |
2896 * Figure out what to set as the mountpoint for the new dataset. | |
2897 * If the source of the mountpoint property is local, use the | |
2898 * mountpoint value itself. Otherwise, remove it from the | |
2899 * zfs properties list so that it gets inherited. | |
2900 */ | |
2901 if (sourcetype & ZPROP_SRC_LOCAL) { | |
2902 /* | |
2903 * If the BE that this file system is a part of is | |
2904 * currently mounted, strip off the BE altroot portion | |
2905 * from the mountpoint. | |
2906 */ | |
2907 zhp_mountpoint = mountpoint; | |
2908 | |
2909 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && | |
2910 bt->obe_altroot != NULL && strcmp(bt->obe_altroot, | |
2911 "/") != 0 && zfs_is_mounted(zhp, NULL)) { | |
2912 | |
2913 int altroot_len = strlen(bt->obe_altroot); | |
2914 | |
2915 if (strncmp(bt->obe_altroot, mountpoint, altroot_len) | |
2916 == 0) { | |
2917 if (mountpoint[altroot_len] == '/') | |
2918 zhp_mountpoint = mountpoint + | |
2919 altroot_len; | |
2920 else if (mountpoint[altroot_len] == '\0') | |
2921 (void) snprintf(mountpoint, | |
2922 sizeof (mountpoint), "/"); | |
2923 } | |
2924 } | |
2925 | |
2926 if (nvlist_add_string(bt->nbe_zfs_props, | |
2927 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), | |
2928 zhp_mountpoint) != 0) { | |
2929 be_print_err(gettext("be_prep_clone_send_fs: " | |
2930 "internal error: out of memory\n")); | |
2931 return (BE_ERR_NOMEM); | |
2932 } | |
2933 } else { | |
2934 err = nvlist_remove_all(bt->nbe_zfs_props, | |
2935 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT)); | |
2936 if (err != 0 && err != ENOENT) { | |
2937 be_print_err(gettext("be_prep_clone_send_fs: " | |
2938 "failed to remove mountpoint from " | |
2939 "nvlist\n")); | |
2940 return (BE_ERR_INVAL); | |
2941 } | |
2942 } | |
2943 | |
2944 /* | |
2945 * Set the 'canmount' property | |
2946 */ | |
2947 if (nvlist_add_string(bt->nbe_zfs_props, | |
2948 zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) { | |
2949 be_print_err(gettext("be_prep_clone_send_fs: " | |
2950 "internal error: out of memory\n")); | |
2951 return (BE_ERR_NOMEM); | |
2952 } | |
2953 | |
2954 return (BE_SUCCESS); | |
2955 } | |
2956 | |
2957 /* | |
2958 * Function: be_get_zone_be_name | |
2959 * Description: This function takes the zones root dataset, the container | |
2960 * dataset and returns the zones BE name based on the zone | |
2961 * root dataset. | |
2962 * Parameters: | |
2963 * root_ds - the zones root dataset. | |
2964 * container_ds - the container dataset for the zone. | |
2965 * Returns: | |
2966 * char * - the BE name of this zone based on the root dataset. | |
2967 */ | |
2968 static char * | |
2969 be_get_zone_be_name(char *root_ds, char *container_ds) | |
2970 { | |
2971 return (root_ds + (strlen(container_ds) + 1)); | |
2972 } | |
2973 | |
2974 /* | |
2975 * Function: be_zone_root_exists_callback | |
2976 * Description: This callback function is used to determine if a | |
2977 * zone root container dataset has any children. It always | |
2978 * returns 1, signifying a hierarchical child of the zone | |
2979 * root container dataset has been traversed and therefore | |
2980 * it has children. | |
2981 * Parameters: | |
2982 * zhp - zfs_handle_t pointer to current dataset being processed. | |
2983 * data - not used. | |
2984 * Returns: | |
2985 * 1 - dataset exists | |
2986 * Scope: | |
2987 * Private | |
2988 */ | |
2989 static int | |
2990 /* LINTED */ | |
2991 be_zone_root_exists_callback(zfs_handle_t *zhp, void *data) | |
2992 { | |
2993 ZFS_CLOSE(zhp); | |
2994 return (1); | |
2995 } |