int state_scrub(struct snapraid_state* state, int percentage, int olderthan) { block_off_t blockmax; block_off_t countlimit; block_off_t i; time_t timelimit; time_t recentlimit; unsigned count; int ret; struct snapraid_parity parity[LEV_MAX]; struct snapraid_parity* parity_ptr[LEV_MAX]; snapraid_info* infomap; unsigned error; time_t now; unsigned l; /* get the present time */ now = time(0); printf("Initializing...\n"); blockmax = parity_size(state); if (state->opt.force_scrub_even) { /* no limit */ countlimit = blockmax; recentlimit = now; } else if (state->opt.force_scrub) { /* scrub the specified amount of blocks */ countlimit = state->opt.force_scrub; recentlimit = now; } else { /* by default scrub 1/12 of the array */ countlimit = md(blockmax, 1, 12); if (percentage != -1) countlimit = md(blockmax, percentage, 100); /* by default use a 10 day time limit */ recentlimit = now - 10 * 24 * 3600; if (olderthan != -1) recentlimit = now - olderthan * 24 * 3600; } /* identify the time limit */ /* we sort all the block times, and we identify the time limit for which we reach the quota */ /* this allow to process first the oldest blocks */ infomap = malloc_nofail(blockmax * sizeof(snapraid_info)); /* copy the info in the temp vector */ count = 0; for(i=0;i<blockmax;++i) { snapraid_info info = info_get(&state->infoarr, i); /* skip unused blocks */ if (info == 0) continue; infomap[count++] = info; } if (!count) { /* LCOV_EXCL_START */ fprintf(stderr, "The array appears to be empty.\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } /* sort it */ qsort(infomap, count, sizeof(snapraid_info), info_time_compare); /* don't check more block than the available ones */ if (countlimit > count) countlimit = count; if (countlimit > 0) { /* get the time limit */ timelimit = info_get_time(infomap[countlimit - 1]); /* don't scrub too recent blocks */ if (timelimit > recentlimit) { timelimit = recentlimit; } } else { /* if we select a 0 percentage, disable also the time limit */ timelimit = 0; } /* free the temp vector */ free(infomap); /* open the file for reading */ for(l=0;l<state->level;++l) { parity_ptr[l] = &parity[l]; ret = parity_open(parity_ptr[l], state->parity_path[l], state->opt.skip_sequential); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "WARNING! Without an accessible %s file, it isn't possible to scrub.\n", lev_name(l)); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } } printf("Scrubbing...\n"); error = 0; ret = state_scrub_process(state, parity_ptr, 0, blockmax, timelimit, countlimit, now); if (ret == -1) { ++error; /* continue, as we are already exiting */ } for(l=0;l<state->level;++l) { ret = parity_close(parity_ptr[l]); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "DANGER! Unexpected close error in %s disk.\n", lev_name(l)); ++error; /* continue, as we are already exiting */ /* LCOV_EXCL_STOP */ } } /* abort if required */ if (error != 0) return -1; return 0; }
int state_scrub(struct snapraid_state* state, int plan, int olderthan) { block_off_t blockmax; block_off_t countlimit; block_off_t i; block_off_t count; time_t recentlimit; int ret; struct snapraid_parity_handle parity_handle[LEV_MAX]; struct snapraid_plan ps; time_t* timemap; unsigned error; time_t now; unsigned l; /* get the present time */ now = time(0); msg_progress("Initializing...\n"); if ((plan == SCRUB_BAD || plan == SCRUB_NEW || plan == SCRUB_FULL) && olderthan >= 0) { /* LCOV_EXCL_START */ log_fatal("You can specify -o, --older-than only with a numeric percentage.\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } blockmax = parity_allocated_size(state); /* preinitialize to avoid warnings */ countlimit = 0; recentlimit = 0; ps.state = state; if (state->opt.force_scrub_even) { ps.plan = SCRUB_EVEN; } else if (plan == SCRUB_FULL) { ps.plan = SCRUB_FULL; } else if (plan == SCRUB_NEW) { ps.plan = SCRUB_NEW; } else if (plan == SCRUB_BAD) { ps.plan = SCRUB_BAD; } else if (state->opt.force_scrub_at) { /* scrub the specified amount of blocks */ ps.plan = SCRUB_AUTO; countlimit = state->opt.force_scrub_at; recentlimit = now; } else { ps.plan = SCRUB_AUTO; if (plan >= 0) { countlimit = md(blockmax, plan, 100); } else { /* by default scrub 8.33% of the array (100/12=8.(3)) */ countlimit = md(blockmax, 1, 12); } if (olderthan >= 0) { recentlimit = now - olderthan * 24 * 3600; } else { /* by default use a 10 day time limit */ recentlimit = now - 10 * 24 * 3600; } } /* identify the time limit */ /* we sort all the block times, and we identify the time limit for which we reach the quota */ /* this allow to process first the oldest blocks */ timemap = malloc_nofail(blockmax * sizeof(time_t)); /* copy the info in the temp vector */ count = 0; log_tag("block_count:%u\n", blockmax); for (i = 0; i < blockmax; ++i) { snapraid_info info = info_get(&state->infoarr, i); /* skip unused blocks */ if (info == 0) continue; timemap[count++] = info_get_time(info); } if (!count) { /* LCOV_EXCL_START */ log_fatal("The array appears to be empty.\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } /* sort it */ qsort(timemap, count, sizeof(time_t), time_compare); /* output the info map */ i = 0; log_tag("info_count:%u\n", count); while (i < count) { unsigned j = i + 1; while (j < count && timemap[i] == timemap[j]) ++j; log_tag("info_time:%" PRIu64 ":%u\n", (uint64_t)timemap[i], j - i); i = j; } /* compute the limits from count/recentlimit */ if (ps.plan == SCRUB_AUTO) { /* no more than the full count */ if (countlimit > count) countlimit = count; /* decrease until we reach the specific recentlimit */ while (countlimit > 0 && timemap[countlimit - 1] > recentlimit) --countlimit; /* if there is something to scrub */ if (countlimit > 0) { /* get the most recent time we want to scrub */ ps.timelimit = timemap[countlimit - 1]; /* count how many entries for this exact time we have to scrub */ /* if the blocks have all the same time, we end with countlimit == lastlimit */ ps.lastlimit = 1; while (countlimit > ps.lastlimit && timemap[countlimit - ps.lastlimit - 1] == ps.timelimit) ++ps.lastlimit; } else { /* if nothing to scrub, disable also other limits */ ps.timelimit = 0; ps.lastlimit = 0; } log_tag("count_limit:%u\n", countlimit); log_tag("time_limit:%" PRIu64 "\n", (uint64_t)ps.timelimit); log_tag("last_limit:%u\n", ps.lastlimit); } /* free the temp vector */ free(timemap); /* open the file for reading */ for (l = 0; l < state->level; ++l) { ret = parity_open(&parity_handle[l], l, state->parity[l].path, state->file_mode); if (ret == -1) { /* LCOV_EXCL_START */ log_fatal("WARNING! Without an accessible %s file, it isn't possible to scrub.\n", lev_name(l)); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } } msg_progress("Scrubbing...\n"); error = 0; ret = state_scrub_process(state, parity_handle, 0, blockmax, &ps, now); if (ret == -1) { ++error; /* continue, as we are already exiting */ } for (l = 0; l < state->level; ++l) { ret = parity_close(&parity_handle[l]); if (ret == -1) { /* LCOV_EXCL_START */ log_fatal("DANGER! Unexpected close error in %s disk.\n", lev_name(l)); ++error; /* continue, as we are already exiting */ /* LCOV_EXCL_STOP */ } } /* abort if required */ if (error != 0) return -1; return 0; }