be.c (485172f537e195e3d5ca9467192f73c1893ab537) | be.c (8f5c6c31ae7b2fe787fbe11909e33fd151c6d524) |
---|---|
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions --- 19 unchanged lines hidden (view full) --- 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/mount.h> 34#include <sys/stat.h> 35#include <sys/ucred.h> | 1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions --- 19 unchanged lines hidden (view full) --- 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/mount.h> 34#include <sys/stat.h> 35#include <sys/ucred.h> |
36 | 36#include <sys/queue.h> |
37#include <sys/zfs_context.h> 38#include <sys/mntent.h> 39 40#include <ctype.h> 41#include <libgen.h> 42#include <libzfs_core.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <time.h> 46#include <unistd.h> 47 48#include "be.h" 49#include "be_impl.h" 50 | 37#include <sys/zfs_context.h> 38#include <sys/mntent.h> 39 40#include <ctype.h> 41#include <libgen.h> 42#include <libzfs_core.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <time.h> 46#include <unistd.h> 47 48#include "be.h" 49#include "be_impl.h" 50 |
51struct promote_entry { 52 char name[BE_MAXPATHLEN]; 53 SLIST_ENTRY(promote_entry) link; 54}; 55 |
|
51struct be_destroy_data { | 56struct be_destroy_data { |
52 libbe_handle_t *lbh; 53 char *snapname; | 57 libbe_handle_t *lbh; 58 char target_name[BE_MAXPATHLEN]; 59 char *snapname; 60 SLIST_HEAD(, promote_entry) promotelist; |
54}; 55 56#if SOON 57static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 58 const char *child_path); 59static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 60#endif 61 --- 127 unchanged lines hidden (view full) --- 189 */ 190void 191be_nicenum(uint64_t num, char *buf, size_t buflen) 192{ 193 194 zfs_nicenum(num, buf, buflen); 195} 196 | 61}; 62 63#if SOON 64static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 65 const char *child_path); 66static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 67#endif 68 --- 127 unchanged lines hidden (view full) --- 196 */ 197void 198be_nicenum(uint64_t num, char *buf, size_t buflen) 199{ 200 201 zfs_nicenum(num, buf, buflen); 202} 203 |
204static bool 205be_should_promote_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 206{ 207 char *atpos; 208 209 if (zfs_get_type(zfs_hdl) != ZFS_TYPE_SNAPSHOT) 210 return (false); 211 212 /* 213 * If we're deleting a snapshot, we need to make sure we only promote 214 * clones that are derived from one of the snapshots we're deleting, 215 * rather than that of a snapshot we're not touching. This keeps stuff 216 * in a consistent state, making sure that we don't error out unless 217 * we really need to. 218 */ 219 if (bdd->snapname == NULL) 220 return (true); 221 222 atpos = strchr(zfs_get_name(zfs_hdl), '@'); 223 return (strcmp(atpos + 1, bdd->snapname) == 0); 224} 225 226/* 227 * This is executed from be_promote_dependent_clones via zfs_iter_dependents, 228 * It checks if the dependent type is a snapshot then attempts to find any 229 * clones associated with it. Any clones not related to the destroy target are 230 * added to the promote list. 231 */ |
|
197static int | 232static int |
233be_dependent_clone_cb(zfs_handle_t *zfs_hdl, void *data) 234{ 235 int err; 236 bool found; 237 char *name; 238 struct nvlist *nvl; 239 struct nvpair *nvp; 240 struct be_destroy_data *bdd; 241 struct promote_entry *entry, *newentry; 242 243 nvp = NULL; 244 err = 0; 245 bdd = (struct be_destroy_data *)data; 246 247 if (be_should_promote_clones(zfs_hdl, bdd) && 248 (nvl = zfs_get_clones_nvl(zfs_hdl)) != NULL) { 249 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 250 name = nvpair_name(nvp); 251 252 /* 253 * Skip if the clone is equal to, or a child of, the 254 * destroy target. 255 */ 256 if (strncmp(name, bdd->target_name, 257 strlen(bdd->target_name)) == 0 || 258 strstr(name, bdd->target_name) == name) { 259 continue; 260 } 261 262 found = false; 263 SLIST_FOREACH(entry, &bdd->promotelist, link) { 264 if (strcmp(entry->name, name) == 0) { 265 found = true; 266 break; 267 } 268 } 269 270 if (found) 271 continue; 272 273 newentry = malloc(sizeof(struct promote_entry)); 274 if (newentry == NULL) { 275 err = ENOMEM; 276 break; 277 } 278 279#define BE_COPY_NAME(entry, src) \ 280 strlcpy((entry)->name, (src), sizeof((entry)->name)) 281 if (BE_COPY_NAME(newentry, name) >= 282 sizeof(newentry->name)) { 283 /* Shouldn't happen. */ 284 free(newentry); 285 err = ENAMETOOLONG; 286 break; 287 } 288#undef BE_COPY_NAME 289 290 /* 291 * We're building up a SLIST here to make sure both that 292 * we get the order right and so that we don't 293 * inadvertently observe the wrong state by promoting 294 * datasets while we're still walking the tree. The 295 * latter can lead to situations where we promote a BE 296 * then effectively demote it again. 297 */ 298 SLIST_INSERT_HEAD(&bdd->promotelist, newentry, link); 299 } 300 nvlist_free(nvl); 301 } 302 zfs_close(zfs_hdl); 303 return (err); 304} 305 306/* 307 * This is called before a destroy, so that any datasets(environments) that are 308 * dependent on this one get promoted before destroying the target. 309 */ 310static int 311be_promote_dependent_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 312{ 313 int err; 314 zfs_handle_t *clone; 315 struct promote_entry *entry; 316 317 snprintf(bdd->target_name, BE_MAXPATHLEN, "%s/", zfs_get_name(zfs_hdl)); 318 err = zfs_iter_dependents(zfs_hdl, true, be_dependent_clone_cb, bdd); 319 320 /* 321 * Drain the list and walk away from it if we're only deleting a 322 * snapshot. 323 */ 324 if (bdd->snapname != NULL && !SLIST_EMPTY(&bdd->promotelist)) 325 err = BE_ERR_HASCLONES; 326 while (!SLIST_EMPTY(&bdd->promotelist)) { 327 entry = SLIST_FIRST(&bdd->promotelist); 328 SLIST_REMOVE_HEAD(&bdd->promotelist, link); 329 330#define ZFS_GRAB_CLONE() \ 331 zfs_open(bdd->lbh->lzh, entry->name, ZFS_TYPE_FILESYSTEM) 332 /* 333 * Just skip this part on error, we still want to clean up the 334 * promotion list after the first error. We'll then preserve it 335 * all the way back. 336 */ 337 if (err == 0 && (clone = ZFS_GRAB_CLONE()) != NULL) { 338 err = zfs_promote(clone); 339 if (err != 0) 340 err = BE_ERR_DESTROYMNT; 341 zfs_close(clone); 342 } 343#undef ZFS_GRAB_CLONE 344 free(entry); 345 } 346 347 return (err); 348} 349 350static int |
|
198be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 199{ 200 char path[BE_MAXPATHLEN]; 201 struct be_destroy_data *bdd; 202 zfs_handle_t *snap; 203 int err; 204 205 bdd = (struct be_destroy_data *)data; --- 28 unchanged lines hidden (view full) --- 234 235#define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN) 236/* 237 * Destroy the boot environment or snapshot specified by the name 238 * parameter. Options are or'd together with the possible values: 239 * BE_DESTROY_FORCE : forces operation on mounted datasets 240 * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 241 */ | 351be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 352{ 353 char path[BE_MAXPATHLEN]; 354 struct be_destroy_data *bdd; 355 zfs_handle_t *snap; 356 int err; 357 358 bdd = (struct be_destroy_data *)data; --- 28 unchanged lines hidden (view full) --- 387 388#define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN) 389/* 390 * Destroy the boot environment or snapshot specified by the name 391 * parameter. Options are or'd together with the possible values: 392 * BE_DESTROY_FORCE : forces operation on mounted datasets 393 * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 394 */ |
242int 243be_destroy(libbe_handle_t *lbh, const char *name, int options) | 395static int 396be_destroy_internal(libbe_handle_t *lbh, const char *name, int options, 397 bool odestroyer) |
244{ 245 struct be_destroy_data bdd; 246 char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 247 zfs_handle_t *fs; 248 char *snapdelim; 249 int err, force, mounted; 250 size_t rootlen; 251 252 bdd.lbh = lbh; 253 bdd.snapname = NULL; | 398{ 399 struct be_destroy_data bdd; 400 char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 401 zfs_handle_t *fs; 402 char *snapdelim; 403 int err, force, mounted; 404 size_t rootlen; 405 406 bdd.lbh = lbh; 407 bdd.snapname = NULL; |
408 SLIST_INIT(&bdd.promotelist); |
|
254 force = options & BE_DESTROY_FORCE; 255 *origin = '\0'; 256 257 be_root_concat(lbh, name, path); 258 259 if ((snapdelim = strchr(path, '@')) == NULL) { 260 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 261 return (set_error(lbh, BE_ERR_NOENT)); 262 263 if (strcmp(path, lbh->rootfs) == 0 || 264 strcmp(path, lbh->bootfs) == 0) 265 return (set_error(lbh, BE_ERR_DESTROYACT)); 266 267 fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 268 if (fs == NULL) 269 return (set_error(lbh, BE_ERR_ZFSOPEN)); 270 | 409 force = options & BE_DESTROY_FORCE; 410 *origin = '\0'; 411 412 be_root_concat(lbh, name, path); 413 414 if ((snapdelim = strchr(path, '@')) == NULL) { 415 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 416 return (set_error(lbh, BE_ERR_NOENT)); 417 418 if (strcmp(path, lbh->rootfs) == 0 || 419 strcmp(path, lbh->bootfs) == 0) 420 return (set_error(lbh, BE_ERR_DESTROYACT)); 421 422 fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 423 if (fs == NULL) 424 return (set_error(lbh, BE_ERR_ZFSOPEN)); 425 |
271 if ((options & BE_DESTROY_WANTORIGIN) != 0 && 272 zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 273 NULL, NULL, 0, 1) != 0 && 274 (options & BE_DESTROY_ORIGIN) != 0) 275 return (set_error(lbh, BE_ERR_NOORIGIN)); 276 277 /* 278 * If the caller wants auto-origin destruction and the origin 279 * name matches one of our automatically created snapshot names 280 * (i.e. strftime("%F-%T") with a serial at the end), then 281 * we'll set the DESTROY_ORIGIN flag and nuke it 282 * be_is_auto_snapshot_name is exported from libbe(3) so that 283 * the caller can determine if it needs to warn about the origin 284 * not being destroyed or not. 285 */ 286 if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' && 287 be_is_auto_snapshot_name(lbh, origin)) 288 options |= BE_DESTROY_ORIGIN; 289 | |
290 /* Don't destroy a mounted dataset unless force is specified */ 291 if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 292 if (force) { 293 zfs_unmount(fs, NULL, 0); 294 } else { 295 free(bdd.snapname); 296 return (set_error(lbh, BE_ERR_DESTROYMNT)); 297 } 298 } 299 } else { | 426 /* Don't destroy a mounted dataset unless force is specified */ 427 if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 428 if (force) { 429 zfs_unmount(fs, NULL, 0); 430 } else { 431 free(bdd.snapname); 432 return (set_error(lbh, BE_ERR_DESTROYMNT)); 433 } 434 } 435 } else { |
436 /* 437 * If we're initially destroying a snapshot, origin options do 438 * not make sense. If we're destroying the origin snapshot of 439 * a BE, we want to maintain the options in case we need to 440 * fake success after failing to promote. 441 */ 442 if (!odestroyer) 443 options &= ~BE_DESTROY_WANTORIGIN; |
|
300 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 301 return (set_error(lbh, BE_ERR_NOENT)); 302 303 bdd.snapname = strdup(snapdelim + 1); 304 if (bdd.snapname == NULL) 305 return (set_error(lbh, BE_ERR_NOMEM)); 306 *snapdelim = '\0'; 307 fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 308 if (fs == NULL) { 309 free(bdd.snapname); 310 return (set_error(lbh, BE_ERR_ZFSOPEN)); 311 } 312 } 313 | 444 if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 445 return (set_error(lbh, BE_ERR_NOENT)); 446 447 bdd.snapname = strdup(snapdelim + 1); 448 if (bdd.snapname == NULL) 449 return (set_error(lbh, BE_ERR_NOMEM)); 450 *snapdelim = '\0'; 451 fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 452 if (fs == NULL) { 453 free(bdd.snapname); 454 return (set_error(lbh, BE_ERR_ZFSOPEN)); 455 } 456 } 457 |
458 /* 459 * Whether we're destroying a BE or a single snapshot, we need to walk 460 * the tree of what we're going to destroy and promote everything in our 461 * path so that we can make it happen. 462 */ 463 if ((err = be_promote_dependent_clones(fs, &bdd)) != 0) { 464 free(bdd.snapname); 465 466 /* 467 * If we're just destroying the origin of some other dataset 468 * we were invoked to destroy, then we just ignore 469 * BE_ERR_HASCLONES and return success unless the caller wanted 470 * to force the issue. 471 */ 472 if (odestroyer && err == BE_ERR_HASCLONES && 473 (options & BE_DESTROY_AUTOORIGIN) != 0) 474 return (0); 475 return (set_error(lbh, err)); 476 } 477 478 /* 479 * This was deferred until after we promote all of the derivatives so 480 * that we grab the new origin after everything's settled down. 481 */ 482 if ((options & BE_DESTROY_WANTORIGIN) != 0 && 483 zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 484 NULL, NULL, 0, 1) != 0 && 485 (options & BE_DESTROY_ORIGIN) != 0) 486 return (set_error(lbh, BE_ERR_NOORIGIN)); 487 488 /* 489 * If the caller wants auto-origin destruction and the origin 490 * name matches one of our automatically created snapshot names 491 * (i.e. strftime("%F-%T") with a serial at the end), then 492 * we'll set the DESTROY_ORIGIN flag and nuke it 493 * be_is_auto_snapshot_name is exported from libbe(3) so that 494 * the caller can determine if it needs to warn about the origin 495 * not being destroyed or not. 496 */ 497 if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' && 498 be_is_auto_snapshot_name(lbh, origin)) 499 options |= BE_DESTROY_ORIGIN; 500 |
|
314 err = be_destroy_cb(fs, &bdd); 315 zfs_close(fs); 316 free(bdd.snapname); 317 if (err != 0) { 318 /* Children are still present or the mount is referenced */ 319 if (err == EBUSY) 320 return (set_error(lbh, BE_ERR_DESTROYMNT)); 321 return (set_error(lbh, BE_ERR_UNKNOWN)); --- 10 unchanged lines hidden (view full) --- 332 /* 333 * We'll be chopping off the BE root and running this back through 334 * be_destroy, so that we properly handle the origin snapshot whether 335 * it be that of a deep BE or not. 336 */ 337 if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 338 return (0); 339 | 501 err = be_destroy_cb(fs, &bdd); 502 zfs_close(fs); 503 free(bdd.snapname); 504 if (err != 0) { 505 /* Children are still present or the mount is referenced */ 506 if (err == EBUSY) 507 return (set_error(lbh, BE_ERR_DESTROYMNT)); 508 return (set_error(lbh, BE_ERR_UNKNOWN)); --- 10 unchanged lines hidden (view full) --- 519 /* 520 * We'll be chopping off the BE root and running this back through 521 * be_destroy, so that we properly handle the origin snapshot whether 522 * it be that of a deep BE or not. 523 */ 524 if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 525 return (0); 526 |
340 return (be_destroy(lbh, origin + rootlen + 1, 341 options & ~BE_DESTROY_ORIGIN)); | 527 return (be_destroy_internal(lbh, origin + rootlen + 1, 528 options & ~BE_DESTROY_ORIGIN, true)); |
342} 343 | 529} 530 |
531int 532be_destroy(libbe_handle_t *lbh, const char *name, int options) 533{ 534 535 /* 536 * The consumer must not set both BE_DESTROY_AUTOORIGIN and 537 * BE_DESTROY_ORIGIN. Internally, we'll set the latter from the former. 538 * The latter should imply that we must succeed at destroying the 539 * origin, or complain otherwise. 540 */ 541 if ((options & BE_DESTROY_WANTORIGIN) == BE_DESTROY_WANTORIGIN) 542 return (set_error(lbh, BE_ERR_UNKNOWN)); 543 return (be_destroy_internal(lbh, name, options, false)); 544} 545 |
|
344static void 345be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 346{ 347 time_t rawtime; 348 int len, serial; 349 350 time(&rawtime); 351 len = strlen(buf); --- 784 unchanged lines hidden --- | 546static void 547be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 548{ 549 time_t rawtime; 550 int len, serial; 551 552 time(&rawtime); 553 len = strlen(buf); --- 784 unchanged lines hidden --- |