Adds filter exclusion.

Some refactoring.
This commit is contained in:
Andrea Mazzoleni 2011-03-16 23:52:10 +01:00
parent c2d113685e
commit b9b8fd0bb8
24 changed files with 687 additions and 217 deletions

10
HISTORY
View file

@ -1,8 +1,14 @@
SnapRAID HISTORY
================
0.2 2011/3
==========
* Exclusion list of files and directories
* More automatic tests
* Better documentation
0.1 2011/3
==========
* First public release of SnapRAID.
* First public test release of SnapRAID.

View file

@ -52,9 +52,10 @@ clean-local:
rm -rf test
maintainer-clean-local:
rm -f README AUTHORS HISTORY INSTALL
rm -f snapraid.1 snapraid.txt snapraid.html
# Automatic testing
CHECK = ./snapraid -v -c test.conf fix
check-local:
@ -90,9 +91,11 @@ check-local:
./mktest damage 1 1 500 test/disk2/b*
./snapraid -c $(srcdir)/test.conf check 2>/dev/null
./snapraid -c $(srcdir)/test.conf fix 2>/dev/null
./snapraid -c $(srcdir)/test.conf check
./snapraid -c $(srcdir)/test.conf check
echo Success!
# Valgrind testing
valgrind:
valgrind \
--tool=memcheck \
@ -130,9 +133,6 @@ cachegrind:
%.html : %.d
advd2 html < $(srcdir)/$< > $@
%.hh : %.d
advd2 frame < $(srcdir)/$< > $@
# Windows distribution
DISTWIN = \

11
README
View file

@ -12,15 +12,14 @@ a lot of big files that rarely change.
The main features of SnapRAID are:
* You can start using SnapRAID with already filled disks.
You do not have to reformat or copy your data.
* The disks of the array can have different sizes.
You have only to use the biggest one for the redundancy data.
* You can add more disks at the array at any time.
* If you accidentally delete some files in a disk, you can
recover them.
* If more than one disk fails, you lose the data only on the failed disks.
All the data in the other disks is safe.
* It doesn't lock-in your data.
You can stop using SnapRAID at any time.
* If more than one disk fails, you lose the data only on the
failed disks. All the data in the other disks is safe.
* It doesn't lock-in your data. You can stop using SnapRAID
at any time without the need to reformat or move data.
The official site of SnapRAID is:

View file

@ -292,7 +292,7 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart)
exit(EXIT_FAILURE);
}
snprintf(path, sizeof(path), "%s", state->parity);
pathcpy(path, sizeof(path), state->parity);
if (fix) {
/* if fixing, create the file and open for writing */
/* it never fails */

View file

@ -17,6 +17,9 @@
/* Define to 1 if you have the `fgetc_unlocked' function. */
#define HAVE_FGETC_UNLOCKED 1
/* Define to 1 if you have the <fnmatch.h> header file. */
#define HAVE_FNMATCH_H 1
/* Define to 1 if you have the `fsync' function. */
#define HAVE_FSYNC 1

View file

@ -18,7 +18,7 @@ AC_HEADER_ASSERT
AC_HEADER_STDC
AC_HEADER_DIRENT
AC_HEADER_TIME
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h getopt.h])
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h getopt.h fnmatch.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST

92
elem.c
View file

