Changeset View
Changeset View
Standalone View
Standalone View
lib/libbe/be.c
Show First 20 Lines • Show All 346 Lines • ▼ Show 20 Lines | if (snap_name != NULL) { | ||||
time(&rawtime); | time(&rawtime); | ||||
len = strlen(buf); | len = strlen(buf); | ||||
strftime(buf + len, sizeof(buf) - len, | strftime(buf + len, sizeof(buf) - len, | ||||
"@%F-%T", localtime(&rawtime)); | "@%F-%T", localtime(&rawtime)); | ||||
if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, | if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, | ||||
sizeof(buf)) >= sizeof(buf)) | sizeof(buf)) >= sizeof(buf)) | ||||
return (set_error(lbh, BE_ERR_INVALIDNAME)); | return (set_error(lbh, BE_ERR_INVALIDNAME)); | ||||
} | } | ||||
if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { | if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { | ||||
switch (err) { | switch (err) { | ||||
case EZFS_INVALIDNAME: | case EZFS_INVALIDNAME: | ||||
return (set_error(lbh, BE_ERR_INVALIDNAME)); | return (set_error(lbh, BE_ERR_INVALIDNAME)); | ||||
default: | default: | ||||
/* | /* | ||||
* The other errors that zfs_ioc_snapshot might return | * The other errors that zfs_ioc_snapshot might return | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | be_deep_clone_prop(int prop, void *cb) | ||||
if (prop == ZFS_PROP_MOUNTPOINT) | if (prop == ZFS_PROP_MOUNTPOINT) | ||||
val = be_mountpoint_augmented(dccb->lbh, val); | val = be_mountpoint_augmented(dccb->lbh, val); | ||||
nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); | nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); | ||||
return (ZPROP_CONT); | return (ZPROP_CONT); | ||||
} | } | ||||
/* | |||||
* Return the corresponding boot environment path for a given | |||||
* dataset path, the constructed path is placed in 'result'. | |||||
* | |||||
* example: say our new boot environment name is 'bootenv' and | |||||
* the dataset path is 'zroot/ROOT/default/data/set'. | |||||
* | |||||
* result should produce: 'zroot/ROOT/bootenv/data/set' | |||||
*/ | |||||
static int | static int | ||||
be_deep_clone(zfs_handle_t *ds, void *data) | be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size) | ||||
{ | { | ||||
char *pos; | |||||
char *child_dataset; | |||||
/* match the root path for the boot environments */ | |||||
pos = strstr(dspath, ldc->lbh->root); | |||||
/* no match, different pools? */ | |||||
if (pos == NULL) | |||||
return (BE_ERR_BADPATH); | |||||
kevans: Return value should be parenthesized | |||||
/* root path of the new boot environment */ | |||||
snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename); | |||||
/* gets us to the parent dataset, the +1 consumes a trailing slash */ | |||||
pos += strlen(ldc->lbh->root) + 1; | |||||
/* skip the parent dataset */ | |||||
if ((child_dataset = strchr(pos, '/')) != NULL) | |||||
strlcat(result, child_dataset, result_size); | |||||
return (BE_ERR_SUCCESS); | |||||
Done Inline ActionsReturn value should be parenthesized kevans: Return value should be parenthesized | |||||
} | |||||
static int | |||||
be_clone_cb(zfs_handle_t *ds, void *data) | |||||
{ | |||||
int err; | int err; | ||||
char be_path[BE_MAXPATHLEN]; | char be_path[BE_MAXPATHLEN]; | ||||
char snap_path[BE_MAXPATHLEN]; | char snap_path[BE_MAXPATHLEN]; | ||||
const char *dspath; | const char *dspath; | ||||
char *dsname; | |||||
zfs_handle_t *snap_hdl; | zfs_handle_t *snap_hdl; | ||||
nvlist_t *props; | nvlist_t *props; | ||||
struct libbe_deep_clone *isdc, sdc; | struct libbe_deep_clone *ldc; | ||||
struct libbe_dccb dccb; | struct libbe_dccb dccb; | ||||
isdc = (struct libbe_deep_clone *)data; | ldc = (struct libbe_deep_clone *)data; | ||||
dspath = zfs_get_name(ds); | dspath = zfs_get_name(ds); | ||||
if ((dsname = strrchr(dspath, '/')) == NULL) | |||||
return (BE_ERR_UNKNOWN); | |||||
dsname++; | |||||
if (isdc->bename == NULL) | snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname); | ||||
snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); | |||||
else | |||||
snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); | |||||
snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); | /* construct the boot environment path from the dataset we're cloning */ | ||||
if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS) | |||||
return (set_error(ldc->lbh, BE_ERR_UNKNOWN)); | |||||
if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) | /* the dataset to be created (i.e. the boot environment) already exists */ | ||||
return (set_error(isdc->lbh, BE_ERR_EXISTS)); | if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) | ||||
return (set_error(ldc->lbh, BE_ERR_EXISTS)); | |||||
/* no snapshot found for this dataset, silently skip it */ | |||||
if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) | |||||
return (0); | |||||
if ((snap_hdl = | if ((snap_hdl = | ||||
zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) | zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) | ||||
return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); | return (set_error(ldc->lbh, BE_ERR_ZFSOPEN)); | ||||
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); | nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); | ||||
nvlist_add_string(props, "canmount", "noauto"); | nvlist_add_string(props, "canmount", "noauto"); | ||||
dccb.lbh = isdc->lbh; | dccb.lbh = ldc->lbh; | ||||
dccb.zhp = ds; | dccb.zhp = ds; | ||||
dccb.props = props; | dccb.props = props; | ||||
if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, | if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, | ||||
ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) | ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) | ||||
return (-1); | return (-1); | ||||
if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) | if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) | ||||
err = BE_ERR_ZFSCLONE; | return (set_error(ldc->lbh, BE_ERR_ZFSCLONE)); | ||||
nvlist_free(props); | nvlist_free(props); | ||||
zfs_close(snap_hdl); | zfs_close(snap_hdl); | ||||
/* Failed to clone */ | /* create recursive boot environment */ | ||||
if (err != BE_ERR_SUCCESS) | if (ldc->recursive) | ||||
return (set_error(isdc->lbh, err)); | err = zfs_iter_filesystems(ds, be_clone_cb, ldc); | ||||
sdc.lbh = isdc->lbh; | return (set_error(ldc->lbh, err)); | ||||
sdc.bename = NULL; | |||||
sdc.snapname = isdc->snapname; | |||||
sdc.be_root = (char *)&be_path; | |||||
err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); | |||||
return (err); | |||||
} | } | ||||
/* | /* | ||||
* Create the boot environment from pre-existing snapshot | * Create a boot environment with a given name from a given snapshot. | ||||
* Snapshots can be in the format 'zroot/ROOT/default@snapshot' or | |||||
* 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended | |||||
* with the root path that libbe was initailized with. | |||||
*/ | */ | ||||
int | static int | ||||
be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, | be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, bool recursive) | ||||
const char *snap) | |||||
{ | { | ||||
int err; | int err; | ||||
char be_path[BE_MAXPATHLEN]; | |||||
char snap_path[BE_MAXPATHLEN]; | char snap_path[BE_MAXPATHLEN]; | ||||
const char *bename; | |||||
char *parentname, *snapname; | char *parentname, *snapname; | ||||
zfs_handle_t *parent_hdl; | zfs_handle_t *parent_hdl; | ||||
struct libbe_deep_clone sdc; | struct libbe_deep_clone ldc; | ||||
if ((err = be_validate_name(lbh, name)) != 0) | /* ensure the boot environment name is valid */ | ||||
if ((err = be_validate_name(lbh, bename)) != 0) | |||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
if ((err = be_root_concat(lbh, snap, snap_path)) != 0) | |||||
/* | |||||
* prepend the boot environment root path if we're | |||||
* given a partial snapshot name. | |||||
*/ | |||||
if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0) | |||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
/* ensure the snapshot exists */ | |||||
if ((err = be_validate_snap(lbh, snap_path)) != 0) | if ((err = be_validate_snap(lbh, snap_path)) != 0) | ||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
if ((err = be_root_concat(lbh, name, be_path)) != 0) | /* get a copy of the snapshot path so we can disect it */ | ||||
return (set_error(lbh, err)); | |||||
if ((bename = strrchr(name, '/')) == NULL) | |||||
bename = name; | |||||
else | |||||
bename++; | |||||
if ((parentname = strdup(snap_path)) == NULL) | if ((parentname = strdup(snap_path)) == NULL) | ||||
return (set_error(lbh, BE_ERR_UNKNOWN)); | return (set_error(lbh, BE_ERR_UNKNOWN)); | ||||
/* split dataset name from snapshot name */ | |||||
snapname = strchr(parentname, '@'); | snapname = strchr(parentname, '@'); | ||||
if (snapname == NULL) { | if (snapname == NULL) { | ||||
free(parentname); | free(parentname); | ||||
return (set_error(lbh, BE_ERR_UNKNOWN)); | return (set_error(lbh, BE_ERR_UNKNOWN)); | ||||
} | } | ||||
*snapname = '\0'; | *snapname = '\0'; | ||||
snapname++; | snapname++; | ||||
sdc.lbh = lbh; | /* set-up the boot environment */ | ||||
sdc.bename = bename; | ldc.lbh = lbh; | ||||
sdc.snapname = snapname; | ldc.bename = bename; | ||||
sdc.be_root = lbh->root; | ldc.snapname = snapname; | ||||
ldc.recursive = recursive; | |||||
/* the boot environment will be cloned from this dataset */ | |||||
parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); | parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); | ||||
err = be_deep_clone(parent_hdl, &sdc); | |||||
/* create the boot environment */ | |||||
err = be_clone_cb(parent_hdl, &ldc); | |||||
free(parentname); | free(parentname); | ||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
} | } | ||||
/* | |||||
* Create a non-recursive boot environment from pre-existing snapshot | |||||
*/ | |||||
int | |||||
be_create_shallow(libbe_handle_t *lbh, const char *bename, | |||||
const char *snap) | |||||
{ | |||||
return (be_clone(lbh, bename, snap, false)); | |||||
} | |||||
Done Inline ActionsI would go ahead and kick err out and move be_clone into the return expression for these two functions, since we're not otherwise using the return value and it doesn't introduce an overly long line. kevans: I would go ahead and kick `err` out and move be_clone into the return expression for these two… | |||||
/* | /* | ||||
* Create the boot environment from pre-existing snapshot | |||||
*/ | |||||
int | |||||
be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, | |||||
const char *snap) | |||||
{ | |||||
return (be_clone(lbh, bename, snap, true)); | |||||
} | |||||
/* | |||||
* Create a boot environment from an existing boot environment | * Create a boot environment from an existing boot environment | ||||
*/ | */ | ||||
int | int | ||||
be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) | be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old) | ||||
{ | { | ||||
int err; | int err; | ||||
char buf[BE_MAXPATHLEN]; | char snap[BE_MAXPATHLEN]; | ||||
if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0) | if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0) | ||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
err = be_create_from_existing_snap(lbh, name, (char *)buf); | err = be_clone(lbh, bename, snap, true); | ||||
return (set_error(lbh, err)); | return (set_error(lbh, err)); | ||||
} | } | ||||
/* | /* | ||||
* Verifies that a snapshot has a valid name, exists, and has a mountpoint of | * Verifies that a snapshot has a valid name, exists, and has a mountpoint of | ||||
* '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon | * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon | ||||
▲ Show 20 Lines • Show All 451 Lines • Show Last 20 Lines |
Return value should be parenthesized