mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Adds the new -m, --filter-missing options to undelete files.
This commit is contained in:
parent
187e3a0bcf
commit
acad6aad04
9 changed files with 103 additions and 37 deletions
9
HISTORY
9
HISTORY
|
|
@ -3,11 +3,16 @@ SnapRAID HISTORY
|
|||
|
||||
2.2 2013/2
|
||||
==========
|
||||
* Added pooling support with the new 'pool' command. It creates a virtual view
|
||||
of the array using symbolic links pointing at the original files.
|
||||
* Added pooling support with the new 'pool' command. It creates a virtual
|
||||
view of the array using symbolic links pointing at the original files.
|
||||
* Added a new -m, -.filter-missing options that allow to undelete files,
|
||||
without checking/fixing the others.
|
||||
* Added support also for hardlinks and empty directories.
|
||||
* Added symlinks support to Windows. Note that Windows junctions are still
|
||||
not supported.
|
||||
* Files without read permission generates now an error instead of a warning.
|
||||
You now must explicitely exclude them in the configuration file with
|
||||
exclusion rules.
|
||||
|
||||
2.1 2013/1
|
||||
==========
|
||||
|
|
|
|||
|
|
@ -273,16 +273,16 @@ endif
|
|||
mv test/_a test/disk1/_a
|
||||
mv test/_b test/disk1/_b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf sync
|
||||
echo --- Delete some files, fix and check with raid5
|
||||
echo --- Delete some files, fix with -m and check with raid5
|
||||
rm test/disk1/8*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -m fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Delete some dirs in a disk, fix and check with raid5
|
||||
echo --- Delete some dirs in a disk, fix with -m and check with raid5
|
||||
rm -r test/disk2/_a
|
||||
rm -r test/disk2/_b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -m fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Delete some dirs in two disks, fix and check with raid6 using the -d option for each disk
|
||||
rm -r test/disk2/_a
|
||||
|
|
|
|||
5
elem.c
5
elem.c
|
|
@ -230,6 +230,11 @@ int filter_dir(tommy_list* filterlist, const char* disk, const char* sub)
|
|||
return filter_element(filterlist, disk, sub, 1);
|
||||
}
|
||||
|
||||
int filter_existence(int filter_missing, unsigned flag)
|
||||
{
|
||||
return filter_missing && (flag & FILE_IS_PRESENT) == FILE_IS_PRESENT;
|
||||
}
|
||||
|
||||
int filter_content(tommy_list* contentlist, const char* path)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
|
|
|||
18
elem.h
18
elem.h
|
|
@ -133,7 +133,7 @@ struct snapraid_block {
|
|||
|
||||
#define FILE_IS_PRESENT 1 /**< If it's seen as present. */
|
||||
#define FILE_IS_EXCLUDED 2 /**< If it's an excluded file from the processing. */
|
||||
#define FILE_IS_LARGER 4 /**< If a larger file was already detected. */
|
||||
#define FILE_IS_LARGER 4 /**< If a larger file was already detected. Just to avoid to report it more times. */
|
||||
#define FILE_IS_DAMAGED 8 /**< If a fix was attempted but it failed. */
|
||||
#define FILE_IS_FIXED 16 /**< If a fix was done. */
|
||||
#define FILE_IS_HARDLINK 32 /**< If it's an hardlink. */
|
||||
|
|
@ -265,6 +265,12 @@ static inline int filter_hidden(int enable, struct dirent* dd)
|
|||
*/
|
||||
int filter_path(tommy_list* filterlist, const char* disk, const char* sub);
|
||||
|
||||
/**
|
||||
* Filter a file/link/dir if missing.
|
||||
* Return !=0 if it matches and it should be excluded.
|
||||
*/
|
||||
int filter_existence(int filter_missing, unsigned flag);
|
||||
|
||||
/**
|
||||
* Filters a dir using a list of filters.
|
||||
* For each element of the path all the filters are applied, until the first one that matches.
|
||||
|
|
@ -294,7 +300,7 @@ static inline void block_file_set(struct snapraid_block* block, struct snapraid_
|
|||
block->file_mixed = (block->file_mixed & ~(uintptr_t)BLOCK_STATE_MASK) | (uintptr_t)file;
|
||||
}
|
||||
|
||||
static inline unsigned block_state_get(struct snapraid_block* block)
|
||||
static inline unsigned block_state_get(const struct snapraid_block* block)
|
||||
{
|
||||
return block->file_mixed & BLOCK_STATE_MASK;
|
||||
}
|
||||
|
|
@ -314,7 +320,7 @@ static inline void block_clear_parity(struct snapraid_block* block)
|
|||
}
|
||||
}
|
||||
|
||||
static inline int block_has_hash(struct snapraid_block* block)
|
||||
static inline int block_has_hash(const struct snapraid_block* block)
|
||||
{
|
||||
unsigned state = block_state_get(block);
|
||||
return state == BLOCK_STATE_BLK || state == BLOCK_STATE_INV;
|
||||
|
|
@ -344,7 +350,7 @@ static inline int block_is_valid(struct snapraid_block* block)
|
|||
*/
|
||||
unsigned block_file_size(struct snapraid_block* block, unsigned block_size);
|
||||
|
||||
static inline int file_flag_has(struct snapraid_file* file, unsigned mask)
|
||||
static inline int file_flag_has(const struct snapraid_file* file, unsigned mask)
|
||||
{
|
||||
return (file->flag & mask) == mask;
|
||||
}
|
||||
|
|
@ -400,7 +406,7 @@ static inline tommy_uint32_t file_path_hash(const char* sub)
|
|||
return tommy_hash_u32(0, sub, strlen(sub));
|
||||
}
|
||||
|
||||
static inline int link_flag_has(struct snapraid_link* link, unsigned mask)
|
||||
static inline int link_flag_has(const struct snapraid_link* link, unsigned mask)
|
||||
{
|
||||
return (link->flag & mask) == mask;
|
||||
}
|
||||
|
|
@ -446,7 +452,7 @@ 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)
|
||||
static inline int dir_flag_has(const struct snapraid_dir* dir, unsigned mask)
|
||||
{
|
||||
return (dir->flag & mask) == mask;
|
||||
}
|
||||
|
|
|
|||
33
snapraid.1
33
snapraid.1
|
|
@ -6,7 +6,7 @@ snapraid [\(hyc, \(hy\(hyconf CONFIG]
|
|||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
[\(hyf, \(hy\(hyfilter PATTERN] [\(hyd, \(hy\(hyfilter\(hydisk NAME]
|
||||
[\(hyf, \(hy\(hyfilter PATTERN] [\(hyd, \(hy\(hyfilter\(hydisk NAME] [\(hym, \(hy\(hyfilter\(hymissing]
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
|
|
@ -307,10 +307,11 @@ in \(a"sync\(a".
|
|||
.PP
|
||||
In this regard SnapRAID is more like a backup program than a RAID
|
||||
system. For example, you can use it to recover from an accidentally
|
||||
deleted directory, simply running the fix command like.
|
||||
deleted directory, simply running the fix command using the
|
||||
\(hym, \(hy\(hyfilter\(hymissing filter, like:
|
||||
.PP
|
||||
.RS 4
|
||||
snapraid fix \(hyf DIR/
|
||||
snapraid fix \(hym \(hyf DIR/
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
|
|
@ -319,7 +320,16 @@ snapraid fix \(hyf DIR/
|
|||
Or to simply recover one file you can use:
|
||||
.PP
|
||||
.RS 4
|
||||
snapraid fix \(hyf FILE
|
||||
snapraid fix \(hym \(hyf FILE
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
.RE
|
||||
.PP
|
||||
Or to recover all the delete files with:
|
||||
.PP
|
||||
.RS 4
|
||||
snapraid fix \(hym
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
|
|
@ -425,13 +435,24 @@ You must specify a disk name as named in the configuration
|
|||
file.
|
||||
In \(a"check\(a", you can make it faster, specifing also \(hya, \(hy\(hyaudit\(hyonly
|
||||
option, to avoid to access other disks to check parity data.
|
||||
If you combine \(hy\(hyfilter and \(hy\(hyfilter\(hydisk options, only files
|
||||
matching both the two groups of rules are selected.
|
||||
If you combine more \(hy\(hyfilter, \(hy\(hyfilter\(hydisk and \(hy\(hyfilter\(hymissing options,
|
||||
only files matching all the set of filters are selected.
|
||||
This option can be used many times.
|
||||
This option can be used only with the \(a"check\(a" and \(a"fix\(a" commands.
|
||||
Note that it cannot be used with \(a"sync\(a", because \(a"sync\(a" always
|
||||
process the whole array.
|
||||
.TP
|
||||
.B \(hyn, \(hy\(hyfileter\(hymissing
|
||||
Filters the files to process in the \(a"check\(a" and \(a"fix\(a"
|
||||
commands.
|
||||
Only the files missing/deleted from the array are processed.
|
||||
When used with \(a"fix\(a", this is a kind of \(a"undelete\(a" command.
|
||||
If you combine more \(hy\(hyfilter, \(hy\(hyfilter\(hydisk and \(hy\(hyfilter\(hymissing options,
|
||||
only files matching all the set of filters are selected.
|
||||
This option can be used only with the \(a"check\(a" and \(a"fix\(a" commands.
|
||||
Note that it cannot be used with \(a"sync\(a", because \(a"sync\(a" always
|
||||
process the whole array.
|
||||
.TP
|
||||
.B \(hya, \(hy\(hyaudit\(hyonly
|
||||
When checking, only verify the hash of the files, without
|
||||
doing any kind of check on the redundancy data.
|
||||
|
|
|
|||
22
snapraid.c
22
snapraid.c
|
|
@ -45,10 +45,11 @@ void usage(void)
|
|||
{
|
||||
version();
|
||||
|
||||
printf("Usage: " PACKAGE " sync|diff|check|fix [options]\n");
|
||||
printf("Usage: " PACKAGE " sync|pool|diff|dup|check|fix [options]\n");
|
||||
printf("\n");
|
||||
printf("Commands:\n");
|
||||
printf(" sync Syncronize the state of the array of disks\n");
|
||||
printf(" pool Create or update the virtual view of the array of disks\n");
|
||||
printf(" diff Show the changes that needs to be syncronized\n");
|
||||
printf(" dup Find duplicate files\n");
|
||||
printf(" check Check the array of disks\n");
|
||||
|
|
@ -56,8 +57,9 @@ void usage(void)
|
|||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-c, --conf FILE ", "-c") " Configuration file (default " CONF ")\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-f, --filter PATTERN ", "-f") " Filter the files to process with a pattern\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-d, --filter-dist NAME", "-f") " Filter the files to process with a disk name\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-f, --filter PATTERN ", "-f") " Process only files matching the pattern\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-d, --filter-dist NAME", "-f") " Process only files in the disk\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-m, --filter-missing ", "-m") " Process only missing/delete files\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-a, --audit-only ", "-A") " Check only file data and not parity\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-N, --find-by-name ", "-N") " Find the files by name instead than by inode\n");
|
||||
printf(" " SWITCH_GETOPT_LONG("-Z, --force-zero ", "-Z") " Force synching of files that get zero size\n");
|
||||
|
|
@ -81,6 +83,7 @@ struct option long_options[] = {
|
|||
{ "conf", 1, 0, 'c' },
|
||||
{ "filter", 1, 0, 'f' },
|
||||
{ "filter-disk", 1, 0, 'd' },
|
||||
{ "filter-missing", 0, 0, 'm' },
|
||||
{ "start", 1, 0, 's' },
|
||||
{ "count", 1, 0, 't' },
|
||||
{ "force-zero", 0, 0, 'Z' },
|
||||
|
|
@ -103,7 +106,7 @@ struct option long_options[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#define OPTIONS "c:f:d:s:t:ZESNaTvhVG"
|
||||
#define OPTIONS "c:f:d:ms:t:ZESNaTvhVG"
|
||||
|
||||
volatile int global_interrupt = 0;
|
||||
|
||||
|
|
@ -146,6 +149,7 @@ int main(int argc, char* argv[])
|
|||
int ret;
|
||||
tommy_list filterlist_file;
|
||||
tommy_list filterlist_disk;
|
||||
int filter_missing;
|
||||
char* e;
|
||||
const char* command;
|
||||
|
||||
|
|
@ -168,6 +172,7 @@ int main(int argc, char* argv[])
|
|||
blockcount = 0;
|
||||
tommy_list_init(&filterlist_file);
|
||||
tommy_list_init(&filterlist_disk);
|
||||
filter_missing = 0;
|
||||
|
||||
opterr = 0;
|
||||
while ((c =
|
||||
|
|
@ -197,6 +202,9 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
tommy_list_insert_tail(&filterlist_disk, &filter->node, filter);
|
||||
} break;
|
||||
case 'm' :
|
||||
filter_missing = 1;
|
||||
break;
|
||||
case 's' :
|
||||
blockstart = strtoul(optarg, &e, 0);
|
||||
if (!e || *e) {
|
||||
|
|
@ -312,7 +320,7 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_DRY :
|
||||
break;
|
||||
default:
|
||||
if (!tommy_list_empty(&filterlist_file) || !tommy_list_empty(&filterlist_disk)) {
|
||||
if (!tommy_list_empty(&filterlist_file) || !tommy_list_empty(&filterlist_disk) || filter_missing) {
|
||||
fprintf(stderr, "You cannot filter with the '%s' command\n", command);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
|
@ -379,7 +387,7 @@ int main(int argc, char* argv[])
|
|||
state_read(&state);
|
||||
|
||||
/* apply the command line filter */
|
||||
state_filter(&state, &filterlist_file, &filterlist_disk);
|
||||
state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing);
|
||||
|
||||
/* intercept Ctrl+C */
|
||||
signal(SIGINT, &signal_handler);
|
||||
|
|
@ -397,7 +405,7 @@ int main(int argc, char* argv[])
|
|||
state_read(&state);
|
||||
|
||||
/* apply the command line filter */
|
||||
state_filter(&state, &filterlist_file, &filterlist_disk);
|
||||
state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing);
|
||||
|
||||
/* intercept Ctrl+C */
|
||||
signal(SIGINT, &signal_handler);
|
||||
|
|
|
|||
28
snapraid.d
28
snapraid.d
|
|
@ -3,7 +3,7 @@ Name{number}
|
|||
|
||||
Synopsis
|
||||
:snapraid [-c, --conf CONFIG]
|
||||
: [-f, --filter PATTERN] [-d, --filter-disk NAME]
|
||||
: [-f, --filter PATTERN] [-d, --filter-disk NAME] [-m, --filter-missing]
|
||||
: [-a, --audit-only]
|
||||
: [-N, --find-by-name]
|
||||
: [-Z, --force-zero] [-E, --force-empty]
|
||||
|
|
@ -176,13 +176,18 @@ Getting Started
|
|||
|
||||
In this regard SnapRAID is more like a backup program than a RAID
|
||||
system. For example, you can use it to recover from an accidentally
|
||||
deleted directory, simply running the fix command like.
|
||||
deleted directory, simply running the fix command using the
|
||||
-m, --filter-missing filter, like:
|
||||
|
||||
:snapraid fix -f DIR/
|
||||
:snapraid fix -m -f DIR/
|
||||
|
||||
Or to simply recover one file you can use:
|
||||
|
||||
:snapraid fix -f FILE
|
||||
:snapraid fix -m -f FILE
|
||||
|
||||
Or to recover all the delete files with:
|
||||
|
||||
:snapraid fix -m
|
||||
|
||||
Commands
|
||||
SnapRAID provides four simple commands that allow to:
|
||||
|
|
@ -287,13 +292,24 @@ Options
|
|||
file.
|
||||
In "check", you can make it faster, specifing also -a, --audit-only
|
||||
option, to avoid to access other disks to check parity data.
|
||||
If you combine --filter and --filter-disk options, only files
|
||||
matching both the two groups of rules are selected.
|
||||
If you combine more --filter, --filter-disk and --filter-missing options,
|
||||
only files matching all the set of filters are selected.
|
||||
This option can be used many times.
|
||||
This option can be used only with the "check" and "fix" commands.
|
||||
Note that it cannot be used with "sync", because "sync" always
|
||||
process the whole array.
|
||||
|
||||
-n, --fileter-missing
|
||||
Filters the files to process in the "check" and "fix"
|
||||
commands.
|
||||
Only the files missing/deleted from the array are processed.
|
||||
When used with "fix", this is a kind of "undelete" command.
|
||||
If you combine more --filter, --filter-disk and --filter-missing options,
|
||||
only files matching all the set of filters are selected.
|
||||
This option can be used only with the "check" and "fix" commands.
|
||||
Note that it cannot be used with "sync", because "sync" always
|
||||
process the whole array.
|
||||
|
||||
-a, --audit-only
|
||||
When checking, only verify the hash of the files, without
|
||||
doing any kind of check on the redundancy data.
|
||||
|
|
|
|||
15
state.c
15
state.c
|
|
@ -1805,12 +1805,12 @@ void state_write(struct snapraid_state* state)
|
|||
state->need_write = 0; /* no write needed anymore */
|
||||
}
|
||||
|
||||
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk)
|
||||
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
/* if no filter, include all */
|
||||
if (tommy_list_empty(filterlist_file) && tommy_list_empty(filterlist_disk))
|
||||
if (!filter_missing && tommy_list_empty(filterlist_file) && tommy_list_empty(filterlist_disk))
|
||||
return;
|
||||
|
||||
printf("Filtering...\n");
|
||||
|
|
@ -1831,6 +1831,8 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
|
|||
printf("/");
|
||||
printf("\n");
|
||||
}
|
||||
if (filter_missing)
|
||||
printf("\t<missing>\n");
|
||||
}
|
||||
|
||||
/* for each disk */
|
||||
|
|
@ -1842,7 +1844,8 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
|
|||
for(j=tommy_list_head(&disk->filelist);j!=0;j=j->next) {
|
||||
struct snapraid_file* file = j->data;
|
||||
|
||||
if (filter_path(filterlist_disk, disk->name, file->sub) != 0
|
||||
if (filter_existence(filter_missing, file->flag) != 0
|
||||
|| filter_path(filterlist_disk, disk->name, file->sub) != 0
|
||||
|| filter_path(filterlist_file, disk->name, file->sub) != 0
|
||||
) {
|
||||
file_flag_set(file, FILE_IS_EXCLUDED);
|
||||
|
|
@ -1857,7 +1860,8 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
|
|||
for(j=tommy_list_head(&disk->linklist);j!=0;j=j->next) {
|
||||
struct snapraid_link* link = j->data;
|
||||
|
||||
if (filter_path(filterlist_disk, disk->name, link->sub) != 0
|
||||
if (filter_existence(filter_missing, link->flag) != 0
|
||||
|| filter_path(filterlist_disk, disk->name, link->sub) != 0
|
||||
|| filter_path(filterlist_file, disk->name, link->sub) != 0
|
||||
) {
|
||||
link_flag_set(link, FILE_IS_EXCLUDED);
|
||||
|
|
@ -1872,7 +1876,8 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom
|
|||
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
|
||||
if (filter_existence(filter_missing, dir->flag) != 0
|
||||
|| 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);
|
||||
|
|
|
|||
2
state.h
2
state.h
|
|
@ -118,7 +118,7 @@ void state_pool(struct snapraid_state* state);
|
|||
* 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);
|
||||
void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing);
|
||||
|
||||
/**
|
||||
* Begins the progress visualization.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue