mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Saves the disk mapping inside the content file.
This commit is contained in:
parent
99515f610c
commit
4adef9dc17
15 changed files with 399 additions and 119 deletions
8
HISTORY
8
HISTORY
|
|
@ -1,8 +1,8 @@
|
|||
SnapRAID HISTORY
|
||||
================
|
||||
|
||||
1.7 2011/9
|
||||
==========
|
||||
1.7 2011/10
|
||||
===========
|
||||
* If a file is modified or removed during a sync, the sync process doesn't
|
||||
stop anymore, but it will simply skip the file, resulting in an incomplete
|
||||
sync. Note that the sync will terminate with an error.
|
||||
|
|
@ -12,6 +12,10 @@ SnapRAID HISTORY
|
|||
a suggestion, but now it's a requirement in case you put them in data disks.
|
||||
* Added checks to ensure that data and parity disks are diffent, and to
|
||||
correctly count the number of copies of "content" files in different disks.
|
||||
* Removed the dependency of the 'disk' order specification in the configuration
|
||||
file. The used order is now saved in the content file to avoid to damage the
|
||||
dual parity in case the order is changed by the user.
|
||||
It also allows to remove or add disks from the array when using a dual parity.
|
||||
|
||||
1.6 2011/9 - THIS UPDATE IS HIGHLY RECOMMENDED!
|
||||
===============================================
|
||||
|
|
|
|||
25
Makefile.am
25
Makefile.am
|
|
@ -55,8 +55,7 @@ EXTRA_DIST = \
|
|||
noautomake.sh \
|
||||
README AUTHORS HISTORY INSTALL COPYING TODO TEST \
|
||||
snapraid.d snapraid.1 snapraid.txt \
|
||||
test-raid5.conf \
|
||||
test-raid6.conf \
|
||||
test-raid5.conf test-raid6.conf test-raid6-hole.conf \
|
||||
snapraid.conf.example \
|
||||
configure.windows-x86 configure.windows-x64 snapraid.conf.example.windows \
|
||||
acinclude.m4
|
||||
|
|
@ -107,12 +106,30 @@ check-local:
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf check
|
||||
echo --- Extend raid5 to raid6 with fix and check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid6.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf fix 2>/dev/null
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Dry
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf dry
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf dry
|
||||
echo --- Make a hole
|
||||
mv test/disk2 test/disk2.old
|
||||
mkdir test/disk2
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --force-empty -c $(srcdir)/test-raid6.conf sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Use the with the hole
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6-hole.conf sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6-hole.conf check
|
||||
rm -r test/disk1/_a
|
||||
rm -r test/disk3/_a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6-hole.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6-hole.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6-hole.conf dry
|
||||
echo --- Fill the hole
|
||||
rm -r test/disk2
|
||||
mv test/disk2.old test/disk2
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Delete some files, create some new, sync and check
|
||||
rm test/disk1/1*
|
||||
rm test/disk2/2*
|
||||
|
|
@ -282,7 +299,7 @@ check-local:
|
|||
|
||||
# Manual testing
|
||||
|
||||
MANUAL = ./snapraid --test-skip-self -c test-raid5.conf sync
|
||||
MANUAL = ./snapraid --test-skip-self --test-skip-device -c test-raid5.conf sync
|
||||
|
||||
valgrind:
|
||||
valgrind \
|
||||
|
|
|
|||
12
TODO
12
TODO
|
|
@ -5,13 +5,7 @@ This is the list of TODO items for SnapRAID.
|
|||
|
||||
- v1.7
|
||||
|
||||
* Put the content files in the data disks in the default configuration file.
|
||||
|
||||
* Remove the dependency of the 'disk' order specification in the conf file.
|
||||
The used order can be defined by SnapRAID and saved in the content file to avoid
|
||||
to damage the parity in case the order is changed by the user.
|
||||
It will also allow to remove disks from the array when using a dual parity
|
||||
configuration, as it's always possible to keep internally dummy empty disks.
|
||||
* When scanning, ensure that each file is in the expected disk.
|
||||
|
||||
* When fixing, before overwriting the present file, make a copy of it just in
|
||||
case that the original file cannot be completely recovered.
|
||||
|
|
@ -21,6 +15,10 @@ The same for symlink if a file with the same name exist or viceversa.
|
|||
|
||||
- v1.8
|
||||
|
||||
* Check why saving the content files is so slow. fprintf() is the bottleneck.
|
||||
|
||||
* Put the content files in the data disks in the default configuration file.
|
||||
|
||||
* Easy way to find duplicates using the computed hash.
|
||||
|
||||
* Easy way to find files using a specified block.
|
||||
|
|
|
|||
31
check.c
31
check.c
|
|
@ -63,7 +63,7 @@ static int blockcmp(struct snapraid_state* state, struct snapraid_block* block,
|
|||
|
||||
/**
|
||||
* Repair errors.
|
||||
* Returns <0 if failure for missing stratey, >0 if data is wrong and we cannot rebuild correctly, 0 on success.
|
||||
* Returns <0 if failure for missing strategy, >0 if data is wrong and we cannot rebuild correctly, 0 on success.
|
||||
* If success, the parity and qarity are computed in the buffer variable.
|
||||
*/
|
||||
static int repair(struct snapraid_state* state, unsigned i, unsigned diskmax, struct failed_struct* failed, unsigned failed_count, unsigned char** buffer, unsigned char* buffer_parity, unsigned char* buffer_qarity, unsigned char* buffer_zero)
|
||||
|
|
@ -73,7 +73,7 @@ static int repair(struct snapraid_state* state, unsigned i, unsigned diskmax, st
|
|||
|
||||
/* no fix required */
|
||||
if (failed_count == 0) {
|
||||
/* compute parity and qarity to check it */
|
||||
/* recompute only parity and qarity */
|
||||
raid_gen(state->level, buffer, diskmax, state->block_size);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -199,7 +199,9 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
|
|||
/* for each disk */
|
||||
one_tocheck = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
struct snapraid_block* block = 0;
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (block
|
||||
&& block_flag_has(block, BLOCK_HAS_HASH) /* only if the block is hashed */
|
||||
&& !file_flag_has(block_file_get(block), FILE_IS_EXCLUDED) /* only if the file is not filtered out */
|
||||
|
|
@ -231,7 +233,9 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
|
|||
/* for each disk */
|
||||
one_tocheck = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
struct snapraid_block* block = 0;
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (block
|
||||
&& block_flag_has(block, BLOCK_HAS_HASH) /* only if the block is hashed */
|
||||
&& !file_flag_has(block_file_get(block), FILE_IS_EXCLUDED) /* only if the file is not filtered out */
|
||||
|
|
@ -254,6 +258,14 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
|
|||
unsigned char hash[HASH_SIZE];
|
||||
struct snapraid_block* block;
|
||||
|
||||
/* if the disk position is not used */
|
||||
if (!handle[j].disk) {
|
||||
/* use an empty block */
|
||||
memset(buffer[j], 0, state->block_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if the disk block is not used */
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block) {
|
||||
/* use an empty block */
|
||||
|
|
@ -524,11 +536,12 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
|
|||
if (fix) {
|
||||
/* for all the files of this block check if we need to fix the modification time */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block;
|
||||
struct snapraid_block* block = 0;
|
||||
struct snapraid_file* collide_file;
|
||||
uint64_t inode;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block) {
|
||||
/* if no block, no file and nothing to do */
|
||||
continue;
|
||||
|
|
@ -593,9 +606,13 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
|
|||
/* check all the links */
|
||||
for(i=0;i<diskmax;++i) {
|
||||
tommy_node* node;
|
||||
struct snapraid_disk* disk = handle[i].disk;
|
||||
struct snapraid_disk* disk;
|
||||
|
||||
if (!handle[i].disk)
|
||||
continue;
|
||||
|
||||
/* for each link in the disk */
|
||||
disk = handle[i].disk;
|
||||
node = disk->linklist;
|
||||
while (node) {
|
||||
char path[PATH_MAX];
|
||||
|
|
|
|||
5
dry.c
5
dry.c
|
|
@ -53,9 +53,10 @@ static int state_dry_process(struct snapraid_state* state, int parity_f, int qar
|
|||
/* for each disk, process the block */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
int read_size;
|
||||
struct snapraid_block* block;
|
||||
struct snapraid_block* block = 0;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
16
elem.c
16
elem.c
|
|
@ -363,3 +363,19 @@ void disk_free(struct snapraid_disk* disk)
|
|||
free(disk);
|
||||
}
|
||||
|
||||
struct snapraid_map* map_alloc(const char* name, unsigned position)
|
||||
{
|
||||
struct snapraid_map* map;
|
||||
|
||||
map = malloc_nofail(sizeof(struct snapraid_map));
|
||||
pathcpy(map->name, sizeof(map->name), name);
|
||||
map->position = position;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void map_free(struct snapraid_map* map)
|
||||
{
|
||||
free(map);
|
||||
}
|
||||
|
||||
|
|
|
|||
21
elem.h
21
elem.h
|
|
@ -133,6 +133,17 @@ struct snapraid_disk {
|
|||
tommy_node node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disk mapping.
|
||||
*/
|
||||
struct snapraid_map {
|
||||
char name[PATH_MAX]; /**< Name of the disk. */
|
||||
unsigned position; /**< Position of the disk in the parity. */
|
||||
|
||||
/* nodes for data structures */
|
||||
tommy_node node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a content.
|
||||
*/
|
||||
|
|
@ -323,5 +334,15 @@ static inline struct snapraid_block* disk_block_get(struct snapraid_disk* disk,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a disk mapping.
|
||||
*/
|
||||
struct snapraid_map* map_alloc(const char* name, unsigned position);
|
||||
|
||||
/**
|
||||
* Deallocates a disk mapping.
|
||||
*/
|
||||
void map_free(struct snapraid_map* map);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
44
handle.c
44
handle.c
|
|
@ -327,21 +327,49 @@ struct snapraid_handle* handle_map(struct snapraid_state* state, unsigned* handl
|
|||
{
|
||||
tommy_node* i;
|
||||
unsigned j;
|
||||
unsigned size = 0;
|
||||
struct snapraid_handle* handle;
|
||||
|
||||
/* first count the disks */
|
||||
*handlemax = 0;
|
||||
for(i=state->disklist;i!=0;i=i->next)
|
||||
++*handlemax;
|
||||
/* get the size of the mapping */
|
||||
size = 0;
|
||||
for(i=state->maplist;i!=0;i=i->next) {
|
||||
struct snapraid_map* map = i->data;
|
||||
if (map->position > size)
|
||||
size = map->position;
|
||||
}
|
||||
++size; /* size is one more than the max */
|
||||
|
||||
handle = malloc_nofail(*handlemax * sizeof(struct snapraid_handle));
|
||||
handle = malloc_nofail(size * sizeof(struct snapraid_handle));
|
||||
|
||||
/* set the vector */
|
||||
for(i=state->disklist,j=0;i!=0;i=i->next,++j) {
|
||||
handle[j].disk = i->data;
|
||||
for(j=0;j<size;++j) {
|
||||
/* default for empty position */
|
||||
handle[j].disk = 0;
|
||||
handle[j].file = 0;
|
||||
handle[j].f = -1;
|
||||
}
|
||||
|
||||
/* set the vector */
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
struct snapraid_map* map;
|
||||
struct snapraid_disk* disk = i->data;
|
||||
tommy_node* k;
|
||||
|
||||
/* search the mapping for this disk */
|
||||
for(k=state->maplist;k!=0;k=k->next) {
|
||||
map = k->data;
|
||||
if (strcmp(disk->name, map->name) == 0)
|
||||
break;
|
||||
}
|
||||
if (k==0) {
|
||||
fprintf(stderr, "Inconsistent disk mapping.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
continue;
|
||||
}
|
||||
|
||||
handle[map->position].disk = disk;
|
||||
}
|
||||
|
||||
*handlemax = size;
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -421,9 +421,6 @@ DIR is the mount point of the disk in the filesystem.
|
|||
You can change the mount point as you like, as long you
|
||||
keep the NAME fixed.
|
||||
.PP
|
||||
The specification order is also important, if you change it,
|
||||
you will invalidate the q\(hyparity file.
|
||||
.PP
|
||||
You should use one option for each disk of the array.
|
||||
.SS exclude/include PATTERN
|
||||
Defines the file or directory patterns to exclude and include
|
||||
|
|
@ -717,6 +714,8 @@ Defines the size of the block in bytes. It must match the size
|
|||
defined in the configuration file.
|
||||
.SS checksum CHECKSUM
|
||||
Defines the checksum kind used. It can be \(a"md5\(a" or \(a"murmur3\(a".
|
||||
.SS map NAME INDEX
|
||||
Defines the position INDEX of the disk NAME in the parity computation.
|
||||
.SS file DISK SIZE TIME INODE PATH
|
||||
Defines a file in the specified DISK.
|
||||
.PP
|
||||
|
|
|
|||
|
|
@ -319,9 +319,6 @@ Configuration
|
|||
You can change the mount point as you like, as long you
|
||||
keep the NAME fixed.
|
||||
|
||||
The specification order is also important, if you change it,
|
||||
you will invalidate the q-parity file.
|
||||
|
||||
You should use one option for each disk of the array.
|
||||
|
||||
exclude/include PATTERN
|
||||
|
|
@ -488,13 +485,16 @@ Content
|
|||
checksum CHECKSUM
|
||||
Defines the checksum kind used. It can be "md5" or "murmur3".
|
||||
|
||||
map NAME INDEX
|
||||
Defines the position INDEX of the disk NAME in the parity computation.
|
||||
|
||||
file DISK SIZE TIME INODE PATH
|
||||
Defines a file in the specified DISK.
|
||||
|
||||
The INODE number is used to identify the file in the "sync"
|
||||
command, allowing to rename or move the file in disk without
|
||||
the need to recompute the parity for it.
|
||||
|
||||
|
||||
The SIZE and TIME information are used to identify if the file
|
||||
changed from the last "sync" command, and if there is the need
|
||||
to recompute the parity.
|
||||
|
|
|
|||
14
snapraid.txt
14
snapraid.txt
|
|
@ -357,9 +357,6 @@ DIR is the mount point of the disk in the filesystem.
|
|||
You can change the mount point as you like, as long you
|
||||
keep the NAME fixed.
|
||||
|
||||
The specification order is also important, if you change it,
|
||||
you will invalidate the q-parity file.
|
||||
|
||||
You should use one option for each disk of the array.
|
||||
|
||||
7.5 exclude/include PATTERN
|
||||
|
|
@ -542,7 +539,12 @@ defined in the configuration file.
|
|||
|
||||
Defines the checksum kind used. It can be "md5" or "murmur3".
|
||||
|
||||
9.3 file DISK SIZE TIME INODE PATH
|
||||
9.3 map NAME INDEX
|
||||
------------------
|
||||
|
||||
Defines the position INDEX of the disk NAME in the parity computation.
|
||||
|
||||
9.4 file DISK SIZE TIME INODE PATH
|
||||
----------------------------------
|
||||
|
||||
Defines a file in the specified DISK.
|
||||
|
|
@ -558,7 +560,7 @@ to recompute the parity.
|
|||
The PATH information is used in the "check" and "fix" commands
|
||||
to identify the file.
|
||||
|
||||
9.4 blk BLOCK HASH
|
||||
9.5 blk BLOCK HASH
|
||||
------------------
|
||||
|
||||
Defines the ordered parity block list used by the last defined file.
|
||||
|
|
@ -569,7 +571,7 @@ BLOCK is the block position in the "parity" file.
|
|||
HASH is the hash of the block. In the last block of the file,
|
||||
the HASH is the hash of only the used part of the block.
|
||||
|
||||
9.5 inv BLOCK [HASH]
|
||||
9.6 inv BLOCK [HASH]
|
||||
--------------------
|
||||
|
||||
Like "blk", but inform that the parity of this block is invalid.
|
||||
|
|
|
|||
297
state.c
297
state.c
|
|
@ -38,6 +38,7 @@ void state_init(struct snapraid_state* state)
|
|||
state->level = 1; /* default is the lowest protection */
|
||||
state->hash = HASH_MURMUR3; /* default is the fastest */
|
||||
tommy_list_init(&state->disklist);
|
||||
tommy_list_init(&state->maplist);
|
||||
tommy_list_init(&state->contentlist);
|
||||
tommy_list_init(&state->filterlist);
|
||||
}
|
||||
|
|
@ -45,10 +46,89 @@ void state_init(struct snapraid_state* state)
|
|||
void state_done(struct snapraid_state* state)
|
||||
{
|
||||
tommy_list_foreach(&state->disklist, (tommy_foreach_func*)disk_free);
|
||||
tommy_list_foreach(&state->maplist, (tommy_foreach_func*)map_free);
|
||||
tommy_list_foreach(&state->contentlist, (tommy_foreach_func*)content_free);
|
||||
tommy_list_foreach(&state->filterlist, (tommy_foreach_func*)filter_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the configuration.
|
||||
*/
|
||||
static void state_config_check(struct snapraid_state* state, const char* path, int skip_device)
|
||||
{
|
||||
if (state->parity[0] == 0) {
|
||||
fprintf(stderr, "No 'parity' specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tommy_list_empty(&state->contentlist)) {
|
||||
fprintf(stderr, "No 'content' specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* count the content files */
|
||||
if (!skip_device) {
|
||||
unsigned content_count;
|
||||
tommy_node* i;
|
||||
|
||||
content_count = 0;
|
||||
for(i=state->contentlist;i!=0;i=i->next) {
|
||||
tommy_node* j;
|
||||
struct snapraid_content* content = i->data;
|
||||
|
||||
/* check if there are others in the same disk */
|
||||
for(j=i->next;j!=0;j=j->next) {
|
||||
struct snapraid_content* other = j->data;
|
||||
if (content->device == other->device) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j != 0) {
|
||||
/* skip it */
|
||||
continue;
|
||||
}
|
||||
|
||||
++content_count;
|
||||
}
|
||||
|
||||
if (content_count < state->level+1) {
|
||||
fprintf(stderr, "You must have at least %d 'content' files in different disks.\n", state->level+1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* check if all the data and parity disks are different */
|
||||
if (!skip_device) {
|
||||
tommy_node* i;
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
tommy_node* j;
|
||||
struct snapraid_disk* disk = i->data;
|
||||
|
||||
for(j=i->next;j!=0;j=j->next) {
|
||||
struct snapraid_disk* other = j->data;
|
||||
if (disk->device == other->device) {
|
||||
fprintf(stderr, "Disks '%s' and '%s' are on the same device.\n", disk->dir, other->dir);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (disk->device == state->parity_device) {
|
||||
fprintf(stderr, "Disk '%s' and parity '%s' are on the same device.\n", disk->dir, state->parity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (disk->device == state->qarity_device) {
|
||||
fprintf(stderr, "Disk '%s' and parity '%s' are on the same device.\n", disk->dir, state->qarity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (state->parity_device == state->qarity_device) {
|
||||
fprintf(stderr, "Parity '%s' and '%s' are on the same device.\n", state->parity, state->qarity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void state_config(struct snapraid_state* state, const char* path, int verbose, int gui, int force_zero, int force_empty, int expect_unrecoverable, int expect_recoverable, int skip_device)
|
||||
{
|
||||
STREAM* f;
|
||||
|
|
@ -306,77 +386,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i
|
|||
|
||||
sclose(f);
|
||||
|
||||
if (state->parity[0] == 0) {
|
||||
fprintf(stderr, "No 'parity' specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tommy_list_empty(&state->contentlist)) {
|
||||
fprintf(stderr, "No 'content' specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* count the content files */
|
||||
if (!skip_device) {
|
||||
unsigned content_count;
|
||||
tommy_node* i;
|
||||
|
||||
content_count = 0;
|
||||
for(i=state->contentlist;i!=0;i=i->next) {
|
||||
tommy_node* j;
|
||||
struct snapraid_content* content = i->data;
|
||||
|
||||
/* check if there are others in the same disk */
|
||||
for(j=i->next;j!=0;j=j->next) {
|
||||
struct snapraid_content* other = j->data;
|
||||
if (content->device == other->device) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j != 0) {
|
||||
/* skip it */
|
||||
continue;
|
||||
}
|
||||
|
||||
++content_count;
|
||||
}
|
||||
|
||||
if (content_count < state->level+1) {
|
||||
fprintf(stderr, "You must have at least %d 'content' files in different disks.\n", state->level+1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* check if all the data and parity disks are different */
|
||||
if (!skip_device) {
|
||||
tommy_node* i;
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
tommy_node* j;
|
||||
struct snapraid_disk* disk = i->data;
|
||||
|
||||
for(j=i->next;j!=0;j=j->next) {
|
||||
struct snapraid_disk* other = j->data;
|
||||
if (disk->device == other->device) {
|
||||
fprintf(stderr, "Disks '%s' and '%s' are on the same device.\n", disk->dir, other->dir);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (disk->device == state->parity_device) {
|
||||
fprintf(stderr, "Disk '%s' and parity '%s' are on the same device.\n", disk->dir, state->parity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (disk->device == state->qarity_device) {
|
||||
fprintf(stderr, "Disk '%s' and parity '%s' are on the same device.\n", disk->dir, state->qarity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (state->parity_device == state->qarity_device) {
|
||||
fprintf(stderr, "Parity '%s' and '%s' are on the same device.\n", state->parity, state->qarity);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
state_config_check(state, path, skip_device);
|
||||
|
||||
if (state->gui) {
|
||||
tommy_node* i;
|
||||
|
|
@ -396,6 +406,108 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the disk mapping if required.
|
||||
*/
|
||||
static void state_map(struct snapraid_state* state)
|
||||
{
|
||||
unsigned hole;
|
||||
tommy_node* i;
|
||||
|
||||
/* removes all the mapping without a disk */
|
||||
/* this happens when a disk is removed from the configuration file */
|
||||
for(i=state->maplist;i!=0;) {
|
||||
struct snapraid_map* map = i->data;
|
||||
struct snapraid_disk* disk;
|
||||
tommy_node* j;
|
||||
|
||||
for(j=state->disklist;j!=0;j=j->next) {
|
||||
disk = j->data;
|
||||
if (strcmp(disk->name, map->name) == 0) {
|
||||
/* disk found */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* go to the next mapping before removing */
|
||||
i = i->next;
|
||||
|
||||
if (j==0) {
|
||||
/* disk not found, remove the mapping */
|
||||
tommy_list_remove_existing(&state->maplist, &map->node);
|
||||
map_free(map);
|
||||
}
|
||||
}
|
||||
|
||||
/* maps each unmapped disk present in the configuration file in the first available hole */
|
||||
/* this happens when you add disks for the first time in the configuration file */
|
||||
hole = 0; /* first position to try */
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
struct snapraid_map* map;
|
||||
tommy_node* j;
|
||||
|
||||
/* check if the disk is already mapped */
|
||||
for(j=state->maplist;j!=0;j=j->next) {
|
||||
map = j->data;
|
||||
if (strcmp(disk->name, map->name) == 0) {
|
||||
/* mapping found */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j!=0)
|
||||
continue;
|
||||
|
||||
/* mapping not found, search for an hole */
|
||||
while (1) {
|
||||
for(j=state->maplist;j!=0;j=j->next) {
|
||||
map = j->data;
|
||||
if (map->position == hole) {
|
||||
/* position already used */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j==0) {
|
||||
/* hole found */
|
||||
break;
|
||||
}
|
||||
|
||||
/* try with the next one */
|
||||
++hole;
|
||||
}
|
||||
|
||||
/* insert the new mapping */
|
||||
map = map_alloc(disk->name, hole);
|
||||
|
||||
tommy_list_insert_tail(&state->maplist, &map->node, map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the content.
|
||||
*/
|
||||
static void state_content_check(struct snapraid_state* state, const char* path)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
/* checks that any map has different name and position */
|
||||
for(i=state->maplist;i!=0;i=i->next) {
|
||||
struct snapraid_map* map = i->data;
|
||||
tommy_node* j;
|
||||
for(j=i->next;j!=0;j=j->next) {
|
||||
struct snapraid_map* other = j->data;
|
||||
if (strcmp(map->name, other->name) == 0) {
|
||||
fprintf(stderr, "Colliding 'map' disk specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (map->position == other->position) {
|
||||
fprintf(stderr, "Colliding 'map' index specification in '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void state_read(struct snapraid_state* state)
|
||||
{
|
||||
STREAM* f;
|
||||
|
|
@ -448,6 +560,9 @@ void state_read(struct snapraid_state* state)
|
|||
|
||||
/* if not found, assume empty */
|
||||
if (!f) {
|
||||
/* create the initial mapping */
|
||||
state_map(state);
|
||||
|
||||
fprintf(stderr, "No content file found. Assuming empty.\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -764,6 +879,31 @@ void state_read(struct snapraid_state* state)
|
|||
fprintf(stderr, "Please restore the 'block_size' value in the configuration file to '%u'\n", blksize / 1024);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (strcmp(tag, "map") == 0) {
|
||||
struct snapraid_map* map;
|
||||
uint32_t v_pos;
|
||||
|
||||
ret = sgettok(f, buffer, sizeof(buffer));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid 'map' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
c = sgetc(f);
|
||||
if (c != ' ') {
|
||||
fprintf(stderr, "Invalid 'map' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = sgetu32(f, &v_pos);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid 'map' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
map = map_alloc(buffer, v_pos);
|
||||
|
||||
tommy_list_insert_tail(&state->maplist, &map->node, map);
|
||||
} else if (tag[0] == 0) {
|
||||
/* allow empty lines */
|
||||
sgetspace(f);
|
||||
|
|
@ -802,6 +942,11 @@ void state_read(struct snapraid_state* state)
|
|||
|
||||
sclose(f);
|
||||
|
||||
/* update the mapping */
|
||||
state_map(state);
|
||||
|
||||
state_content_check(state, path);
|
||||
|
||||
if (state->verbose) {
|
||||
printf("\tfile %u\n", count_file);
|
||||
printf("\tblock %u\n", count_block);
|
||||
|
|
@ -848,6 +993,16 @@ static void state_write_one(struct snapraid_state* state, const char* path, unsi
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* for each map */
|
||||
for(i=state->maplist;i!=0;i=i->next) {
|
||||
struct snapraid_map* map = i->data;
|
||||
ret = fprintf(f, "map %s %u\n", map->name, map->position);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing the content file '%s' in fprintf(). %s.\n", tmp, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* for each disk */
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
tommy_node* j;
|
||||
|
|
|
|||
1
state.h
1
state.h
|
|
@ -46,6 +46,7 @@ struct snapraid_state {
|
|||
unsigned hash; /**< Hash kind used. */
|
||||
tommy_list contentlist; /**< List of content files. */
|
||||
tommy_list disklist; /**< List of all the disks. */
|
||||
tommy_list maplist; /**< List of all the disk mappings. */
|
||||
tommy_list filterlist; /**< List of inclusion/exclusion. */
|
||||
time_t progress_start; /**< Start of processing for progress visualization. */
|
||||
time_t progress_last; /**< Last update of progress visualization. */
|
||||
|
|
|
|||
22
sync.c
22
sync.c
|
|
@ -65,7 +65,9 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa
|
|||
/* for each disk */
|
||||
one_invalid = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
struct snapraid_block* block = 0;
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (block && !block_flag_has(block, BLOCK_HAS_HASH | BLOCK_HAS_PARITY)) {
|
||||
one_invalid = 1;
|
||||
break;
|
||||
|
|
@ -90,7 +92,9 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa
|
|||
/* for each disk */
|
||||
one_invalid = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
struct snapraid_block* block = 0;
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (block && !block_flag_has(block, BLOCK_HAS_HASH | BLOCK_HAS_PARITY)) {
|
||||
one_invalid = 1;
|
||||
break;
|
||||
|
|
@ -110,6 +114,14 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa
|
|||
unsigned char hash[HASH_SIZE];
|
||||
struct snapraid_block* block;
|
||||
|
||||
/* if the disk position is not used */
|
||||
if (!handle[j].disk) {
|
||||
/* use an empty block */
|
||||
memset(buffer[j], 0, state->block_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if the disk block is not used */
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block) {
|
||||
/* use an empty block */
|
||||
|
|
@ -233,9 +245,9 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa
|
|||
|
||||
/* for each disk, mark the blocks as processed */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
struct snapraid_block* block = 0;
|
||||
if (handle[j].disk)
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
9
test-raid6-hole.conf
Normal file
9
test-raid6-hole.conf
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
block_size 1
|
||||
parity test/parity
|
||||
q-parity test/q-parity
|
||||
content test/content
|
||||
content test/p-content
|
||||
content test/q-content
|
||||
disk d1 test/disk1/
|
||||
disk d3 test/disk3/
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue