int i, num_complete;
struct snapshot *s, *ref = NULL;
+ DSS_DEBUG_LOG(("picking snapshot with earliest creation time\n"));
num_complete = num_complete_snapshots(sl);
if (num_complete <= OPT_UINT32_VAL(DSS, MIN_COMPLETE))
return NULL;
ref = s;
continue;
}
- DSS_INFO_LOG(("oldest removable snapshot: %s\n", s->name));
return s;
}
assert(ref);
return ref;
}
+/* returns NULL <==> *reason is set to NULL */
+static struct snapshot *find_removable_snapshot(struct snapshot_list *sl,
+ bool try_hard, char **reason)
+{
+ struct snapshot *victim;
+
+ /*
+ * Don't remove anything if there is free space and we have fewer
+ * snapshots than configured, plus one. This way there is always one
+ * snapshot that can be recycled.
+ */
+ if (!try_hard && sl->num_snapshots <=
+ 1 << OPT_UINT32_VAL(DSS, NUM_INTERVALS))
+ goto nope;
+ victim = find_orphaned_snapshot(sl);
+ if (victim) {
+ *reason = make_message("orphaned");
+ return victim;
+ }
+ victim = find_outdated_snapshot(sl);
+ if (victim) {
+ *reason = make_message("outdated");
+ return victim;
+ }
+ if (!OPT_GIVEN(DSS, KEEP_REDUNDANT)) {
+ victim = find_redundant_snapshot(sl);
+ if (victim) {
+ *reason = make_message("redundant");
+ return victim;
+ }
+ }
+ if (!try_hard)
+ goto nope;
+ DSS_WARNING_LOG(("nothing obvious to remove\n"));
+ victim = find_oldest_removable_snapshot(sl);
+ if (victim) {
+ *reason = make_message("oldest");
+ return victim;
+ }
+nope:
+ *reason = NULL;
+ return NULL;
+}
+
static int rename_incomplete_snapshot(int64_t start)
{
char *old_name;
struct snapshot_list sl;
struct snapshot *victim;
struct timeval now;
- const char *why;
+ char *why;
int low_disk_space;
ret = disk_space_low(NULL);
if (tv_diff(&next_removal_check, &now, NULL) > 0)
return 0;
if (!low_disk_space) {
- if (OPT_GIVEN(DSS, KEEP_REDUNDANT))
- return 0;
if (snapshot_creation_status != HS_READY)
return 0;
if (next_snapshot_is_due())
return 0;
}
- /*
- * Idle and --keep_redundant not given, or low disk space. Look at
- * existing snapshots.
- */
+ /* Idle or low disk space, look at existing snapshots. */
dss_get_snapshot_list(&sl);
- ret = 0;
- /*
- * Don't remove anything if there is free space and we have fewer
- * snapshots than configured, plus one. This way there is always one
- * snapshot that can be recycled.
- */
- if (!low_disk_space && sl.num_snapshots <=
- 1 << OPT_UINT32_VAL(DSS, NUM_INTERVALS))
- goto out;
- why = "outdated";
- victim = find_outdated_snapshot(&sl);
- if (victim)
- goto remove;
- why = "redundant";
- victim = find_redundant_snapshot(&sl);
- if (victim)
- goto remove;
- why = "orphaned";
- victim = find_orphaned_snapshot(&sl);
+ victim = find_removable_snapshot(&sl, low_disk_space, &why);
+ if (victim) {
+ pre_remove_hook(victim, why);
+ free(why);
+ }
+ free_snapshot_list(&sl);
if (victim)
- goto remove;
- /* try harder only if disk space is low */
+ return 1;
if (!low_disk_space)
- goto out;
- DSS_WARNING_LOG(("disk space low and nothing obvious to remove\n"));
- why = "oldest";
- victim = find_oldest_removable_snapshot(&sl);
- if (victim)
- goto remove;
+ return 0;
DSS_CRIT_LOG(("uhuhu: disk space low and nothing to remove\n"));
- ret = -ERRNO_TO_DSS_ERROR(ENOSPC);
- goto out;
-remove:
- pre_remove_hook(victim, why);
-out:
- free_snapshot_list(&sl);
- return ret;
+ return -ERRNO_TO_DSS_ERROR(ENOSPC);
}
static void post_create_hook(void)
struct snapshot_list sl;
struct snapshot *victim;
struct disk_space ds;
- const char *why;
+ char *why;
+ bool try_hard;
lock_dss_or_die();
- ret = get_disk_space(".", &ds);
- if (ret < 0)
- return ret;
- log_disk_space(&ds);
+ switch (OPT_UINT32_VAL(PRUNE, DISK_SPACE)) {
+ case FDS_LOW: try_hard = true; break;
+ case FDS_HIGH: try_hard = false; break;
+ default:
+ ret = get_disk_space(".", &ds);
+ if (ret < 0)
+ return ret;
+ log_disk_space(&ds);
+ try_hard = disk_space_low(&ds);
+ }
dss_get_snapshot_list(&sl);
- why = "outdated";
- victim = find_outdated_snapshot(&sl);
- if (victim)
- goto rm;
- why = "redundant";
- victim = find_redundant_snapshot(&sl);
- if (victim)
- goto rm;
- ret = 0;
- goto out;
-rm:
+ victim = find_removable_snapshot(&sl, try_hard, &why);
+ if (!victim) {
+ dss_msg("nothing to prune\n");
+ ret = 0;
+ goto free_sl;
+ }
if (OPT_GIVEN(DSS, DRY_RUN)) {
- dss_msg("%s snapshot %s (interval = %i)\n",
+ dss_msg("picking %s snapshot %s (interval = %i)\n",
why, victim->name, victim->interval);
ret = 0;
- goto out;
+ goto free_why;
}
pre_remove_hook(victim, why);
if (snapshot_removal_status == HS_PRE_RUNNING) {
ret = wait_for_remove_process();
if (ret < 0)
- goto out;
+ goto free_why;
+ ret = -E_HOOK_FAILED;
if (snapshot_removal_status != HS_PRE_SUCCESS)
- goto out;
+ goto free_why;
}
ret = exec_rm();
if (ret < 0)
- goto out;
+ goto free_why;
ret = wait_for_remove_process();
if (ret < 0)
- goto out;
- if (snapshot_removal_status != HS_SUCCESS)
- goto out;
+ goto free_why;
+ assert(snapshot_removal_status == HS_SUCCESS);
post_remove_hook();
- if (snapshot_removal_status != HS_POST_RUNNING)
- goto out;
+ assert(snapshot_removal_status == HS_POST_RUNNING);
ret = wait_for_remove_process();
- if (ret < 0)
- goto out;
- ret = 1;
-out:
+free_why:
+ free(why);
+free_sl:
free_snapshot_list(&sl);
return ret;
}
snapshots.
[/description]
[subcommand prune]
- purpose = remove redundant and outdated snapshots
+ purpose = remove snapshots
[description]
- A snapshot is considered outdated if its interval number is greater or
- equal than the specified number of unit intervals. See --unit-interval
- and --num-intervals above.
-
- A snapshot is said to be redundant if the interval it belongs to
- contains more than the configured number of snapshots.
-
- The prune command gets rid of both outdated and redundant snapshots. At
- most one snapshot is removed per invocation. If --dry-run is given, the
- subcommand only prints the snapshot that would be removed.
+ A snapshot is said to be (a) outdated if its interval number is greater
+ or equal than the specified number of unit intervals, (b) redundant if
+ the interval it belongs to contains more than the configured number of
+ snapshots, and (c) orphaned if it is incomplete and not being created
+ or deleted. All other snapshots are called regular.
+
+ Unless --dry-run is given, which just prints the snapshot that would be
+ removed, this subcommand gets rid of non-regular snapshots. At most
+ one snapshot is removed per invocation. If no such snapshot exists
+ and disk space is low, the subcommand also removes regular snapshots,
+ always picking the oldest one.
+
+ The subcommand fails if there is another dss "run" process.
[/description]
+ [option disk-space]
+ summary = act as if free disk space was high/low
+ arg_info = required_arg
+ arg_type = string
+ typestr = mode
+ values = {
+ FDS_CHECK = "check",
+ FDS_HIGH = "high",
+ FDS_LOW = "low"
+ }
+ default_val = check
+ [help]
+ By default, free disk space is checked and even regular snapshots
+ become candidates for removal if disk space is low. This option
+ overrides the result of the check.
+ [/help]
[subcommand ls]
purpose = print the list of all snapshots
[description]