@ -23,6 +23,81 @@
/****************************************************************************/
/* snapraid */
struct snapraid_filter* filter_alloc(const char* pattern)
{
struct snapraid_filter* filter;
char* i;
char* first;
char* last;
filter = malloc_nofail(sizeof(struct snapraid_filter));
pathcpy(filter->pattern, sizeof(filter->pattern), pattern);
/* find first and last slash */
first = 0;
last = 0;
for(i=filter->pattern;*i;++i) {
if (*i == '/') {
if (!first)
first = i;
last = i;
}
}
if (first == 0) {
/* no slash */
filter->is_path = 0;
filter->is_dir = 0;
} else if (first == last && last[1] == 0) {
/* one slash at the end */
filter->is_path = 0;
filter->is_dir = 1;
last[0] = 0;
} else {
/* at least a slash not at the end */
filter->is_path = 1;
if (last[1] == 0) {
filter->is_dir = 1;
last[0] = 0;
} else {
filter->is_dir = 0;
}
/* a slash must be the first char, as we reject PATH/DIR/FILE */
if (filter->pattern[0] != '/') {
free(filter);
return 0;
}
}
return filter;
}
void filter_free(struct snapraid_filter* filter)
{
free(filter);
}
int filter_filter(struct snapraid_filter* filter, const char* path, const char* name, int is_dir)
{
/* matches dirs with dirs and files with files */
if (filter->is_dir && !is_dir)
return -1;
if (!filter->is_dir && is_dir)
return -1;
if (filter->is_path) {
/* skip initial slash, as always missing from the path */
if (fnmatch(filter->pattern + 1, path, FNM_PATHNAME) == 0)
return 0;
} else {
if (fnmatch(filter->pattern, name, 0) == 0)
return 0;
}
return -1;
}
block_off_t block_file_pos(struct snapraid_block* block)
{
struct snapraid_file* file = block->file;
@ -59,7 +134,7 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_
block_off_t i;
file = malloc_nofail(sizeof(struct snapraid_file));
snprintf(file->sub, sizeof(file->sub), "%s", sub);
pathcpy(file->sub, sizeof(file->sub), sub);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->mtime = mtime;
@ -94,20 +169,11 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir)
struct snapraid_disk* disk;
disk = malloc_nofail(sizeof(struct snapraid_disk));
snprintf(disk->name, sizeof(disk->name), "%s", name);
snprintf(disk->dir, sizeof(disk->dir), "%s", dir);
pathcpy(disk->name, sizeof(disk->name), name);
pathcpy(disk->dir, sizeof(disk->dir), dir);
/* ensure that the dir terminate with "/" if it isn't empty */
if (disk->dir[0] != 0) {
char last = disk->dir[strlen(disk->dir) - 1];
if (last != '/'
#ifdef _WIN32
&& last != '\\'
#endif
) {
snprintf(disk->dir, sizeof(disk->dir), "%s/", dir);
}
}
pathslash(disk->dir, sizeof(disk->dir));
disk->first_free_block = 0;
tommy_list_init(&disk->filelist);

28
elem.h
View file

@ -48,6 +48,16 @@ typedef uint32_t block_off_t;
*/
typedef uint64_t data_off_t;
/**
* Filter for paths.
*/
struct snapraid_filter {
char pattern[PATH_MAX]; /**< Filter pattern. */
int is_path; /**< If the pattern is only for the complete path. */
int is_dir; /**< If the pattern is only for dir. */
tommy_node node; /**< Next node in the list. */
};
struct snapraid_file;
/**
@ -88,6 +98,22 @@ struct snapraid_disk {
tommy_array blockarr; /**< Block array of the disk. */
};
/**
* Allocates an exclusion.
*/
struct snapraid_filter* filter_alloc(const char* pattern);
/**
* Deallocates an exclusion.
*/
void filter_free(struct snapraid_filter* filter);
/**
* Checks if a path/name matches the pattern.
* Returns 0 if it matches.
*/
int filter_filter(struct snapraid_filter* filter, const char* path, const char* name, int is_dir);
/**
* Gets the relative position of a block inside the file.
*/
@ -105,7 +131,7 @@ unsigned block_file_size(struct snapraid_block* block, unsigned block_size);
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, time_t mtime);
/**
* Deallocate a file.
* Deallocates a file.
*/
void file_free(struct snapraid_file* file);

View file

