mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Saves and restores also empty directories.
This commit is contained in:
parent
bf0ab70a00
commit
a0a213664b
12 changed files with 471 additions and 25 deletions
4
HISTORY
4
HISTORY
|
|
@ -1,6 +1,10 @@
|
|||
SnapRAID HISTORY
|
||||
================
|
||||
|
||||
2.2 2013/2
|
||||
==========
|
||||
* Syncs and fixes also empty directories.
|
||||
|
||||
2.1 2013/1
|
||||
==========
|
||||
* Checks for wrong empty fields in the configuration file.
|
||||
|
|
|
|||
|
|
@ -105,8 +105,13 @@ check-local:
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -a check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf -a check
|
||||
echo --- Create some more files, sync raid5 and check
|
||||
echo --- Create some more files and empty directories, sync raid5 and check
|
||||
$(TESTENV) ./mktest$(EXEEXT) generate 2 3 $(CHECKCOUNT) $(CHECKSIZE)
|
||||
mkdir test/disk1/empty1
|
||||
mkdir test/disk2/empty2
|
||||
mkdir test/disk3/empty3a
|
||||
mkdir test/disk3/empty3a/emptyinner
|
||||
mkdir test/disk3/empty3b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf diff
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf dup
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf sync
|
||||
|
|
|
|||
17
TODO
17
TODO
|
|
@ -5,9 +5,10 @@ This is the list of TODO items for SnapRAID.
|
|||
|
||||
- v2.2
|
||||
|
||||
* Allow to specify more than one disk directories to cover the case of multi partitions.
|
||||
|
||||
* Keep track of empty directories and restore them.
|
||||
* Implement an "undelete" command to recover missing files, without the
|
||||
integrity check of the existing ones.
|
||||
It should restore only missing files, without recovering modified ones
|
||||
or check validity of data of existing ones.
|
||||
|
||||
* If a content file is missing, sync should rewrite it, even if no change was done at the content.
|
||||
|
||||
|
|
@ -18,17 +19,13 @@ This will also help when running with the parity filesystem unmounted.
|
|||
|
||||
* Filtered out files should be opened in read-only mode.
|
||||
|
||||
* Implement an "undelete" command to recover missing files, without the
|
||||
integrity check of the existing ones.
|
||||
It should restore only missing files, without recovering modified ones
|
||||
or check validity of data of existing ones.
|
||||
|
||||
* Adds a log option, to save log files with the date in the name. This could
|
||||
help to keep track of what happens during automated operations.
|
||||
We must keep the log to stdout/err, and only save a copy to the log file.
|
||||
|
||||
* In fix an existing symlink with the same name of a file to be recovered may stop
|
||||
the process making the create() operation to fail.
|
||||
The same for directories, when recreating the directory tree.
|
||||
|
||||
* If a directory exists with the same name of a parity/content file be more explicative
|
||||
on the error message. See: https://sourceforge.net/projects/snapraid/forums/forum/1677233/topic/4861034
|
||||
|
|
@ -105,6 +102,10 @@ Rejected TODO
|
|||
|
||||
This is a list of rejected TODO items.
|
||||
|
||||
* Allow to specify more than one disk directories to cover the case of multi partitions.
|
||||
- Different partitions have duplicate inode. The only way to support this is to
|
||||
add also a kind of device_id, increasing the memory required.
|
||||
|
||||
* Recognizes that a file is moved from one disk to another, and if the parity
|
||||
data doesn't overlap, do not recompute it.
|
||||
- It's going to work only in RAID5 mode and only in special cases.
|
||||
|
|
|
|||
72
check.c
72
check.c
|
|
@ -1118,6 +1118,78 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
|
|||
}
|
||||
}
|
||||
|
||||
/* check all dirs */
|
||||
for(i=0;i<diskmax;++i) {
|
||||
tommy_node* node;
|
||||
struct snapraid_disk* disk;
|
||||
|
||||
if (!handle[i].disk)
|
||||
continue;
|
||||
|
||||
/* for each dir in the disk */
|
||||
disk = handle[i].disk;
|
||||
node = disk->dirlist;
|
||||
while (node) {
|
||||
char path[PATH_MAX];
|
||||
struct stat st;
|
||||
struct snapraid_dir* dir;
|
||||
int failed = 0;
|
||||
|
||||
dir = node->data;
|
||||
node = node->next; /* next node */
|
||||
|
||||
/* if excluded continue to the next one */
|
||||
if (dir_flag_has(dir, FILE_IS_EXCLUDED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stat the dir */
|
||||
pathprint(path, sizeof(path), "%s%s", disk->dir, dir->sub);
|
||||
ret = stat(path, &st);
|
||||
if (ret == -1) {
|
||||
failed = 1;
|
||||
|
||||
fprintf(stderr, "Error stating dir '%s'. %s.\n", path, strerror(errno));
|
||||
fprintf(stderr, "dir_error:%s:%s: Dir stat error\n", disk->name, dir->sub);
|
||||
++error;
|
||||
} else if (!S_ISDIR(st.st_mode)) {
|
||||
failed = 1;
|
||||
|
||||
fprintf(stderr, "dir_error:%s:%s: Dir error for not directory\n", disk->name, dir->sub);
|
||||
++error;
|
||||
}
|
||||
|
||||
if (fix && failed) {
|
||||
/* create the ancestor directories */
|
||||
ret = handle_ancestor(path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
printf("Stopping\n");
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* create it */
|
||||
ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error creating dir '%s'. %s.\n", path, strerror(errno));
|
||||
if (errno == EACCES) {
|
||||
fprintf(stderr, "WARNING! Please give write permission to the dir.\n");
|
||||
} else {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
}
|
||||
printf("Stopping\n");
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "dir_fixed:%s:%s: Fixed dir error\n", disk->name, dir->sub);
|
||||
++recovered_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state_progress_end(state, countpos, countmax, countsize);
|
||||
|
||||
bail:
|
||||
|
|
|
|||
27
elem.c
27
elem.c
|
|
@ -373,6 +373,29 @@ int link_name_compare(const void* void_arg, const void* void_data)
|
|||
return strcmp(arg, link->sub);
|
||||
}
|
||||
|
||||
struct snapraid_dir* dir_alloc(const char* sub)
|
||||
{
|
||||
struct snapraid_dir* dir;
|
||||
|
||||
dir = malloc_nofail(sizeof(struct snapraid_dir));
|
||||
pathcpy(dir->sub, sizeof(dir->sub), sub);
|
||||
dir->flag = 0;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
void dir_free(struct snapraid_dir* dir)
|
||||
{
|
||||
free(dir);
|
||||
}
|
||||
|
||||
int dir_name_compare(const void* void_arg, const void* void_data)
|
||||
{
|
||||
const char* arg = void_arg;
|
||||
const struct snapraid_dir* dir = void_data;
|
||||
return strcmp(arg, dir->sub);
|
||||
}
|
||||
|
||||
struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev)
|
||||
{
|
||||
struct snapraid_disk* disk;
|
||||
|
|
@ -391,6 +414,8 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev
|
|||
tommy_hashdyn_init(&disk->pathset);
|
||||
tommy_list_init(&disk->linklist);
|
||||
tommy_hashdyn_init(&disk->linkset);
|
||||
tommy_list_init(&disk->dirlist);
|
||||
tommy_hashdyn_init(&disk->dirset);
|
||||
tommy_array_init(&disk->blockarr);
|
||||
|
||||
return disk;
|
||||
|
|
@ -403,6 +428,8 @@ void disk_free(struct snapraid_disk* disk)
|
|||
tommy_hashdyn_done(&disk->pathset);
|
||||
tommy_list_foreach(&disk->linklist, (tommy_foreach_func*)link_free);
|
||||
tommy_hashdyn_done(&disk->linkset);
|
||||
tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free);
|
||||
tommy_hashdyn_done(&disk->dirset);
|
||||
tommy_array_done(&disk->blockarr);
|
||||
free(disk);
|
||||
}
|
||||
|
|
|
|||
53
elem.h
53
elem.h
|
|
@ -175,6 +175,19 @@ struct snapraid_link {
|
|||
tommy_hashdyn_node nodeset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dir.
|
||||
*/
|
||||
struct snapraid_dir {
|
||||
unsigned flag; /**< FILE_IS_* flags. */
|
||||
char sub[PATH_MAX]; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
|
||||
|
||||
/* nodes for data structures */
|
||||
tommy_node nodelist;
|
||||
tommy_hashdyn_node nodeset;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disk.
|
||||
*/
|
||||
|
|
@ -188,6 +201,8 @@ struct snapraid_disk {
|
|||
tommy_hashdyn pathset; /**< Hashtable by path of all the files. */
|
||||
tommy_list linklist; /**< List of all the links. */
|
||||
tommy_hashdyn linkset; /**< Hashtable by name of all the links. */
|
||||
tommy_list dirlist; /**< List of all the dirs. */
|
||||
tommy_hashdyn dirset; /**< Hashtable by name of all the dirs. */
|
||||
tommy_array blockarr; /**< Block array of the disk. */
|
||||
|
||||
/* nodes for data structures */
|
||||
|
|
@ -423,6 +438,44 @@ static inline tommy_uint32_t link_name_hash(const char* name)
|
|||
return tommy_hash_u32(0, name, strlen(name));
|
||||
}
|
||||
|
||||
static inline int dir_flag_has(struct snapraid_dir* dir, unsigned mask)
|
||||
{
|
||||
return (dir->flag & mask) == mask;
|
||||
}
|
||||
|
||||
static inline void dir_flag_set(struct snapraid_dir* dir, unsigned mask)
|
||||
{
|
||||
dir->flag |= mask;
|
||||
}
|
||||
|
||||
static inline void dir_flag_clear(struct snapraid_dir* dir, unsigned mask)
|
||||
{
|
||||
dir->flag &= ~mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a dir.
|
||||
*/
|
||||
struct snapraid_dir* dir_alloc(const char* name);
|
||||
|
||||
/**
|
||||
* Deallocates a dir.
|
||||
*/
|
||||
void dir_free(struct snapraid_dir* dir);
|
||||
|
||||
/**
|
||||
* Compare a dir with a name.
|
||||
*/
|
||||
int dir_name_compare(const void* void_arg, const void* void_data);
|
||||
|
||||
/**
|
||||
* Computes the hash of a dir name.
|
||||
*/
|
||||
static inline tommy_uint32_t dir_name_hash(const char* name)
|
||||
{
|
||||
return tommy_hash_u32(0, name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a disk.
|
||||
*/
|
||||
|
|
|
|||
144
scan.c
144
scan.c
|
|
@ -33,6 +33,7 @@ struct snapraid_scan {
|
|||
|
||||
tommy_list file_insert_list; /**< Files to insert. */
|
||||
tommy_list link_insert_list; /**< Links to insert. */
|
||||
tommy_list dir_insert_list; /**< Dirs to insert. */
|
||||
|
||||
/* nodes for data structures */
|
||||
tommy_node node;
|
||||
|
|
@ -434,10 +435,95 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state,
|
|||
}
|
||||
|
||||
/**
|
||||
* Processes a directory.
|
||||
* Removes the specified dir from the data set.
|
||||
*/
|
||||
static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* dir, const char* sub)
|
||||
static void scan_emptydir_remove(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_dir* dir)
|
||||
{
|
||||
/* state changed */
|
||||
state->need_write = 1;
|
||||
|
||||
/* remove the file from the dir containers */
|
||||
tommy_hashdyn_remove_existing(&disk->dirset, &dir->nodeset);
|
||||
tommy_list_remove_existing(&disk->dirlist, &dir->nodelist);
|
||||
|
||||
/* deallocate */
|
||||
dir_free(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the specified dir in the data set.
|
||||
*/
|
||||
static void scan_emptydir_insert(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_dir* dir)
|
||||
{
|
||||
/* state changed */
|
||||
state->need_write = 1;
|
||||
|
||||
/* insert the dir in the dir containers */
|
||||
tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
|
||||
tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a dir.
|
||||
*/
|
||||
static void scan_emptydir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* sub)
|
||||
{
|
||||
struct snapraid_dir* dir;
|
||||
|
||||
/* check if the dir already exists */
|
||||
dir = tommy_hashdyn_search(&disk->dirset, dir_name_compare, sub, dir_name_hash(sub));
|
||||
if (dir) {
|
||||
/* check if multiple files have the same name */
|
||||
if (dir_flag_has(dir, FILE_IS_PRESENT)) {
|
||||
fprintf(stderr, "Internal inconsistency for symdir '%s%s'\n", disk->dir, sub);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* mark as present */
|
||||
dir_flag_set(dir, FILE_IS_PRESENT);
|
||||
|
||||
/* it's equal */
|
||||
++scan->count_equal;
|
||||
|
||||
if (state->gui) {
|
||||
fprintf(stderr, "scan:equal:%s:%s\n", disk->name, dir->sub);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/* nothing more to do */
|
||||
return;
|
||||
} else {
|
||||
/* create the new dir */
|
||||
++scan->count_insert;
|
||||
|
||||
if (state->gui) {
|
||||
fprintf(stderr, "scan:add:%s:%s\n", disk->name, sub);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (output) {
|
||||
printf("Add '%s%s'\n", disk->dir, sub);
|
||||
}
|
||||
|
||||
/* and continue to insert it */
|
||||
}
|
||||
|
||||
/* insert it */
|
||||
dir = dir_alloc(sub);
|
||||
|
||||
/* mark it as present */
|
||||
dir_flag_set(dir, FILE_IS_PRESENT);
|
||||
|
||||
/* insert it in the delayed insert list */
|
||||
tommy_list_insert_tail(&scan->dir_insert_list, &dir->nodelist, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a directory.
|
||||
* Return != 0 if at least one file or link is processed.
|
||||
*/
|
||||
static int scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* dir, const char* sub)
|
||||
{
|
||||
int processed = 0;
|
||||
DIR* d;
|
||||
|
||||
d = opendir(dir);
|
||||
|
|
@ -525,6 +611,7 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i
|
|||
#endif
|
||||
|
||||
scan_file(scan, state, output, disk, sub_next, &st);
|
||||
processed = 1;
|
||||
} else {
|
||||
if (state->verbose) {
|
||||
printf("Excluding file '%s'\n", path_next);
|
||||
|
|
@ -549,6 +636,7 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i
|
|||
subnew[ret] = 0;
|
||||
|
||||
scan_link(scan, state, output, disk, sub_next, subnew);
|
||||
processed = 1;
|
||||
} else {
|
||||
if (state->verbose) {
|
||||
printf("Excluding link '%s'\n", path_next);
|
||||
|
|
@ -556,9 +644,18 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i
|
|||
}
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
if (filter_dir(&state->filterlist, disk->name, sub_next) == 0) {
|
||||
char sub_dir[PATH_MAX];
|
||||
|
||||
/* recurse */
|
||||
pathslash(path_next, sizeof(path_next));
|
||||
pathslash(sub_next, sizeof(sub_next));
|
||||
scan_dir(scan, state, output, disk, path_next, sub_next);
|
||||
pathcpy(sub_dir, sizeof(sub_dir), sub_next);
|
||||
pathslash(sub_dir, sizeof(sub_dir));
|
||||
if (scan_dir(scan, state, output, disk, path_next, sub_dir) == 0) {
|
||||
/* scan the directory as empty dir */
|
||||
scan_emptydir(scan, state, output, disk, sub_next);
|
||||
}
|
||||
/* or we processed something internally, or we have added the empty dir */
|
||||
processed = 1;
|
||||
} else {
|
||||
if (state->verbose) {
|
||||
printf("Excluding directory '%s'\n", path_next);
|
||||
|
|
@ -579,6 +676,8 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i
|
|||
fprintf(stderr, "Error closing directory '%s'. %s.\n", dir, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
void state_scan(struct snapraid_state* state, int output)
|
||||
|
|
@ -602,6 +701,7 @@ void state_scan(struct snapraid_state* state, int output)
|
|||
scan->count_insert = 0;
|
||||
tommy_list_init(&scan->file_insert_list);
|
||||
tommy_list_init(&scan->link_insert_list);
|
||||
tommy_list_init(&scan->dir_insert_list);
|
||||
|
||||
tommy_list_insert_tail(&scanlist, &scan->node, scan);
|
||||
|
||||
|
|
@ -657,6 +757,30 @@ void state_scan(struct snapraid_state* state, int output)
|
|||
}
|
||||
}
|
||||
|
||||
/* check for removed dirs */
|
||||
node = disk->dirlist;
|
||||
while (node) {
|
||||
struct snapraid_dir* dir = node->data;
|
||||
|
||||
/* next node */
|
||||
node = node->next;
|
||||
|
||||
/* remove if not present */
|
||||
if (!dir_flag_has(dir, FILE_IS_PRESENT)) {
|
||||
++scan->count_remove;
|
||||
|
||||
if (state->gui) {
|
||||
fprintf(stderr, "scan:remove:%s:%s\n", disk->name, dir->sub);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (output) {
|
||||
printf("Remove '%s%s'\n", disk->dir, dir->sub);
|
||||
}
|
||||
|
||||
scan_emptydir_remove(state, disk, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* insert all the new files, we insert them only after the deletion */
|
||||
/* to reuse the just freed space */
|
||||
node = scan->file_insert_list;
|
||||
|
|
@ -681,6 +805,18 @@ void state_scan(struct snapraid_state* state, int output)
|
|||
/* insert it */
|
||||
scan_link_insert(state, disk, link);
|
||||
}
|
||||
|
||||
/* insert all the new dirs */
|
||||
node = scan->dir_insert_list;
|
||||
while (node) {
|
||||
struct snapraid_dir* dir = node->data;
|
||||
|
||||
/* next node */
|
||||
node = node->next;
|
||||
|
||||
/* insert it */
|
||||
scan_emptydir_insert(state, disk, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* checks for disks where all the previously existing files where removed */
|
||||
|
|
|
|||
20
snapraid.1
20
snapraid.1
|
|
@ -114,7 +114,7 @@ With a Backup you are able to recover from a complete
|
|||
failure of the whole disk array.
|
||||
.IP \(bu
|
||||
Only file data, time and symlinks are saved. Permissions,
|
||||
extended attributes and hard\(hylinks are not saved.
|
||||
ownership, extended attributes and hard\(hylinks are not saved.
|
||||
.PD
|
||||
.SH GETTING STARTED
|
||||
To use SnapRAID you need to first select one disk of your disk array
|
||||
|
|
@ -904,14 +904,15 @@ This file is read and written by the \(a"sync\(a" command, and only read by
|
|||
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".
|
||||
Defines the checksum kind used. It can be only \(a"murmur3\(a".
|
||||
From SnapRAID 2.0 the old \(a"md5\(a" checksum is not supported anymore.
|
||||
.SS map NAME INDEX
|
||||
Defines the position INDEX of the disk NAME in the parity computation.
|
||||
.SS sign SIGN
|
||||
Signature checksum of the content file to ensure that it doesn\(cqt get
|
||||
corrupted. If you want to modify the content file manually, you have
|
||||
to remove this line to avoid this check.
|
||||
.SS file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH
|
||||
.SS file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH
|
||||
Defines a file in the specified DISK.
|
||||
.PP
|
||||
The INODE number is used to identify the file in the \(a"sync\(a"
|
||||
|
|
@ -922,6 +923,10 @@ The SIZE and TIME information are used to identify if the file
|
|||
changed from the last \(a"sync\(a" command, and if there is the need
|
||||
to recompute the parity.
|
||||
.PP
|
||||
The [.TIME_NSEC] information is optional, and only present if
|
||||
it\(cqs possible to read a such precise time information.
|
||||
It\(cqs present only from SnapRAID 2.0.
|
||||
.PP
|
||||
The PATH information is used in the \(a"check\(a" and \(a"fix\(a" commands
|
||||
to identify the file.
|
||||
.SS blk BLOCK HASH
|
||||
|
|
@ -962,6 +967,15 @@ for which the parity is computed using the previous value.
|
|||
.PP
|
||||
This field is used only when you interrupt manually the \(a"sync\(a"
|
||||
command.
|
||||
.SS symlink DISK PATH
|
||||
Defines a symbolic link in the specified DISK.
|
||||
.SS to PATH
|
||||
Defines where the previous symlink links to.
|
||||
.SS dir DISK PATH
|
||||
Defines an empty directory in the specified DISK.
|
||||
.PP
|
||||
Directory that contains saved files, are not included, as they
|
||||
are implicitely defined in the file specifications.
|
||||
.SH PARITY
|
||||
SnapRAID stores the redundancy information of your array in the parity
|
||||
and q\(hyparity files.
|
||||
|
|
|
|||
23
snapraid.d
23
snapraid.d
|
|
@ -71,7 +71,7 @@ Limitations
|
|||
With a Backup you are able to recover from a complete
|
||||
failure of the whole disk array.
|
||||
* Only file data, time and symlinks are saved. Permissions,
|
||||
extended attributes and hard-links are not saved.
|
||||
ownership, extended attributes and hard-links are not saved.
|
||||
|
||||
Getting Started
|
||||
To use SnapRAID you need to first select one disk of your disk array
|
||||
|
|
@ -637,7 +637,8 @@ Content
|
|||
defined in the configuration file.
|
||||
|
||||
checksum CHECKSUM
|
||||
Defines the checksum kind used. It can be "md5" or "murmur3".
|
||||
Defines the checksum kind used. It can be only "murmur3".
|
||||
From SnapRAID 2.0 the old "md5" checksum is not supported anymore.
|
||||
|
||||
map NAME INDEX
|
||||
Defines the position INDEX of the disk NAME in the parity computation.
|
||||
|
|
@ -647,7 +648,7 @@ Content
|
|||
corrupted. If you want to modify the content file manually, you have
|
||||
to remove this line to avoid this check.
|
||||
|
||||
file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH
|
||||
file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH
|
||||
Defines a file in the specified DISK.
|
||||
|
||||
The INODE number is used to identify the file in the "sync"
|
||||
|
|
@ -658,6 +659,10 @@ Content
|
|||
changed from the last "sync" command, and if there is the need
|
||||
to recompute the parity.
|
||||
|
||||
The [.TIME_NSEC] information is optional, and only present if
|
||||
it's possible to read a such precise time information.
|
||||
It's present only from SnapRAID 2.0.
|
||||
|
||||
The PATH information is used in the "check" and "fix" commands
|
||||
to identify the file.
|
||||
|
||||
|
|
@ -705,6 +710,18 @@ Content
|
|||
This field is used only when you interrupt manually the "sync"
|
||||
command.
|
||||
|
||||
symlink DISK PATH
|
||||
Defines a symbolic link in the specified DISK.
|
||||
|
||||
to PATH
|
||||
Defines where the previous symlink links to.
|
||||
|
||||
dir DISK PATH
|
||||
Defines an empty directory in the specified DISK.
|
||||
|
||||
Directory that contains saved files, are not included, as they
|
||||
are implicitely defined in the file specifications.
|
||||
|
||||
Parity
|
||||
SnapRAID stores the redundancy information of your array in the parity
|
||||
and q-parity files.
|
||||
|
|
|
|||
31
snapraid.txt
31
snapraid.txt
|
|
@ -81,7 +81,7 @@ Other limitations are:
|
|||
With a Backup you are able to recover from a complete
|
||||
failure of the whole disk array.
|
||||
* Only file data, time and symlinks are saved. Permissions,
|
||||
extended attributes and hard-links are not saved.
|
||||
ownership, extended attributes and hard-links are not saved.
|
||||
|
||||
|
||||
4 GETTING STARTED
|
||||
|
|
@ -698,7 +698,8 @@ defined in the configuration file.
|
|||
10.2 checksum CHECKSUM
|
||||
----------------------
|
||||
|
||||
Defines the checksum kind used. It can be "md5" or "murmur3".
|
||||
Defines the checksum kind used. It can be only "murmur3".
|
||||
From SnapRAID 2.0 the old "md5" checksum is not supported anymore.
|
||||
|
||||
10.3 map NAME INDEX
|
||||
-------------------
|
||||
|
|
@ -712,8 +713,8 @@ Signature checksum of the content file to ensure that it doesn't get
|
|||
corrupted. If you want to modify the content file manually, you have
|
||||
to remove this line to avoid this check.
|
||||
|
||||
10.5 file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH
|
||||
-------------------------------------------------
|
||||
10.5 file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH
|
||||
---------------------------------------------------
|
||||
|
||||
Defines a file in the specified DISK.
|
||||
|
||||
|
|
@ -725,6 +726,10 @@ 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.
|
||||
|
||||
The [.TIME_NSEC] information is optional, and only present if
|
||||
it's possible to read a such precise time information.
|
||||
It's present only from SnapRAID 2.0.
|
||||
|
||||
The PATH information is used in the "check" and "fix" commands
|
||||
to identify the file.
|
||||
|
||||
|
|
@ -784,6 +789,24 @@ for which the parity is computed using the previous value.
|
|||
This field is used only when you interrupt manually the "sync"
|
||||
command.
|
||||
|
||||
10.12 symlink DISK PATH
|
||||
-----------------------
|
||||
|
||||
Defines a symbolic link in the specified DISK.
|
||||
|
||||
10.13 to PATH
|
||||
-------------
|
||||
|
||||
Defines where the previous symlink links to.
|
||||
|
||||
10.14 dir DISK PATH
|
||||
-------------------
|
||||
|
||||
Defines an empty directory in the specified DISK.
|
||||
|
||||
Directory that contains saved files, are not included, as they
|
||||
are implicitely defined in the file specifications.
|
||||
|
||||
|
||||
11 PARITY
|
||||
=========
|
||||
|
|
|
|||
94
state.c
94
state.c
|
|
@ -691,12 +691,14 @@ void state_read(struct snapraid_state* state)
|
|||
unsigned count_file;
|
||||
unsigned count_block;
|
||||
unsigned count_link;
|
||||
unsigned count_dir;
|
||||
tommy_node* node;
|
||||
oathash_t hash;
|
||||
|
||||
count_file = 0;
|
||||
count_block = 0;
|
||||
count_link = 0;
|
||||
count_dir = 0;
|
||||
hash = 0;
|
||||
|
||||
/* iterate over all the available content files and load the first one present */
|
||||
|
|
@ -1125,6 +1127,59 @@ void state_read(struct snapraid_state* state)
|
|||
|
||||
/* stat */
|
||||
++count_link;
|
||||
} else if (strcmp(tag, "dir") == 0) {
|
||||
/* symlink */
|
||||
char sub[PATH_MAX];
|
||||
tommy_node* i;
|
||||
struct snapraid_dir* dir;
|
||||
|
||||
hash = oathash8(hash, 'r');
|
||||
|
||||
ret = sgettok(f, buffer, sizeof(buffer));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hash = oathashs(hash, buffer);
|
||||
|
||||
c = sgetc(f);
|
||||
if (c != ' ') {
|
||||
fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = sgetline(f, sub, sizeof(sub));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!*sub) {
|
||||
fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hash = oathashs(hash, sub);
|
||||
|
||||
/* find the disk */
|
||||
for(i=state->disklist;i!=0;i=i->next) {
|
||||
disk = i->data;
|
||||
if (strcmp(disk->name, buffer) == 0)
|
||||
break;
|
||||
}
|
||||
if (!i) {
|
||||
fprintf(stderr, "Disk named '%s' not found in '%s' at line %u\n", buffer, path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allocate the dir */
|
||||
dir = dir_alloc(sub);
|
||||
|
||||
/* insert the dir in the dir containers */
|
||||
tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
|
||||
tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
|
||||
|
||||
/* stat */
|
||||
++count_dir;
|
||||
} else if (strcmp(tag, "checksum") == 0) {
|
||||
hash = oathash8(hash, 'c');
|
||||
|
||||
|
|
@ -1258,6 +1313,7 @@ void state_read(struct snapraid_state* state)
|
|||
printf("\tfile %u\n", count_file);
|
||||
printf("\tblock %u\n", count_block);
|
||||
printf("\tsymlink %u\n", count_link);
|
||||
printf("\temptydir %u\n", count_dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1267,6 +1323,7 @@ void state_write(struct snapraid_state* state)
|
|||
unsigned count_file;
|
||||
unsigned count_block;
|
||||
unsigned count_link;
|
||||
unsigned count_dir;
|
||||
unsigned count_content;
|
||||
tommy_node* i;
|
||||
unsigned k;
|
||||
|
|
@ -1275,6 +1332,7 @@ void state_write(struct snapraid_state* state)
|
|||
count_file = 0;
|
||||
count_block = 0;
|
||||
count_link = 0;
|
||||
count_dir = 0;
|
||||
hash = 0;
|
||||
|
||||
/* count the content files */
|
||||
|
|
@ -1466,6 +1524,26 @@ void state_write(struct snapraid_state* state)
|
|||
++count_link;
|
||||
}
|
||||
|
||||
/* for each dir */
|
||||
for(j=disk->dirlist;j!=0;j=j->next) {
|
||||
struct snapraid_dir* dir = j->data;
|
||||
|
||||
sputsl("dir ", f);
|
||||
hash = oathash8(hash, 'r');
|
||||
sputs(disk->name, f);
|
||||
hash = oathashs(hash, disk->name);
|
||||
sputc(' ', f);
|
||||
sputs(dir->sub, f);
|
||||
hash = oathashs(hash, dir->sub);
|
||||
sputeol(f);
|
||||
if (serror(f)) {
|
||||
fprintf(stderr, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
++count_dir;
|
||||
}
|
||||
|
||||
{
|
||||
block_off_t k;
|
||||
block_off_t blockmax;
|
||||
|
|
@ -1552,6 +1630,7 @@ void state_write(struct snapraid_state* state)
|
|||
printf("\tfile %u\n", count_file);
|
||||
printf("\tblock %u\n", count_block);
|
||||
printf("\tsymlink %u\n", count_link);
|
||||
printf("\temptydir %u\n", count_dir);
|
||||
}
|
||||
|
||||
state->need_write = 0; /* no write needed anymore */
|
||||
|
|
@ -1619,6 +1698,21 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
|
|||
printf("Processing symlink '%s'\n", link->sub);
|
||||
}
|
||||
}
|
||||
|
||||
/* for each dir */
|
||||
for(j=tommy_list_head(&disk->dirlist);j!=0;j=j->next) {
|
||||
struct snapraid_dir* dir = j->data;
|
||||
|
||||
if (filter_dir(filterlist_disk, disk->name, dir->sub) != 0
|
||||
|| filter_dir(filterlist_file, disk->name, dir->sub) != 0
|
||||
) {
|
||||
dir_flag_set(dir, FILE_IS_EXCLUDED);
|
||||
}
|
||||
|
||||
if (state->verbose && !dir_flag_has(dir, FILE_IS_EXCLUDED)) {
|
||||
printf("Processing dir '%s'\n", dir->sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
4
state.h
4
state.h
|
|
@ -108,8 +108,8 @@ void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
void state_dup(struct snapraid_state* state);
|
||||
|
||||
/**
|
||||
* Filter files.
|
||||
* Apply and additional filter to list of file currently loaded.
|
||||
* Filter files, symlinks and dirs.
|
||||
* Apply an additional filter to the list currently loaded.
|
||||
*/
|
||||
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue