Files

268 lines
8.3 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 Andrea Mazzoleni
#include "os/portable.h"
#include "elem.h"
#include "parity.h"
#include "state.h"
#include "stream.h"
#include "locate.h"
struct snapraid_parity_entry {
uint32_t block_size;
block_off_t low; /**< Lower position in the parity */
block_off_t high; /**< Higher position in the parity */
block_off_t fragments; /**< Number of fragments in the parity */
struct snapraid_file* file;
struct snapraid_disk* disk;
tommy_node node;
};
struct snapraid_locate_info {
block_off_t block_max; /**< Number of blocks in the parity */
data_off_t parity_size; /**< Size in bytes of the parity */
block_off_t tail_block; /**< Number of blocks of the tail of the parity */
block_off_t low_to_free_tail_block; /**< First block that has to be freed */
};
static int parity_entry_compare(const void* void_a, const void* void_b)
{
const struct snapraid_parity_entry* entry_a = void_a;
const struct snapraid_parity_entry* entry_b = void_b;
if (entry_a->high < entry_b->high)
return -1;
if (entry_a->high > entry_b->high)
return 1;
if (entry_a->low < entry_b->low)
return -1;
if (entry_a->low > entry_b->low)
return 1;
return strcmp(entry_a->file->sub, entry_b->file->sub);
}
static void dump_entry(void* void_entry)
{
struct snapraid_parity_entry* entry = void_entry;
printf("%12" PRIu64 " ", entry->low * (uint64_t)entry->block_size);
printf("%12" PRIu64 " ", (entry->high - entry->low + 1) * (uint64_t)entry->block_size);
printf("%8" PRIu64 " ", entry->fragments);
printf("%s\n", fmt_term(entry->disk, entry->file->sub));
}
static void add_size_to_sum(void* arg, void* void_entry)
{
struct snapraid_parity_entry* entry = void_entry;
data_off_t* size_sum = arg;
*size_sum += entry->file->size;
}
static void collect_parity_block_file(uint32_t block_size, struct snapraid_disk* disk, struct snapraid_file* file, tommy_list* file_list, block_off_t low_to_free_tail_block)
{
block_off_t parity_low = 0; /* lower block */
block_off_t parity_high = 0; /* higher block */
block_off_t parity_fragments = 0; /* number of fragments */
for (block_off_t i = 0; i < file->blockmax; ++i) {
block_off_t parity_pos = fs_file2par_find(disk, file, i);
/* check if a valid position is found */
if (parity_pos == POS_NULL) {
/* block not yet allocated */
continue;
}
if (parity_fragments == 0) {
parity_low = parity_pos;
parity_high = parity_pos;
parity_fragments = 1;
} else {
if (parity_pos != parity_high + 1)
++parity_fragments;
parity_high = parity_pos;
}
}
if (parity_fragments == 0)
return; /* not allocated */
if (low_to_free_tail_block > 0 && parity_high < low_to_free_tail_block)
return; /* entry not in the range to free */
/* found a relevant block so add the corresponding file */
struct snapraid_parity_entry* entry = malloc_nofail(sizeof(struct snapraid_parity_entry));
entry->block_size = block_size;
entry->file = file;
entry->disk = disk;
entry->low = parity_low;
entry->high = parity_high;
entry->fragments = parity_fragments;
tommy_list_insert_tail(file_list, &entry->node, entry);
}
static void state_locate_info(struct snapraid_state* state, uint64_t parity_tail, struct snapraid_locate_info* info)
{
uint32_t block_size = state->block_size;
info->block_max = parity_allocated_size(state);
info->parity_size = info->block_max * (uint64_t)block_size;
info->tail_block = (parity_tail / block_size) + (parity_tail % block_size != 0);
if (info->tail_block > info->block_max)
info->low_to_free_tail_block = 0;
else
info->low_to_free_tail_block = info->block_max - info->tail_block;
}
void state_locate(struct snapraid_state* state, uint64_t parity_tail)
{
char buf[64];
uint32_t block_size = state->block_size;
block_off_t low_to_free_tail_block;
printf("SnapRAID locate report:\n");
printf("\n");
if (parity_tail == 0) {
printf("Locate all files\n\n");
low_to_free_tail_block = 0;
} else {
printf("Locate files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
struct snapraid_locate_info info;
state_locate_info(state, parity_tail, &info);
printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
if (info.tail_block > info.block_max) {
printf("Specified tail greater than the parity size! Operate on the full parity\n");
}
low_to_free_tail_block = info.low_to_free_tail_block;
}
msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
tommy_list files;
tommy_list_init(&files);
for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
struct snapraid_file* file = j->data;
collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
}
}
printf("\n");
if (tommy_list_count(&files) == 0) {
printf("No files located in the specified parity tail.\n");
} else {
tommy_list_sort(&files, parity_entry_compare);
data_off_t total_size_located = 0;
tommy_list_foreach_arg(&files, add_size_to_sum, &total_size_located);
printf("Located data in this range: %sB\n", fmt_size(total_size_located, buf, sizeof(buf)));
printf("\n");
/* |<##################################################################72>|####80>| */
printf(" Offset Span Frags\n");
tommy_list_foreach(&files, dump_entry);
}
tommy_list_foreach(&files, free);
}
void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint64_t parity_tail)
{
char buf[64];
uint32_t block_size = state->block_size;
block_off_t low_to_free_tail_block;
if (parity_tail == 0) {
printf("Forcing reallocation of all files\n\n");
low_to_free_tail_block = 0;
} else {
msg_progress("Forcing reallocation of all files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
struct snapraid_locate_info info;
state_locate_info(state, parity_tail, &info);
printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
if (info.tail_block > info.block_max) {
printf("Specified tail greater than the parity size! Operate on the full parity\n");
}
low_to_free_tail_block = info.low_to_free_tail_block;
}
msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
tommy_list files;
tommy_list_init(&files);
for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
struct snapraid_file* file = j->data;
collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
}
}
printf("\n");
if (tommy_list_count(&files) == 0) {
printf("No files located in the specified parity tail.\n");
} else {
/* process all the files partiall or fully overlapping the free zone */
for (tommy_node* j = tommy_list_head(&files); j != 0; j = j->next) {
struct snapraid_parity_entry* entry = j->data;
struct snapraid_file* file = entry->file;
struct snapraid_disk* disk = entry->disk;
/*
* Reallocate the full file, not only the part of in the free zone
* this is required because the files blocks have to be in order in
* in the parity file.
* not reallocating the file head will prevent the reallocation of the file tail
*/
for (block_off_t f = 0; f < file->blockmax; ++f) {
block_off_t parity_pos = fs_file2par_find(disk, file, f);
if (parity_pos == POS_NULL) {
/* block not yet allocated */
continue;
}
struct snapraid_block* block = fs_file2block_get(file, f);
unsigned block_state = block_state_get(block);
if (block_state == BLOCK_STATE_BLK
|| block_state == BLOCK_STATE_REBUILD
) {
/*
* Explicit force-realloc overrides an in-place REBUILD request.
* REP makes the block eligible for the normal reallocation path.
*
* This transition changes persistent block-state semantics. Mark the
* state dirty explicitly rather than relying on the following scan to
* incidentally do so.
*/
block_state_set(block, BLOCK_STATE_REP);
state->need_write = 1;
}
}
}
}
tommy_list_foreach(&files, free);
}