@ -18,6 +18,7 @@
#include "portable.h"
#include "elem.h"
#include "util.h"
#include "handle.h"
/****************************************************************************/
@ -42,7 +43,7 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
}
}
snprintf(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
handle->f = open(handle->path, O_RDWR | O_CREAT | O_BINARY, 0600);
if (handle->f == -1) {
@ -115,7 +116,7 @@ int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snaprai
}
}
snprintf(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
handle->f = open(handle->path, O_RDONLY | O_BINARY);
if (handle->f == -1) {

View file

@ -90,6 +90,10 @@
#include <getopt.h>
#endif
#if HAVE_FNMATCH_H
#include <fnmatch.h>
#endif
#if HAVE_GETOPT_LONG
#define SWITCH_GETOPT_LONG(a,b) a
#else

67
scan.c
View file

@ -29,7 +29,7 @@ struct snapraid_scan {
};
/**
* Remove the specified file from the data set.
* Removes the specified file from the data set.
*/
static void scan_file_remove(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
@ -55,7 +55,7 @@ static void scan_file_remove(struct snapraid_state* state, struct snapraid_disk*
}
/**
* Insert the specified file in the data set.
* Inserts the specified file in the data set.
*/
static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
{
@ -91,6 +91,9 @@ static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk*
tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
}
/**
* Processes a file.
*/
static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, struct snapraid_disk* disk, const char* path, const char* sub, const struct stat* st)
{
struct snapraid_file* file;
@ -122,6 +125,25 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state,
scan_file_insert(state, disk, file);
}
/**
* Checks if the specified path/file should be listet.
*/
static int scan_filter(struct snapraid_state* state, const char* path, const char* file, int is_dir)
{
tommy_node* node = tommy_list_head(&state->excludelist);
while (node) {
struct snapraid_filter* filter = node->data;
if (filter_filter(filter, path, file, is_dir) == 0)
return -1;
node = node->next;
}
return 0;
}
/**
* Processes a directory.
*/
static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, struct snapraid_disk* disk, const char* dir, const char* sub)
{
DIR* d;
@ -134,32 +156,43 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, s
}
while ((dd = readdir(d)) != 0) {
char path[PATH_MAX];
char path_next[PATH_MAX];
char sub_next[PATH_MAX];
const char* name = dd->d_name;
struct stat st;
snprintf(path, sizeof(path), "%s%s", dir, dd->d_name);
pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
pathprint(sub_next, sizeof(sub_next), "%s%s", sub, name);
if (stat(path, &st) != 0) {
fprintf(stderr, "Error in stat file '%s'\n", path);
if (stat(path_next, &st) != 0) {
fprintf(stderr, "Error in stat file '%s'\n", path_next);
exit(EXIT_FAILURE);
}
if (S_ISREG(st.st_mode)) {
char sub_next[PATH_MAX];
snprintf(sub_next, sizeof(sub_next), "%s%s", sub, dd->d_name);
scan_file(scan, state, disk, path, sub_next, &st);
if (scan_filter(state, sub_next, name, 0) == 0) {
scan_file(scan, state, disk, path_next, sub_next, &st);
} else {
if (state->verbose) {
printf("warning: Excluded file '/%s'\n", sub_next);
}
}
} else if (S_ISDIR(st.st_mode)) {
if (strcmp(dd->d_name, ".") != 0 && strcmp(dd->d_name, "..") != 0 && strcmp(dd->d_name, "lost+found") != 0
) {
char dir_next[PATH_MAX];
char sub_next[PATH_MAX];
snprintf(dir_next, sizeof(dir_next), "%s%s/", dir, dd->d_name);
snprintf(sub_next, sizeof(sub_next), "%s%s/", sub, dd->d_name);
scan_dir(scan, state, disk, dir_next, sub_next);
int is_auto = name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0));
if (!is_auto) {
if (scan_filter(state, sub_next, name, 1) == 0) {
pathslash(path_next, sizeof(path_next));
pathslash(sub_next, sizeof(sub_next));
scan_dir(scan, state, disk, path_next, sub_next);
} else {
if (state->verbose) {
printf("warning: Excluded directory '/%s'\n", sub_next);
}
}
}
} else {
if (state->verbose) {
printf("warning: File '%s/%s' not processed\n", dir, dd->d_name);
printf("warning: Ignored file '/%s'\n", sub_next);
}
}
}

View file

@ -103,7 +103,7 @@ int main(int argc, char* argv[])
conf = optarg;
break;
case 's' :
if (stru(optarg, &blockstart) != 0) {
if (stru32(optarg, &blockstart) != 0) {
fprintf(stderr, "Invalid start position '%s'\n", optarg);
exit(EXIT_FAILURE);
}

View file

@ -1,10 +1,5 @@
# Configuration for snapraid
# Define the block size in kilo bytes (1024 bytes).
# Format: "block_size SIZE_IN_KB"
# Default value is 128 -> 128 kilo bytes
block_size 128
# Define the file to use as parity storage
# It must me in an different disk than the data disk
# Format: "parity FILE_PATH"
@ -25,3 +20,17 @@ disk d4 /mnt/d4/
disk d5 /mnt/d5/
disk d6 /mnt/d6/
# Define files and directories to exclude
# Format: "exclude FILE"
# Format: "exclude DIR/"
# Format: "exclude /PATH/FILE"
# Format: "exclude /PATH/DIR/"
exclude *.bak
exclude tmp/
exclude /lost+found/
# Define the block size in kilo bytes (1024 bytes).
# Format: "block_size SIZE_IN_KB"
# Default value is 128 -> 128 kilo bytes
block_size 128

View file

@ -20,16 +20,14 @@ Description
The main features of SnapRAID are:
* You can start using SnapRAID with already filled disks.
You do not have to reformat or copy your data.
* The disks of the array can have different sizes.
You have only to use the biggest one for the redundancy data.
* You can add more disks at the array at any time.
* If you accidentally delete some files in a disk, you can
recover them.
* If more than one disk fails, you lose the data only on the
failed disks. All the data in the other disks is safe.
* It doesn't lock-in your data.
You can stop using SnapRAID at any time without the
need to reformat or move data.
* It doesn't lock-in your data. You can stop using SnapRAID at any
time without the need to reformat or move data.
The official site of SnapRAID is:
@ -148,6 +146,12 @@ Configuration
keep the NAME fixed.
You should use one option for each disk of the array.
=exclude GLOB_PATTERN
Defines the file or directory patterns to exclude from the sync
process. See the PATTERN section for more details in the
pattern specifications.
This option can be used many times.
=block_size SIZE_IN_KILOBYTES
Defines the basic block size in kilo bytes of
the redundancy blocks. The default is 128 and it should
@ -170,6 +174,9 @@ Configuration
:disk d1 /mnt/disk1/
:disk d2 /mnt/disk2/
:disk d3 /mnt/disk3/
:exclude *.bak
:exclude /lost+found/
:exclude tmp/
:block_size 256
Commands
@ -196,6 +203,45 @@ Commands
also tries to fix problems reverting the state of the
disk array at the previous "sync" command.
Pattern
Patterns are used to define the files and directories to exclude
from the redundancy process.
It makes sense to exclude any file not worth to be saved.
There are four different types of patterns:
=FILE
Excludes any file named as FILE. You can use any globbing
character like * and ?.
This pattern is applied only to files and not to directories.
=DIR/
Excludes any directory named DIR. You can use any globbing
character like * and ?.
This pattern is applied only to directories and not to files.
=/PATH/FILE
Excludes the exact specified file path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to files and not to directories.
=/PATH/DIR/
Excludes the exact specified directory path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to directories and not to files.
For example:
:# Excludes any file named "*.bak"
:exclude *.bak
:# Excludes the root directory "lost+found"
:exclude /lost+found/
:# Excludes any directory named "tmp"
:exclude tmp/
Options
-c, --conf CONFIG
Selects the configuration file. If not specified is assumed

View file

@ -31,11 +31,12 @@ The main features of SnapRAID are:
<ul>
<li>
You can start using SnapRAID with already filled disks.
You do not have to reformat or copy your data.
</li>
<li>
The disks of the array can have different sizes.
You have only to use the biggest one for the redundancy data.
</li>
<li>
You can add more disks at the array at any time.
</li>
<li>
If you accidentally delete some files in a disk, you can
@ -46,9 +47,8 @@ If more than one disk fails, you lose the data only on the
failed disks. All the data in the other disks is safe.
</li>
<li>
It doesn't lock-in your data.
You can stop using SnapRAID at any time without the
need to reformat or move data.
It doesn't lock-in your data. You can stop using SnapRAID at any
time without the need to reformat or move data.
</li>
</ul>
<p>
@ -219,6 +219,16 @@ You should use one option for each disk of the array.
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
exclude GLOB_PATTERN
</strong></td></tr><tr><td>
Defines the file or directory patterns to exclude from the sync
process. See the PATTERN section for more details in the
pattern specifications.
This option can be used many times.
</td></tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
block_size SIZE_IN_KILOBYTES
</strong></td></tr><tr><td>
Defines the basic block size in kilo bytes of
@ -252,6 +262,9 @@ content /mnt/diskpar/content
disk d1 /mnt/disk1/
disk d2 /mnt/disk2/
disk d3 /mnt/disk3/
exclude *.bak
exclude /lost+found/
exclude tmp/
block_size 256
</pre>
</td></tr></table>
@ -300,6 +313,66 @@ disk array at the previous "sync" command.
</td></tr>
</table>
<h2><a name="7">7</a>
Pattern
</h2>
Patterns are used to define the files and directories to exclude
from the redundancy process.
<p>
It makes sense to exclude any file not worth to be saved.
<p>
There are four different types of patterns:
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
FILE
</strong></td></tr><tr><td>
Excludes any file named as FILE. You can use any globbing
character like * and ?.
This pattern is applied only to files and not to directories.
</td></tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
DIR/
</strong></td></tr><tr><td>
Excludes any directory named DIR. You can use any globbing
character like * and ?.
This pattern is applied only to directories and not to files.
</td></tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
/PATH/FILE
</strong></td></tr><tr><td>
Excludes the exact specified file path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to files and not to directories.
</td></tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
<tr valign="top" align="left"><td><strong>
/PATH/DIR/
</strong></td></tr><tr><td>
Excludes the exact specified directory path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to directories and not to files.
</td></tr>
</table>
<p>
For example:
<p>
<table width="100%" border="0" cellspacing="0" cellpadding="2"><tr><td width="5%"></td><td width="95%">
<pre>
# Excludes any file named "*.bak"
exclude *.bak
# Excludes the root directory "lost+found"
exclude /lost+found/
# Excludes any directory named "tmp"
exclude tmp/
</pre>
</td></tr></table>
<h2><a name="8">8</a>
Options
</h2>
<table width="100%" border="0" cellspacing="0" cellpadding="2">
@ -351,11 +424,11 @@ Prints a short help screen.
Prints the program version.
</td></tr>
</table>
<h2><a name="8">8</a>
<h2><a name="9">9</a>
Copyright
</h2>
This file is Copyright (C) 2011 Andrea Mazzoleni
<h2><a name="9">9</a>
<h2><a name="10">10</a>
See Also
</h2>
rsync(1)

View file

@ -27,16 +27,14 @@ a lot of big files that rarely change.
The main features of SnapRAID are:
* You can start using SnapRAID with already filled disks.
You do not have to reformat or copy your data.
* The disks of the array can have different sizes.
You have only to use the biggest one for the redundancy data.
* You can add more disks at the array at any time.
* If you accidentally delete some files in a disk, you can
recover them.
* If more than one disk fails, you lose the data only on the
failed disks. All the data in the other disks is safe.
* It doesn't lock-in your data.
You can stop using SnapRAID at any time without the
need to reformat or move data.
* It doesn't lock-in your data. You can stop using SnapRAID at any
time without the need to reformat or move data.
The official site of SnapRAID is:
@ -164,6 +162,12 @@ it should contains the following options:
keep the NAME fixed.
You should use one option for each disk of the array.
exclude GLOB_PATTERN
Defines the file or directory patterns to exclude from the sync
process. See the PATTERN section for more details in the
pattern specifications.
This option can be used many times.
block_size SIZE_IN_KILOBYTES
Defines the basic block size in kilo bytes of
the redundancy blocks. The default is 128 and it should
@ -186,6 +190,9 @@ An example of a typical configuration is:
disk d1 /mnt/disk1/
disk d2 /mnt/disk2/
disk d3 /mnt/disk3/
exclude *.bak
exclude /lost+found/
exclude tmp/
block_size 256
@ -216,7 +223,49 @@ SnapRAID provides three simple commands that allow to:
disk array at the previous "sync" command.
7 OPTIONS
7 PATTERN
=========
Patterns are used to define the files and directories to exclude
from the redundancy process.
It makes sense to exclude any file not worth to be saved.
There are four different types of patterns:
FILE
Excludes any file named as FILE. You can use any globbing
character like * and ?.
This pattern is applied only to files and not to directories.
DIR/
Excludes any directory named DIR. You can use any globbing
character like * and ?.
This pattern is applied only to directories and not to files.
/PATH/FILE
Excludes the exact specified file path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to files and not to directories.
/PATH/DIR/
Excludes the exact specified directory path. You can use any
globbing character like * and ? but they never matches a
directory slash.
This pattern is applied only to directories and not to files.
For example:
# Excludes any file named "*.bak"
exclude *.bak
# Excludes the root directory "lost+found"
exclude /lost+found/
# Excludes any directory named "tmp"
exclude tmp/
8 OPTIONS
=========
-c, --conf CONFIG
@ -245,13 +294,13 @@ SnapRAID provides three simple commands that allow to:
Prints the program version.
8 COPYRIGHT
9 COPYRIGHT
===========
This file is Copyright (C) 2011 Andrea Mazzoleni
9 SEE ALSO
==========
10 SEE ALSO
===========
rsync(1)

22
state.c
View file

@ -33,6 +33,7 @@ void state_init(struct snapraid_state* state)
state->content[0] = 0;
state->parity[0] = 0;
tommy_array_init(&state->diskarr);
tommy_list_init(&state->excludelist);
if (sizeof(off_t) < sizeof(uint64_t)) {
fprintf(stderr, "Missing support for large files\n");
@ -43,9 +44,11 @@ void state_init(struct snapraid_state* state)
void state_done(struct snapraid_state* state)
{
unsigned i;
for(i=0;i<tommy_array_size(&state->diskarr);++i)
disk_free(tommy_array_get(&state->diskarr, i));
tommy_array_done(&state->diskarr);
tommy_list_foreach(&state->excludelist, (tommy_foreach_func*)filter_free);
}
void state_config(struct snapraid_state* state, const char* path, int verbose, int force)
@ -97,7 +100,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i
s = strtoken(s);
if (strcmp(tag, "block_size") == 0) {
ret = stru(s, &state->block_size);
ret = stru32(s, &state->block_size);
if (ret != 0) {
fprintf(stderr, "Invalid 'block_size' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -121,17 +124,24 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i
fprintf(stderr, "Multiple 'parity' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
snprintf(state->parity, sizeof(state->parity), "%s", s);
pathcpy(state->parity, sizeof(state->parity), s);
} else if (strcmp(tag, "content") == 0) {
if (*state->content) {
fprintf(stderr, "Multiple 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
snprintf(state->content, sizeof(state->content), "%s", s);
pathcpy(state->content, sizeof(state->content), s);
} else if (strcmp(tag, "disk") == 0) {
char* name = s;
s = strtoken(s);
tommy_array_insert(&state->diskarr, disk_alloc(name, s));
} else if (strcmp(tag, "exclude") == 0) {
struct snapraid_filter* filter = filter_alloc(s);
if (!filter) {
fprintf(stderr, "Invalid 'exclude' specification '%s' in '%s' at line %u\n", s, path, line);
exit(EXIT_FAILURE);
}
tommy_list_insert_tail(&state->excludelist, &filter->node, filter);
} else {
fprintf(stderr, "Unknown tag '%s' in '%s'\n", tag, path);
exit(EXIT_FAILURE);
@ -164,7 +174,7 @@ void state_read(struct snapraid_state* state)
count_file = 0;
count_block = 0;
snprintf(path, sizeof(path), "%s", state->content);
pathcpy(path, sizeof(path), state->content);
f = fopen(state->content, "r");
if (!f) {
/* if not found, assume empty */
@ -292,7 +302,7 @@ void state_read(struct snapraid_state* state)
s = strtoken(s);
hash = s;
ret = stru(pos, &v_pos);
ret = stru32(pos, &v_pos);
if (ret != 0) {
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -367,7 +377,7 @@ void state_write(struct snapraid_state* state)
printf("Saving state...\n");
snprintf(path, sizeof(path), "%s.tmp", state->content);
pathprint(path, sizeof(path), "%s.tmp", state->content);
f = fopen(path, "w");
if (!f) {
fprintf(stderr, "Error opening for writing the state file '%s'\n", path);

View file

@ -29,10 +29,11 @@ extern volatile int global_interrupt;
struct snapraid_state {
int verbose; /**< Verbose output. */
int force; /**< Forced dangerous operations. */
unsigned block_size; /**< Block size in bytes. */
uint32_t block_size; /**< Block size in bytes. */
char content[PATH_MAX]; /**< Path of the content file. */
char parity[PATH_MAX]; /**< Path of the parity file. */
tommy_array diskarr; /**< Disk array. */
tommy_list excludelist; /**< List of exclusion. */
};
/**

2
sync.c
View file

@ -155,7 +155,7 @@ void state_sync(struct snapraid_state* state, block_off_t blockstart)
exit(EXIT_FAILURE);
}
snprintf(path, sizeof(path), "%s", state->parity);
pathcpy(path, sizeof(path), state->parity);
f = parity_create(path, size);
state_sync_process(state, f, blockstart, blockmax);

View file

@ -1,7 +1,7 @@
block_size 1
parity test/parity
content test/content
disk d1 test/disk1
disk d2 test/disk2
disk d3 test/disk3
disk d1 test/disk1/
disk d2 test/disk2/
disk d3 test/disk3/

View file

@ -305,5 +305,31 @@ tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list)
return tommy_list_head(list) == 0;
}
/**
* Calls the specified function for each element in the list.
*/
tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func)
{
tommy_node* node = tommy_list_head(list);
while (node) {
void* data = node->data;
node = node->next;
func(data);
}
}
/**
* Calls the specified function with argument for each element in the list.
*/
tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg)
{
tommy_node* node = tommy_list_head(list);
while (node) {
void* data = node->data;
node = node->next;
func(arg, data);
}
}
#endif

View file

@ -203,6 +203,17 @@ typedef struct tommy_node_struct {
*/
typedef int tommy_compare_func(const void* void_a, const void* void_b);
/**
* Foreach function.
*/
typedef void tommy_foreach_func(void* void_a);
/**
* Foreach function with an argument.
* The argument is always passed as first parameter.
*/
typedef void tommy_foreach_arg_func(void* void_arg, void* void_a);
/******************************************************************************/
/* bit hacks */

192
util.c
View file

@ -19,6 +19,198 @@
#include "util.h"
/****************************************************************************/
/* string */
int strgets(char* s, unsigned size, FILE* f)
{
char* i = s;
char* send = s + size;
int c;
while (1) {
#if HAVE_FGETC_UNLOCKED
c = fgetc_unlocked(f);
#else
c = fgetc(f);
#endif
if (c == EOF || c == '\n')
break;
*i++ = c;
if (i == send) {
return -1;
}
}
if (c == EOF) {
if (
#if HAVE_FERROR_UNLOCKED
ferror_unlocked(f)
#else
ferror(f)
#endif
) {
return -1;
}
if (i == s)
return 0;
}
/* remove ending spaces */
while (i != s && isspace(i[-1]))
--i;
*i = 0;
return 1;
}
int stru32(const char* s, uint32_t* value)
{
uint32_t v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
int stru64(const char* s, uint64_t* value)
{
uint64_t v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
static char strhexset[16] = "0123456789abcdef";
void strenchex(char* str, const void* void_data, unsigned data_len)
{
const unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
unsigned char b = data[i];
*str++ = strhexset[b >> 4];
*str++ = strhexset[b & 0xF];
}
}
char* strdechex(void* void_data, unsigned data_len, char* str)
{
unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
char c0;
char c1;
unsigned char b0;
unsigned char b1;
c0 = *str;
if (c0 >= 'A' && c0 <= 'F')
b0 = c0 - 'A' + 10;
else if (c0 >= 'a' && c0 <= 'f')
b0 = c0 - 'a' + 10;
else if (c0 >= '0' && c0 <= '9')
b0 = c0 - '0';
else
return str;
++str;
c1 = *str;
if (c1 >= 'A' && c1 <= 'F')
b1 = c1 - 'A' + 10;
else if (c1 >= 'a' && c1 <= 'f')
b1 = c1 - 'a' + 10;
else if (c1 >= '0' && c1 <= '9')
b1 = c1 - '0';
else
return str;
++str;
data[i] = (b0 << 4) | b1;
}
return 0;
}
/****************************************************************************/
/* path */
void pathcpy(char* str, size_t size, const char* src)
{
size_t len = strlen(src);
if (len + 1 >= size) {
fprintf(stderr, "Path too long\n");
exit(EXIT_FAILURE);
}
memcpy(str, src, len + 1);
}
void pathprint(char* str, size_t size, const char* format, ...)
{
int len;
va_list ap;
va_start(ap, format);
len = vsnprintf(str, size, format, ap);
va_end(ap);
if (len >= size) {
fprintf(stderr, "Path too long\n");
exit(EXIT_FAILURE);
}
}
void pathslash(char* str, size_t size)
{
size_t len = strlen(str);
if (len > 0 && str[len - 1] != '/') {
if (len + 2 >= size) {
fprintf(stderr, "Path too long\n");
exit(EXIT_FAILURE);
}
str[len] = '/';
str[len+1] = 0;
}
}
/****************************************************************************/
/* mem */
void* malloc_nofail(size_t size)
{
void* ptr = malloc(size);

177
util.h
View file

@ -21,6 +21,9 @@
/****************************************************************************/
/* string */
/**
* Marks the end of the current token, and move to the next one.
*/
static inline char* strtoken(char* s)
{
while (*s && *s != ' ')
@ -32,6 +35,9 @@ static inline char* strtoken(char* s)
return s;
}
/**
* Move to the next token skipping any separator.
*/
static inline char* strskip(char* s)
{
while (*s == ' ')
@ -39,145 +45,54 @@ static inline char* strskip(char* s)
return s;
}
static inline int strgets(char* s, unsigned size, FILE* f)
{
char* i = s;
char* send = s + size;
int c;
/**
* Gets a text line from a file.
* Returns -1 on error, 0 on oef, and 1 if more data is available.
*/
int strgets(char* s, unsigned size, FILE* f);
while (1) {
#if HAVE_FGETC_UNLOCKED
c = fgetc_unlocked(f);
#else
c = fgetc(f);
#endif
/**
* Convert a string to a 32 bit unsigned.
* Returns 0 on success.
*/
int stru32(const char* s, uint32_t* value);
if (c == EOF || c == '\n')
break;
/**
* Convert a string to a 64 bit unsigned.
* Returns 0 on success.
*/
int stru64(const char* s, uint64_t* value);
*i++ = c;
/**
* Encodes to hex.
*/
void strenchex(char* str, const void* void_data, unsigned data_len);
if (i == send) {
return -1;
}
}
/**
* Decodes from hex.
*/
char* strdechex(void* void_data, unsigned data_len, char* str);
if (c == EOF) {
#if HAVE_FERROR_UNLOCKED
if (ferror_unlocked(f)) {
#else
if (ferror(f)) {
#endif
return -1;
}
if (i == s)
return 0;
}
/****************************************************************************/
/* path */
/* remove ending spaces */
while (i != s && isspace(i[-1]))
--i;
*i = 0;
/**
* Copies a path limiting the size.
* Aborts if too long.
*/
void pathcpy(char* str, size_t size, const char* src);
return 1;
}
/**
* Prints a path.
* Aborts if too long.
*/
void pathprint(char* str, size_t size, const char* format, ...);
static inline int stru(const char* s, unsigned* value)
{
unsigned v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
static inline int stru64(const char* s, uint64_t* value)
{
uint64_t v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
static char strhexset[16] = "0123456789abcdef";
static inline void strenchex(char* str, const void* void_data, unsigned data_len)
{
const unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
unsigned char b = data[i];
*str++ = strhexset[b >> 4];
*str++ = strhexset[b & 0xF];
}
}
static inline char* strdechex(void* void_data, unsigned data_len, char* str)
{
unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
char c0;
char c1;
unsigned char b0;
unsigned char b1;
c0 = *str;
if (c0 >= 'A' && c0 <= 'F')
b0 = c0 - 'A' + 10;
else if (c0 >= 'a' && c0 <= 'f')
b0 = c0 - 'a' + 10;
else if (c0 >= '0' && c0 <= '9')
b0 = c0 - '0';
else
return str;
++str;
c1 = *str;
if (c1 >= 'A' && c1 <= 'F')
b1 = c1 - 'A' + 10;
else if (c1 >= 'a' && c1 <= 'f')
b1 = c1 - 'a' + 10;
else if (c1 >= '0' && c1 <= '9')
b1 = c1 - '0';
else
return str;
++str;
data[i] = (b0 << 4) | b1;
}
return 0;
}
/**
* Ensures the presence of a terminating slash, if it isn't empty.
* Aborts if too long.
*/
void pathslash(char* str, size_t size);
/****************************************************************************/
/* memory */