mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
Change the internal mapping to a tree of chunk
This is intended to save memory as now we can use a fixed block of 17 bytes to store the block information, compared to the previous 28/36 bytes.
This commit is contained in:
parent
fa775d24a8
commit
9bc5f9e5ce
12 changed files with 900 additions and 258 deletions
|
|
@ -59,6 +59,7 @@ noinst_HEADERS = \
|
|||
tommyds/tommyhash.h \
|
||||
tommyds/tommyhashdyn.h \
|
||||
tommyds/tommylist.h \
|
||||
tommyds/tommytree.h \
|
||||
tommyds/tommytypes.h \
|
||||
tommyds/tommyarray.c \
|
||||
tommyds/tommyarrayblk.c \
|
||||
|
|
@ -66,6 +67,7 @@ noinst_HEADERS = \
|
|||
tommyds/tommyhash.c \
|
||||
tommyds/tommyhashdyn.c \
|
||||
tommyds/tommylist.c \
|
||||
tommyds/tommytree.c \
|
||||
cmdline/portable.h \
|
||||
cmdline/snapraid.h \
|
||||
cmdline/util.h \
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ struct snapraid_hash* hash_alloc(struct snapraid_state* state, struct snapraid_d
|
|||
|
||||
/* set the back pointer */
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
struct snapraid_block* block = fs_file2block_get(disk, file, i);
|
||||
struct snapraid_block* block = fs_file2block_get(file, i);
|
||||
|
||||
memcpy(buf + i * HASH_SIZE, block->hash, HASH_SIZE);
|
||||
|
||||
|
|
|
|||
390
cmdline/elem.c
390
cmdline/elem.c
|
|
@ -355,24 +355,6 @@ int filter_content(tommy_list* contentlist, const char* path)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file containing the block.
|
||||
*/
|
||||
static void block_file_set(struct snapraid_block* block, struct snapraid_file* file)
|
||||
{
|
||||
uintptr_t ptr = (uintptr_t)file;
|
||||
|
||||
/* ensure that the pointer doesn't use the flag space */
|
||||
if ((ptr & (uintptr_t)BLOCK_STATE_MASK) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal error for pointer not aligned\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
block->private_file_mixed = (block->private_file_mixed & (uintptr_t)BLOCK_STATE_MASK) | ptr;
|
||||
}
|
||||
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
|
||||
{
|
||||
struct snapraid_file* file;
|
||||
|
|
@ -390,13 +372,6 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_
|
|||
file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block));
|
||||
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
file->blockvec[i].private_parity_pos = POS_INVALID;
|
||||
file->blockvec[i].private_file_mixed = 0;
|
||||
|
||||
/* set the file */
|
||||
block_file_set(&file->blockvec[i], file);
|
||||
|
||||
/* set an invalid hash */
|
||||
block_state_set(&file->blockvec[i], BLOCK_STATE_CHG);
|
||||
hash_invalid_set(file->blockvec[i].hash);
|
||||
}
|
||||
|
|
@ -421,12 +396,8 @@ struct snapraid_file* file_dup(struct snapraid_file* copy)
|
|||
file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block));
|
||||
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
file->blockvec[i].private_file_mixed = copy->blockvec[i].private_file_mixed;
|
||||
file->blockvec[i].private_parity_pos = copy->blockvec[i].private_parity_pos;
|
||||
file->blockvec[i].state = copy->blockvec[i].state;
|
||||
memcpy(file->blockvec[i].hash, copy->blockvec[i].hash, HASH_SIZE);
|
||||
|
||||
/* set the file to overwrite the copied one */
|
||||
block_file_set(&file->blockvec[i], file);
|
||||
}
|
||||
|
||||
return file;
|
||||
|
|
@ -632,6 +603,55 @@ int file_pathstamp_compare(const void* void_a, const void* void_b)
|
|||
return file_stamp_compare(void_a, void_b);
|
||||
}
|
||||
|
||||
struct snapraid_chunk* chunk_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
chunk = malloc_nofail(sizeof(struct snapraid_chunk));
|
||||
chunk->parity_pos = parity_pos;
|
||||
chunk->file = file;
|
||||
chunk->file_pos = file_pos;
|
||||
chunk->count = count;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void chunk_free(struct snapraid_chunk* chunk)
|
||||
{
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
int chunk_parity_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const struct snapraid_chunk* arg_a = void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
if (arg_a->parity_pos < arg_b->parity_pos)
|
||||
return -1;
|
||||
if (arg_a->parity_pos > arg_b->parity_pos)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chunk_file_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const struct snapraid_chunk* arg_a = void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
if (arg_a->file < arg_b->file)
|
||||
return -1;
|
||||
if (arg_a->file > arg_b->file)
|
||||
return 1;
|
||||
|
||||
if (arg_a->file_pos < arg_b->file_pos)
|
||||
return -1;
|
||||
if (arg_a->file_pos > arg_b->file_pos)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snapraid_link* link_alloc(const char* sub, const char* linkto, unsigned link_flag)
|
||||
{
|
||||
struct snapraid_link* link;
|
||||
|
|
@ -724,7 +744,8 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev
|
|||
tommy_hashdyn_init(&disk->linkset);
|
||||
tommy_list_init(&disk->dirlist);
|
||||
tommy_hashdyn_init(&disk->dirset);
|
||||
tommy_arrayblk_init(&disk->blockarr);
|
||||
tommy_tree_init(&disk->fs_parity, chunk_parity_compare);
|
||||
tommy_tree_init(&disk->fs_file, chunk_file_compare);
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
|
@ -733,6 +754,7 @@ void disk_free(struct snapraid_disk* disk)
|
|||
{
|
||||
tommy_list_foreach(&disk->filelist, (tommy_foreach_func*)file_free);
|
||||
tommy_list_foreach(&disk->deletedlist, (tommy_foreach_func*)file_free);
|
||||
tommy_tree_foreach(&disk->fs_file, (tommy_foreach_func*)chunk_free);
|
||||
tommy_hashdyn_done(&disk->inodeset);
|
||||
tommy_hashdyn_done(&disk->pathset);
|
||||
tommy_hashdyn_done(&disk->stampset);
|
||||
|
|
@ -740,87 +762,32 @@ void disk_free(struct snapraid_disk* disk)
|
|||
tommy_hashdyn_done(&disk->linkset);
|
||||
tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free);
|
||||
tommy_hashdyn_done(&disk->dirset);
|
||||
tommy_arrayblk_done(&disk->blockarr);
|
||||
free(disk);
|
||||
}
|
||||
|
||||
block_off_t disk_block_size(struct snapraid_disk* disk)
|
||||
struct chunk_disk_empty {
|
||||
block_off_t blockmax;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for any block inside the specified blockmax.
|
||||
*/
|
||||
static int chunk_disk_empty_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
return tommy_arrayblk_size(&disk->blockarr);
|
||||
}
|
||||
const struct chunk_disk_empty* arg_a = void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
void fs_par2file_set(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
(void)disk;
|
||||
|
||||
assert(file_pos < file->blockmax);
|
||||
|
||||
file->blockvec[file_pos].private_parity_pos = parity_pos;
|
||||
}
|
||||
|
||||
struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
|
||||
{
|
||||
struct snapraid_block* block = fs_par2block_get(disk, parity_pos);
|
||||
struct snapraid_file* file = (struct snapraid_file*)(block->private_file_mixed & ~(uintptr_t)BLOCK_STATE_MASK);
|
||||
|
||||
if (block == BLOCK_EMPTY)
|
||||
/* if the block is inside the specified blockmax, it's found */
|
||||
if (arg_a->blockmax > arg_b->parity_pos)
|
||||
return 0;
|
||||
|
||||
if (file_pos) {
|
||||
if (block < file->blockvec || block >= file->blockvec + file->blockmax) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency in block %u ownership\n", block->private_parity_pos);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
*file_pos = block - file->blockvec;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
(void)disk;
|
||||
|
||||
assert(file_pos < file->blockmax);
|
||||
|
||||
return file->blockvec[file_pos].private_parity_pos;
|
||||
}
|
||||
|
||||
struct snapraid_block* fs_file2block_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
(void)disk;
|
||||
|
||||
assert(file_pos < file->blockmax);
|
||||
|
||||
return &file->blockvec[file_pos];
|
||||
}
|
||||
|
||||
void fs_par2block_clear(struct snapraid_disk* disk, block_off_t pos)
|
||||
{
|
||||
tommy_arrayblk_grow(&disk->blockarr, pos + 1);
|
||||
tommy_arrayblk_set(&disk->blockarr, pos, BLOCK_EMPTY);
|
||||
}
|
||||
|
||||
void fs_par2block_set(struct snapraid_disk* disk, block_off_t pos, struct snapraid_block* block)
|
||||
{
|
||||
tommy_arrayblk_grow(&disk->blockarr, pos + 1);
|
||||
tommy_arrayblk_set(&disk->blockarr, pos, block);
|
||||
}
|
||||
|
||||
struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
{
|
||||
if (parity_pos < tommy_arrayblk_size(&disk->blockarr))
|
||||
return tommy_arrayblk_get(&disk->blockarr, parity_pos);
|
||||
else
|
||||
return BLOCK_EMPTY;
|
||||
/* otherwise search for a smaller one */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int disk_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
|
||||
{
|
||||
block_off_t i;
|
||||
struct chunk_disk_empty arg = { blockmax };
|
||||
|
||||
/* if there is an element, it's not empty */
|
||||
/* even if links and dirs have no block allocation */
|
||||
|
|
@ -831,35 +798,208 @@ int disk_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
|
|||
if (!tommy_list_empty(&disk->dirlist))
|
||||
return 0;
|
||||
|
||||
/* limit the search to the size of the disk */
|
||||
if (blockmax > tommy_arrayblk_size(&disk->blockarr))
|
||||
blockmax = tommy_arrayblk_size(&disk->blockarr);
|
||||
|
||||
/* checks all the blocks to search for deleted ones */
|
||||
/* this search is slow, but it's done only if no file is present */
|
||||
for (i = 0; i < blockmax; ++i) {
|
||||
struct snapraid_block* block = tommy_arrayblk_get(&disk->blockarr, i);
|
||||
unsigned block_state = block_state_get(block);
|
||||
|
||||
switch (block_state) {
|
||||
case BLOCK_STATE_EMPTY :
|
||||
/* empty block are expected for an empty disk */
|
||||
break;
|
||||
case BLOCK_STATE_DELETED :
|
||||
/* if there is a deleted block, the disk is not empty */
|
||||
return 0;
|
||||
default :
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency for used block in disk '%s' without files\n", disk->name);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
/* search for any chunk inside blockmax */
|
||||
if (tommy_tree_search_compare(&disk->fs_parity, chunk_disk_empty_compare, &arg) != 0)
|
||||
return 0;
|
||||
|
||||
/* finally, it's empty */
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct chunk_disk_size {
|
||||
block_off_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for the chunk with the highest parity position.
|
||||
*
|
||||
* The maximum parity position is stored as size.
|
||||
*/
|
||||
static int chunk_disk_size_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
struct chunk_disk_size* arg_a = (void*)void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
/* get the maximum size */
|
||||
if (arg_a->size < arg_b->parity_pos + arg_b->count)
|
||||
arg_a->size = arg_b->parity_pos + arg_b->count;
|
||||
|
||||
/* search always for a bigger one */
|
||||
return 1;
|
||||
}
|
||||
|
||||
block_off_t disk_size(struct snapraid_disk* disk)
|
||||
{
|
||||
struct chunk_disk_size arg = { 0 };
|
||||
tommy_tree_search_compare(&disk->fs_parity, chunk_disk_size_compare, &arg);
|
||||
return arg.size;
|
||||
}
|
||||
|
||||
struct chunk_parity_inside {
|
||||
block_off_t parity_pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for the chunk containing the specified parity position.
|
||||
*/
|
||||
static int chunk_parity_inside_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const struct chunk_parity_inside* arg_a = void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
if (arg_a->parity_pos < arg_b->parity_pos)
|
||||
return -1;
|
||||
if (arg_a->parity_pos >= arg_b->parity_pos + arg_b->count)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
|
||||
{
|
||||
struct chunk_parity_inside arg = { parity_pos };
|
||||
struct snapraid_chunk* chunk = tommy_tree_search_compare(&disk->fs_parity, chunk_parity_inside_compare, &arg);
|
||||
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
if (file_pos)
|
||||
*file_pos = chunk->file_pos + (parity_pos - chunk->parity_pos);
|
||||
|
||||
return chunk->file;
|
||||
}
|
||||
|
||||
struct chunk_file_inside {
|
||||
struct snapraid_file* file;
|
||||
block_off_t file_pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search for the chunk containing the specified file position.
|
||||
*/
|
||||
static int chunk_file_inside_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const struct chunk_file_inside* arg_a = void_a;
|
||||
const struct snapraid_chunk* arg_b = void_b;
|
||||
|
||||
if (arg_a->file < arg_b->file)
|
||||
return -1;
|
||||
if (arg_a->file > arg_b->file)
|
||||
return 1;
|
||||
|
||||
if (arg_a->file_pos < arg_b->file_pos)
|
||||
return -1;
|
||||
if (arg_a->file_pos >= arg_b->file_pos + arg_b->count)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
struct chunk_file_inside arg = { file, file_pos };
|
||||
struct snapraid_chunk* chunk = tommy_tree_search_compare(&disk->fs_file, chunk_file_inside_compare, &arg);
|
||||
|
||||
if (!chunk) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency for a file without parity in disk '%s'\n", disk->name);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return chunk->parity_pos + (file_pos - chunk->file_pos);
|
||||
}
|
||||
|
||||
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
if (file_pos > 0) {
|
||||
/* search an existing chunk for the previous file_pos */
|
||||
struct chunk_file_inside arg = { file, file_pos - 1 };
|
||||
|
||||
chunk = tommy_tree_search_compare(&disk->fs_file, chunk_file_inside_compare, &arg);
|
||||
|
||||
if (chunk != 0 && parity_pos == chunk->parity_pos + chunk->count) {
|
||||
/* ensure that we are really extending a chunk */
|
||||
if (file_pos != chunk->file_pos + chunk->count) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency extending a chunk in disk '%s'\n", disk->name);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* extend the existing chunk */
|
||||
++chunk->count;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* a chunk doesn't exist, and we have to create a new one */
|
||||
chunk = chunk_alloc(parity_pos, file, file_pos, 1);
|
||||
|
||||
/* insert te chunk in the trees */
|
||||
tommy_tree_insert(&disk->fs_parity, &chunk->parity_node, chunk);
|
||||
tommy_tree_insert(&disk->fs_file, &chunk->file_node, chunk);
|
||||
}
|
||||
|
||||
void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
{
|
||||
struct chunk_parity_inside arg = { parity_pos };
|
||||
struct snapraid_chunk* chunk = tommy_tree_search_compare(&disk->fs_parity, chunk_parity_inside_compare, &arg);
|
||||
|
||||
if (!chunk) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency for clearing a not existing block in disk '%s'\n", disk->name);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* if it's the only block of the chunk, delete it */
|
||||
if (chunk->count == 1) {
|
||||
/* remove from the trees */
|
||||
tommy_tree_remove(&disk->fs_parity, chunk);
|
||||
tommy_tree_remove(&disk->fs_file, chunk);
|
||||
|
||||
/* deallocate */
|
||||
chunk_free(chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if it's at the start of the chunk, shrink the chunk */
|
||||
if (parity_pos == chunk->parity_pos) {
|
||||
++chunk->parity_pos;
|
||||
++chunk->file_pos;
|
||||
--chunk->count;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if it's at the end of the chunk, shrink the chunk */
|
||||
if (parity_pos == chunk->parity_pos + chunk->count - 1) {
|
||||
--chunk->count;
|
||||
return;
|
||||
}
|
||||
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency for clearing in the middle of a chunk in disk '%s'\n", disk->name);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
{
|
||||
struct snapraid_file* file;
|
||||
block_off_t file_pos;
|
||||
|
||||
file = fs_par2file_get(disk, parity_pos, &file_pos);
|
||||
|
||||
if (!file)
|
||||
return BLOCK_EMPTY;
|
||||
|
||||
return fs_file2block_get(file, file_pos);
|
||||
}
|
||||
|
||||
struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid)
|
||||
{
|
||||
struct snapraid_map* map;
|
||||
|
|
|
|||
131
cmdline/elem.h
131
cmdline/elem.h
|
|
@ -20,8 +20,9 @@
|
|||
|
||||
#include "util.h"
|
||||
#include "support.h"
|
||||
#include "tommyds/tommylist.h"
|
||||
#include "tommyds/tommyhash.h"
|
||||
#include "tommyds/tommylist.h"
|
||||
#include "tommyds/tommytree.h"
|
||||
#include "tommyds/tommyhashdyn.h"
|
||||
#include "tommyds/tommyarray.h"
|
||||
#include "tommyds/tommyarrayblk.h"
|
||||
|
|
@ -152,19 +153,12 @@ struct snapraid_filter {
|
|||
*/
|
||||
#define BLOCK_STATE_DELETED 4
|
||||
|
||||
/**
|
||||
* Mask used to store the previous states of the block.
|
||||
* Used to mix them inside the file pointer.
|
||||
*/
|
||||
#define BLOCK_STATE_MASK 7
|
||||
|
||||
/**
|
||||
* Block of a file.
|
||||
*/
|
||||
struct snapraid_block {
|
||||
uintptr_t private_file_mixed; /**< Back pointer to the file owning this block, mixed with some flags. */
|
||||
block_off_t private_parity_pos; /**< Position of the block in the parity. */
|
||||
unsigned char hash[HASH_SIZE]; /**< Hash of the block. */
|
||||
unsigned char state; /**< State of the block. */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -287,6 +281,20 @@ struct snapraid_dir {
|
|||
tommy_hashdyn_node nodeset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Chunk.
|
||||
*
|
||||
* A chunk represents a fragment of a file mapped into the parity.
|
||||
*/
|
||||
struct snapraid_chunk {
|
||||
struct snapraid_file* file; /* File containing this chunk. */
|
||||
block_off_t parity_pos; /**< Parity position. */
|
||||
block_off_t file_pos; /**< Position in the file. */
|
||||
block_off_t count; /**< Number of sequential blocks in the file and parity. */
|
||||
tommy_tree_node parity_node; /**< Tree sorted by <parity_pos>. */
|
||||
tommy_tree_node file_node; /**< Tree sorter by <file,file_pos>. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Disk.
|
||||
*/
|
||||
|
|
@ -315,12 +323,12 @@ struct snapraid_disk {
|
|||
int had_empty_uuid; /**< If the disk had an empty UUID, meaning that it's a new disk. */
|
||||
int mapping_idx; /**< Index in the mapping vector. Used only as buffer when writing the content file. */
|
||||
|
||||
/**<
|
||||
* Block array of the disk.
|
||||
*
|
||||
* Each element points to a snapraid_block structure, or it's BLOCK_EMPTY.
|
||||
/**
|
||||
* Mapping of chunks in the parity.
|
||||
* Sorted by <parity_pos> and by <file,file_pos>
|
||||
*/
|
||||
tommy_arrayblk blockarr;
|
||||
tommy_tree fs_parity;
|
||||
tommy_tree fs_file;
|
||||
|
||||
/**
|
||||
* List of all the snapraid_file for the disk.
|
||||
|
|
@ -531,21 +539,15 @@ static inline unsigned block_state_get(const struct snapraid_block* block)
|
|||
if (block == BLOCK_EMPTY)
|
||||
return BLOCK_STATE_EMPTY;
|
||||
|
||||
return block->private_file_mixed & BLOCK_STATE_MASK;
|
||||
return block->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the block.
|
||||
*/
|
||||
static inline void block_state_set(struct snapraid_block* block, unsigned state)
|
||||
{
|
||||
/* ensure that the state can be stored inside the file pointer */
|
||||
if ((state & BLOCK_STATE_MASK) != state) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal error when setting the block state %u\n", state);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
block->private_file_mixed &= ~(uintptr_t)BLOCK_STATE_MASK;
|
||||
block->private_file_mixed |= state & BLOCK_STATE_MASK;
|
||||
block->state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -724,6 +726,26 @@ static inline tommy_uint32_t file_stamp_hash(data_off_t size, int64_t mtime_sec,
|
|||
return tommy_inthash_u32((tommy_uint32_t)size ^ tommy_inthash_u32(mtime_sec ^ tommy_inthash_u32(mtime_nsec)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a chunk.
|
||||
*/
|
||||
struct snapraid_chunk* chunk_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count);
|
||||
|
||||
/**
|
||||
* Deallocate a chunk.
|
||||
*/
|
||||
void chunk_free(struct snapraid_chunk* chunk);
|
||||
|
||||
/**
|
||||
* Compare chunk by parity position.
|
||||
*/
|
||||
int chunk_parity_compare(const void* void_a, const void* void_b);
|
||||
|
||||
/**
|
||||
* Compare chunk by file and file position.
|
||||
*/
|
||||
int chunk_file_compare(const void* void_a, const void* void_b);
|
||||
|
||||
static inline int link_flag_has(const struct snapraid_link* link, unsigned mask)
|
||||
{
|
||||
return (link->flag & mask) == mask;
|
||||
|
|
@ -829,12 +851,41 @@ void disk_free(struct snapraid_disk* disk);
|
|||
/**
|
||||
* Get the size of the disk in blocks.
|
||||
*/
|
||||
block_off_t disk_block_size(struct snapraid_disk* disk);
|
||||
block_off_t disk_size(struct snapraid_disk* disk);
|
||||
|
||||
/**
|
||||
* Set a filesystem mapping between file and parity positions.
|
||||
* Check if a disk is totally empty and can be discarded from the content file.
|
||||
* A disk is empty if it doesn't contain any file, symlink, hardlink or dir
|
||||
* and without any DELETED block.
|
||||
* The blockmax is used to limit the search of DELETED block up to blockmax.
|
||||
*/
|
||||
void fs_par2file_set(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos);
|
||||
int disk_is_empty(struct snapraid_disk* disk, block_off_t blockmax);
|
||||
|
||||
/**
|
||||
* Allocate a parity position for the specified file position.
|
||||
*
|
||||
* After this call you can use the par2file/par2block operations
|
||||
* to query the relation.
|
||||
*/
|
||||
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos);
|
||||
|
||||
/**
|
||||
* Deallocate the parity position.
|
||||
*
|
||||
* After this call you can the par2file/par2block operations
|
||||
* won't find anymore the parity association.
|
||||
*/
|
||||
void fs_deallocate(struct snapraid_disk* disk, block_off_t pos);
|
||||
|
||||
/**
|
||||
* Get the block from the file position.
|
||||
*/
|
||||
static inline struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
assert(file_pos < file->blockmax);
|
||||
|
||||
return &file->blockvec[file_pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file position from the parity position.
|
||||
|
|
@ -847,36 +898,12 @@ struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t pa
|
|||
*/
|
||||
block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos);
|
||||
|
||||
/**
|
||||
* Get the block from the file position.
|
||||
*/
|
||||
struct snapraid_block* fs_file2block_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos);
|
||||
|
||||
/**
|
||||
* Clear a specific block.
|
||||
*/
|
||||
void fs_par2block_clear(struct snapraid_disk* disk, block_off_t pos);
|
||||
|
||||
/**
|
||||
* Set the block from parity position.
|
||||
* In case of holes, everything is initialized to 0.
|
||||
*/
|
||||
void fs_par2block_set(struct snapraid_disk* disk, block_off_t pos, struct snapraid_block* block);
|
||||
|
||||
/**
|
||||
* Get the block from the parity position.
|
||||
* Returns BLOCK_EMPTY==0 if the block is over the end of the disk or not used.
|
||||
*/
|
||||
struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos);
|
||||
|
||||
/**
|
||||
* Check if a disk is totally empty and can be discarded from the content file.
|
||||
* A disk is empty if it doesn't contain any file, symlink, hardlink or dir
|
||||
* and without any DELETED block.
|
||||
* The blockmax is used to limit the search of DELETED block up to blockmax.
|
||||
*/
|
||||
int disk_is_empty(struct snapraid_disk* disk, block_off_t blockmax);
|
||||
|
||||
/**
|
||||
* Allocates a disk mapping.
|
||||
* Uses uuid="" if not available.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ block_off_t parity_allocated_size(struct snapraid_state* state)
|
|||
struct snapraid_disk* disk = i->data;
|
||||
|
||||
/* start from the declared size */
|
||||
block_off_t block = disk_block_size(disk);
|
||||
block_off_t block = disk_size(disk);
|
||||
|
||||
/* decrease the block until an allocated one, but part of a file */
|
||||
/* we don't stop at deleted blocks, because we want to have them cleared */
|
||||
|
|
@ -64,7 +64,7 @@ block_off_t parity_used_size(struct snapraid_state* state)
|
|||
struct snapraid_disk* disk = i->data;
|
||||
|
||||
/* start from the declared size */
|
||||
block_off_t block = disk_block_size(disk);
|
||||
block_off_t block = disk_size(disk);
|
||||
|
||||
/* decrease the block until an used one */
|
||||
while (block > parity_block && !block_has_file_and_valid_parity(fs_par2block_get(disk, block - 1)))
|
||||
|
|
|
|||
|
|
@ -170,19 +170,27 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file*
|
|||
/* allocate the blocks of the file */
|
||||
parity_pos = disk->first_free_block;
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
struct snapraid_block* block;
|
||||
struct snapraid_block* over_block;
|
||||
snapraid_info info;
|
||||
struct snapraid_block* block = fs_file2block_get(disk, file, i);
|
||||
|
||||
/* increment the position until the first really free block */
|
||||
while (block_has_file(fs_par2block_get(disk, parity_pos)))
|
||||
++parity_pos;
|
||||
|
||||
/* set the position */
|
||||
block->private_parity_pos = parity_pos;
|
||||
/* get block we are going to overwrite, if any */
|
||||
over_block = fs_par2block_get(disk, parity_pos);
|
||||
|
||||
/* deallocate it */
|
||||
if (over_block != BLOCK_EMPTY)
|
||||
fs_deallocate(disk, parity_pos);
|
||||
|
||||
/* get block specific info */
|
||||
info = info_get(&state->infoarr, parity_pos);
|
||||
|
||||
/* get the new block we are going to write */
|
||||
block = fs_file2block_get(file, i);
|
||||
|
||||
/* if the file block already has an updated hash without rehash */
|
||||
if (block_has_updated_hash(block) && !info_get_rehash(info)) {
|
||||
/* the only possible case is for REP blocks */
|
||||
|
|
@ -193,26 +201,22 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file*
|
|||
|
||||
/* and keep the hash as it's */
|
||||
} else {
|
||||
struct snapraid_block* over_block;
|
||||
unsigned block_state;
|
||||
unsigned over_state;
|
||||
|
||||
/* convert to a CHG block */
|
||||
block_state_set(block, BLOCK_STATE_CHG);
|
||||
|
||||
/* block to overwrite */
|
||||
over_block = fs_par2block_get(disk, parity_pos);
|
||||
|
||||
/* state of the block we are going to overwrite */
|
||||
block_state = block_state_get(over_block);
|
||||
over_state = block_state_get(over_block);
|
||||
|
||||
/* if the block is an empty one */
|
||||
if (block_state == BLOCK_STATE_EMPTY) {
|
||||
if (over_state == BLOCK_STATE_EMPTY) {
|
||||
/* the block was empty and filled with zeros */
|
||||
/* set the hash to the special ZERO value */
|
||||
hash_zero_set(block->hash);
|
||||
} else {
|
||||
/* otherwise it's a DELETED one */
|
||||
assert(block_state == BLOCK_STATE_DELETED);
|
||||
assert(over_state == BLOCK_STATE_DELETED);
|
||||
|
||||
/* copy the past hash of the block */
|
||||
memcpy(block->hash, over_block->hash, HASH_SIZE);
|
||||
|
|
@ -235,7 +239,7 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file*
|
|||
}
|
||||
|
||||
/* store in the disk map, after invalidating all the other blocks */
|
||||
fs_par2block_set(disk, parity_pos, block);
|
||||
fs_allocate(disk, parity_pos, file, i);
|
||||
|
||||
/* set the new free position */
|
||||
disk->first_free_block = parity_pos + 1;
|
||||
|
|
@ -263,7 +267,7 @@ static void scan_file_deallocate(struct snapraid_scan* scan, struct snapraid_fil
|
|||
|
||||
/* free all the blocks of the file */
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
struct snapraid_block* block = fs_file2block_get(disk, file, i);
|
||||
struct snapraid_block* block = fs_file2block_get(file, i);
|
||||
block_off_t parity_pos = fs_file2par_get(disk, file, i);
|
||||
unsigned block_state;
|
||||
|
||||
|
|
@ -357,7 +361,7 @@ static int file_is_full_invalid_parity_and_stable(struct snapraid_state* state,
|
|||
/* check all blocks */
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
snapraid_info info;
|
||||
struct snapraid_block* block = fs_file2block_get(disk, file, i);
|
||||
struct snapraid_block* block = fs_file2block_get(file, i);
|
||||
block_off_t parity_pos = fs_file2par_get(disk, file, i);
|
||||
|
||||
/* exclude blocks with parity */
|
||||
|
|
@ -390,7 +394,7 @@ static int file_is_full_hashed_and_stable(struct snapraid_state* state, struct s
|
|||
/* check all blocks */
|
||||
for (i = 0; i < file->blockmax; ++i) {
|
||||
snapraid_info info;
|
||||
struct snapraid_block* block = fs_file2block_get(disk, file, i);
|
||||
struct snapraid_block* block = fs_file2block_get(file, i);
|
||||
block_off_t parity_pos = fs_file2par_get(disk, file, i);
|
||||
|
||||
/* exclude blocks without hash */
|
||||
|
|
|
|||
|
|
@ -78,14 +78,14 @@ void memory(void)
|
|||
{
|
||||
log_tag("memory:used:%" PRIu64 "\n", (uint64_t)malloc_counter());
|
||||
|
||||
/* size of the block + the size of the blockarr pointer to it */
|
||||
log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block) + sizeof(void*)));
|
||||
|
||||
/* size of the file + the size of the 3 hashtables pointers to it, * 2 for the hashtable grow factor */
|
||||
log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file) + 3*2*sizeof(void*)));
|
||||
/* size of the block */
|
||||
log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block)));
|
||||
log_tag("memory:chunk:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_chunk)));
|
||||
log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file)));
|
||||
log_tag("memory:link:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_link)));
|
||||
log_tag("memory:dir:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_dir)));
|
||||
|
||||
msg_progress("Using %u MiB of memory.\n", (unsigned)(malloc_counter() / (1024 * 1024)));
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -1440,7 +1440,7 @@ static void position_clear_deleted(struct snapraid_state* state, block_off_t pos
|
|||
/* if the block is deleted */
|
||||
if (block_state_get(block) == BLOCK_STATE_DELETED) {
|
||||
/* set it to empty */
|
||||
fs_par2block_clear(disk, pos);
|
||||
fs_deallocate(disk, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1721,15 +1721,7 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
|
||||
/* fill the blocks in the run */
|
||||
while (v_count) {
|
||||
struct snapraid_block* block = &file->blockvec[v_idx];
|
||||
|
||||
if (block->private_parity_pos != POS_INVALID) {
|
||||
/* LCOV_EXCL_START */
|
||||
decoding_error(path, f);
|
||||
log_fatal("Internal inconsistency in block position!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
struct snapraid_block* block = fs_file2block_get(file, v_idx);
|
||||
|
||||
switch (c) {
|
||||
case 'b' :
|
||||
|
|
@ -1753,8 +1745,6 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
block->private_parity_pos = v_pos;
|
||||
|
||||
/* read the hash only for 'blk/chg/rep', and not for 'new' */
|
||||
if (c != 'n') {
|
||||
ret = sread(f, block->hash, HASH_SIZE);
|
||||
|
|
@ -1811,8 +1801,8 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* insert the block in the block array */
|
||||
fs_par2block_set(disk, v_pos, block);
|
||||
/* set the parity association */
|
||||
fs_allocate(disk, v_pos, file, v_idx);
|
||||
|
||||
/* go to the next block */
|
||||
++v_idx;
|
||||
|
|
@ -1925,7 +1915,6 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
} else if (c == 'h') {
|
||||
/* hole */
|
||||
uint32_t v_pos;
|
||||
uint32_t i;
|
||||
struct snapraid_disk* disk;
|
||||
uint32_t mapping;
|
||||
|
||||
|
|
@ -1941,6 +1930,7 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
|
||||
v_pos = 0;
|
||||
while (v_pos < blockmax) {
|
||||
uint32_t v_idx;
|
||||
uint32_t v_count;
|
||||
struct snapraid_file* deleted;
|
||||
|
||||
|
|
@ -1967,23 +1957,20 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
case 'o' :
|
||||
/* if it's a run of deleted blocks */
|
||||
|
||||
/* allocate the deleted file */
|
||||
/* allocate a fake deleted file */
|
||||
deleted = file_alloc(state->block_size, "", v_count * state->block_size, 0, 0, 0, 0);
|
||||
|
||||
/* insert it in the list of deleted files */
|
||||
tommy_list_insert_tail(&disk->deletedlist, &deleted->nodelist, deleted);
|
||||
|
||||
/* process all blocks */
|
||||
i = 0;
|
||||
v_idx = 0;
|
||||
while (v_count) {
|
||||
struct snapraid_block* block = &deleted->blockvec[i];
|
||||
struct snapraid_block* block = fs_file2block_get(deleted, v_idx);
|
||||
|
||||
/* set the block as deleted */
|
||||
block_state_set(block, BLOCK_STATE_DELETED);
|
||||
|
||||
/* set the position */
|
||||
block->private_parity_pos = v_pos;
|
||||
|
||||
/* read the hash */
|
||||
ret = sread(f, block->hash, HASH_SIZE);
|
||||
if (ret < 0) {
|
||||
|
|
@ -2012,13 +1999,13 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
/* LCOV_EXCL_STOP */
|
||||
} else {
|
||||
/* insert the block in the block array */
|
||||
fs_par2block_set(disk, v_pos, block);
|
||||
fs_allocate(disk, v_pos, deleted, v_idx);
|
||||
}
|
||||
|
||||
/* go to next block */
|
||||
++v_pos;
|
||||
++v_idx;
|
||||
--v_count;
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
case 'O' :
|
||||
|
|
@ -2463,7 +2450,7 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
unsigned count_symlink;
|
||||
unsigned count_dir;
|
||||
tommy_node* i;
|
||||
block_off_t b;
|
||||
block_off_t idx;
|
||||
block_off_t blockmax;
|
||||
block_off_t begin;
|
||||
time_t info_oldest;
|
||||
|
|
@ -2484,10 +2471,10 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
/* and get some other info */
|
||||
info_oldest = 0; /* oldest time in info */
|
||||
info_has_rehash = 0; /* if there is a rehash info */
|
||||
for (b = 0; b < blockmax; ++b) {
|
||||
for (idx = 0; idx < blockmax; ++idx) {
|
||||
/* if the position is used */
|
||||
if (position_is_required(state, b)) {
|
||||
snapraid_info info = info_get(&state->infoarr, b);
|
||||
if (position_is_required(state, idx)) {
|
||||
snapraid_info info = info_get(&state->infoarr, idx);
|
||||
|
||||
/* only if there is some info to store */
|
||||
if (info) {
|
||||
|
|
@ -2501,10 +2488,10 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
}
|
||||
} else {
|
||||
/* clear any previous info */
|
||||
info_set(&state->infoarr, b, 0);
|
||||
info_set(&state->infoarr, idx, 0);
|
||||
|
||||
/* and clear any deleted blocks */
|
||||
position_clear_deleted(state, b);
|
||||
position_clear_deleted(state, idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2633,7 +2620,6 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
/* for each file */
|
||||
for (j = disk->filelist; j != 0; j = j->next) {
|
||||
struct snapraid_file* file = j->data;
|
||||
struct snapraid_block* blockvec = file->blockvec;
|
||||
uint64_t size;
|
||||
uint64_t mtime_sec;
|
||||
int32_t mtime_nsec;
|
||||
|
|
@ -2665,22 +2651,23 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
/* for all the blocks of the file */
|
||||
begin = 0;
|
||||
while (begin < file->blockmax) {
|
||||
unsigned block_state = block_state_get(blockvec + begin);
|
||||
uint32_t v_pos = blockvec[begin].private_parity_pos;
|
||||
unsigned v_state = block_state_get(fs_file2block_get(file, begin));
|
||||
block_off_t v_pos = fs_file2par_get(disk, file, begin);
|
||||
uint32_t v_count;
|
||||
|
||||
block_off_t end;
|
||||
|
||||
/* find the end of run of blocks */
|
||||
end = begin + 1;
|
||||
while (end < file->blockmax
|
||||
&& block_state == block_state_get(blockvec + end)
|
||||
&& blockvec[end - 1].private_parity_pos + 1 == blockvec[end].private_parity_pos
|
||||
) {
|
||||
while (end < file->blockmax) {
|
||||
if (v_state != block_state_get(fs_file2block_get(file, end)))
|
||||
break;
|
||||
if (v_pos + (end - begin) != fs_file2par_get(disk, file, end))
|
||||
break;
|
||||
++end;
|
||||
}
|
||||
|
||||
switch (block_state) {
|
||||
switch (v_state) {
|
||||
case BLOCK_STATE_BLK :
|
||||
sputc('b', f);
|
||||
break;
|
||||
|
|
@ -2692,7 +2679,7 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
break;
|
||||
default :
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency in state for block %u state %u\n", v_pos, block_state);
|
||||
log_fatal("Internal inconsistency in state for block %u state %u\n", v_pos, v_state);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
@ -2703,16 +2690,8 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
sputb32(v_count, f);
|
||||
|
||||
/* write hashes */
|
||||
for (b = begin; b < end; ++b) {
|
||||
struct snapraid_block* block = blockvec + b;
|
||||
|
||||
/* consistency check */
|
||||
if (block != fs_par2block_get(disk, block->private_parity_pos)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency in mismatch for block %u state %u in file '%s'\n", block->private_parity_pos, block_state, file->sub);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
for (idx = begin; idx < end; ++idx) {
|
||||
struct snapraid_block* block = fs_file2block_get(file, idx);
|
||||
|
||||
swrite(block->hash, HASH_SIZE, f);
|
||||
}
|
||||
|
|
@ -2808,14 +2787,6 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
while (begin < end) {
|
||||
struct snapraid_block* block = fs_par2block_get(disk, begin);
|
||||
|
||||
/* consistency check */
|
||||
if (block->private_parity_pos != begin) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency in delete position %u/%u!\n", block->private_parity_pos, begin);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
swrite(block->hash, HASH_SIZE, f);
|
||||
|
||||
++begin;
|
||||
|
|
|
|||
|
|
@ -1031,7 +1031,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* if it's a deleted block */
|
||||
if (block_state_get(block) == BLOCK_STATE_DELETED) {
|
||||
/* the parity is now updated without this block, so it's now empty */
|
||||
fs_par2block_clear(handle[j].disk, i);
|
||||
fs_deallocate(handle[j].disk, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,5 +38,6 @@
|
|||
#include "tommyarrayblk.c"
|
||||
#include "tommyarrayblkof.c"
|
||||
#include "tommylist.c"
|
||||
#include "tommytree.c"
|
||||
#include "tommyhashdyn.c"
|
||||
|
||||
|
|
|
|||
283
tommyds/tommytree.c
Normal file
283
tommyds/tommytree.c
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Andrea Mazzoleni. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tommytree.h"
|
||||
|
||||
#include <assert.h> /* for assert */
|
||||
|
||||
/******************************************************************************/
|
||||
/* tree */
|
||||
|
||||
void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp)
|
||||
{
|
||||
tree->root = 0;
|
||||
tree->count = 0;
|
||||
tree->cmp = cmp;
|
||||
}
|
||||
|
||||
static int tommy_tree_delta(tommy_tree_node* root)
|
||||
{
|
||||
int left_height = root->prev ? root->prev->key : 0;
|
||||
int right_height = root->next ? root->next->key : 0;
|
||||
|
||||
return left_height - right_height;
|
||||
}
|
||||
|
||||
/* AVL tree operations */
|
||||
static tommy_tree_node* tommy_tree_balance(tommy_tree_node*);
|
||||
|
||||
static tommy_tree_node* tommy_tree_rotate_left(tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* next = root->next;
|
||||
|
||||
root->next = next->prev;
|
||||
|
||||
next->prev = tommy_tree_balance(root);
|
||||
|
||||
return tommy_tree_balance(next);
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_rotate_right(tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* prev = root->prev;
|
||||
|
||||
root->prev = prev->next;
|
||||
|
||||
prev->next = tommy_tree_balance(root);
|
||||
|
||||
return tommy_tree_balance(prev);
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_move_right(tommy_tree_node* root, tommy_tree_node* node)
|
||||
{
|
||||
if (!root)
|
||||
return node;
|
||||
|
||||
root->next = tommy_tree_move_right(root->next, node);
|
||||
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_balance(tommy_tree_node* root)
|
||||
{
|
||||
int delta = tommy_tree_delta(root);
|
||||
|
||||
if (delta < -1) {
|
||||
if (tommy_tree_delta(root->next) > 0)
|
||||
root->next = tommy_tree_rotate_right(root->next);
|
||||
return tommy_tree_rotate_left(root);
|
||||
}
|
||||
|
||||
if (delta > 1) {
|
||||
if (tommy_tree_delta(root->prev) < 0)
|
||||
root->prev = tommy_tree_rotate_left(root->prev);
|
||||
return tommy_tree_rotate_right(root);
|
||||
}
|
||||
|
||||
/* recompute key */
|
||||
root->key = 0;
|
||||
|
||||
if (root->prev && root->prev->key > root->key)
|
||||
root->key = root->prev->key;
|
||||
|
||||
if (root->next && root->next->key > root->key)
|
||||
root->key = root->next->key;
|
||||
|
||||
/* count itself */
|
||||
root->key += 1;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_insert_node(tommy_compare_func* cmp, tommy_tree_node* root, tommy_tree_node** let)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return *let;
|
||||
|
||||
c = cmp((*let)->data, root->data);
|
||||
|
||||
if (c < 0) {
|
||||
root->prev = tommy_tree_insert_node(cmp, root->prev, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
if (c > 0) {
|
||||
root->next = tommy_tree_insert_node(cmp, root->next, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
/* already present, set the return pointer */
|
||||
*let = root;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
|
||||
{
|
||||
node->data = data;
|
||||
node->prev = 0;
|
||||
node->next = 0;
|
||||
node->key = 0;
|
||||
|
||||
tree->root = tommy_tree_insert_node(tree->cmp, tree->root, &node);
|
||||
|
||||
++tree->count;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_remove_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data, tommy_tree_node** let)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return 0;
|
||||
|
||||
c = cmp(data, root->data);
|
||||
|
||||
if (c < 0) {
|
||||
root->prev = tommy_tree_remove_node(cmp, root->prev, data, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
if (c > 0) {
|
||||
root->next = tommy_tree_remove_node(cmp, root->next, data, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
/* found */
|
||||
*let = root;
|
||||
|
||||
return tommy_tree_move_right(root->prev, root->next);
|
||||
}
|
||||
|
||||
void* tommy_tree_remove(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = 0;
|
||||
|
||||
tree->root = tommy_tree_remove_node(tree->cmp, tree->root, data, &node);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return 0;
|
||||
|
||||
c = cmp(data, root->data);
|
||||
|
||||
if (c < 0)
|
||||
return tommy_tree_search_node(cmp, root->prev, data);
|
||||
|
||||
if (c > 0)
|
||||
return tommy_tree_search_node(cmp, root->next, data);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void* tommy_tree_search(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node)
|
||||
{
|
||||
return tommy_tree_remove(tree, node->data);
|
||||
}
|
||||
|
||||
static void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
|
||||
{
|
||||
tommy_tree_node* next;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
tommy_tree_foreach_node(root->prev, func);
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
func(root->data);
|
||||
|
||||
tommy_tree_foreach_node(next, func);
|
||||
}
|
||||
|
||||
void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
|
||||
{
|
||||
tommy_tree_foreach_node(tree->root, func);
|
||||
}
|
||||
|
||||
static void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_node* next;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
tommy_tree_foreach_arg_node(root->prev, func, arg);
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
func(arg, root->data);
|
||||
|
||||
tommy_tree_foreach_arg_node(next, func, arg);
|
||||
}
|
||||
|
||||
void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_foreach_arg_node(tree->root, func, arg);
|
||||
}
|
||||
|
||||
tommy_size_t tommy_tree_memory_usage(tommy_tree* tree)
|
||||
{
|
||||
return tommy_tree_count(tree) * sizeof(tommy_tree_node);
|
||||
}
|
||||
|
||||
214
tommyds/tommytree.h
Normal file
214
tommyds/tommytree.h
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Andrea Mazzoleni. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* AVL tree.
|
||||
*
|
||||
* This tree is a standard AVL tree implementation that stores elements in the order defined
|
||||
* by the comparsion function.
|
||||
*
|
||||
* \code
|
||||
* tommy_tree tree;
|
||||
*
|
||||
* tommy_tree_init(&tree, cmp);
|
||||
* \endcode
|
||||
*
|
||||
* To insert elements in the tree you have to call tommy_tree_insert() for
|
||||
* each element.
|
||||
* In the insertion call you have to specify the address of the node, the
|
||||
* address of the object.
|
||||
* The address of the object is used to initialize the tommy_node::data field
|
||||
* of the node.
|
||||
*
|
||||
* \code
|
||||
* struct object {
|
||||
* int value;
|
||||
* // other fields
|
||||
* tommy_tree_node node;
|
||||
* };
|
||||
*
|
||||
* struct object* obj = malloc(sizeof(struct object)); // creates the object
|
||||
*
|
||||
* obj->value = ...; // initializes the object
|
||||
*
|
||||
* tommy_tree_insert(&tree, &obj->node, obj); // inserts the object
|
||||
* \endcode
|
||||
*
|
||||
* To find and element in the tree you have to call tommy_tree_search() providing
|
||||
* the key to search.
|
||||
*
|
||||
* \code
|
||||
* struct object value_to_find = { 1 };
|
||||
* struct object* obj = tommy_tree_search(&tree, &value_to_find);
|
||||
* if (!obj) {
|
||||
* // not found
|
||||
* } else {
|
||||
* // found
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To remove an element from the tree you have to call tommy_tree_remove()
|
||||
* providing the key to search and remove.
|
||||
*
|
||||
* \code
|
||||
* struct object* obj = tommy_tree_remove(&tree, value_to_remove);
|
||||
* if (obj) {
|
||||
* free(obj); // frees the object allocated memory
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To destroy the tree you have to remove all the elements.
|
||||
*
|
||||
* If you need to iterate over all the elements in the tree, you can use
|
||||
* tommy_hashtable_foreach() or tommy_hashtable_foreach_arg().
|
||||
* If you need a more precise control with a real iteration, you have to insert
|
||||
* all the elements also in a ::tommy_list, and use the list to iterate.
|
||||
* See the \ref multiindex example for more detail.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYTREE_H
|
||||
#define __TOMMYTREE_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* tree */
|
||||
|
||||
/**
|
||||
* Tree node.
|
||||
* This is the node that you have to include inside your objects.
|
||||
*/
|
||||
typedef tommy_node tommy_tree_node;
|
||||
|
||||
typedef struct tommy_tree_struct {
|
||||
tommy_tree_node* root; /**< Root node. */
|
||||
tommy_count_t count; /**< Number of elements. */
|
||||
tommy_compare_func* cmp; /**< Comparison function. */
|
||||
} tommy_tree;
|
||||
|
||||
/**
|
||||
* Initializes the tree.
|
||||
*/
|
||||
void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp);
|
||||
|
||||
/**
|
||||
* Inserts an element in the tree.
|
||||
* If the element is already present, it's not inserted again.
|
||||
* You have to provide the pointer of the node embedded into the object,
|
||||
* the pointer at the object.
|
||||
* \param node Pointer at the node embedded into the object to insert.
|
||||
* \param data Pointer at the object to insert.
|
||||
* \return The element in the tree. Either the already existing one, or the one just inserted.
|
||||
*/
|
||||
void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
|
||||
|
||||
/**
|
||||
* Searches and removes an element.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The removed element, or 0 if not found.
|
||||
*/
|
||||
void* tommy_tree_remove(tommy_tree* tree, void* data);
|
||||
|
||||
/**
|
||||
* Searches an element in the tree.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first element found, or 0 if none.
|
||||
*/
|
||||
void* tommy_tree_search(tommy_tree* tree, void* data);
|
||||
|
||||
/**
|
||||
* Searches an element in the tree with a specific compare function.
|
||||
*
|
||||
* Like tommy_tree_search() but you can specify a different comparison function.
|
||||
* Note that this function must define a suborder of the original one.
|
||||
*
|
||||
* The ::data argument is going to always be the first argument of the comparison function,
|
||||
* and it can be something different than the objects in the tree.
|
||||
*/
|
||||
void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg);
|
||||
|
||||
/**
|
||||
* Removes an element from the tree.
|
||||
* You must already have the address of the element to remove.
|
||||
* \return The tommy_node::data field of the node removed.
|
||||
*/
|
||||
void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
|
||||
|
||||
/**
|
||||
* Calls the specified function for each element in the tree.
|
||||
*
|
||||
* The elements are processed in order.
|
||||
*
|
||||
* You can use this function to deallocate all the elements inserted.
|
||||
*
|
||||
* \code
|
||||
* tommy_tree tree;
|
||||
*
|
||||
* // initializes the tree
|
||||
* tommy_tree_init(&tree, cmp);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // creates an object
|
||||
* struct object* obj = malloc(sizeof(struct object));
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // insert it in the hashtable
|
||||
* tommy_tree_insert(&tree, &obj->node, obj);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // deallocates all the objects iterating the tree
|
||||
* tommy_tree_foreach(&tree, free);
|
||||
* \endcode
|
||||
*/
|
||||
void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func);
|
||||
|
||||
/**
|
||||
* Calls the specified function with an argument for each element in the tree.
|
||||
*/
|
||||
void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg);
|
||||
|
||||
/**
|
||||
* Gets the number of elements.
|
||||
*/
|
||||
tommy_inline tommy_count_t tommy_tree_count(tommy_tree* tree)
|
||||
{
|
||||
return tree->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of allocated memory.
|
||||
* It includes the size of the ::tommy_tree_node of the stored elements.
|
||||
*/
|
||||
tommy_size_t tommy_tree_memory_usage(tommy_tree* tree);
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue