mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 02:49:02 -04:00
Adds filter exclusion.
Some refactoring.
This commit is contained in:
parent
c2d113685e
commit
b9b8fd0bb8
24 changed files with 687 additions and 217 deletions
10
HISTORY
10
HISTORY
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
10
Makefile.am
10
Makefile.am
|
|
@ -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
11
README
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
2
check.c
2
check.c
|
|
@ -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 */
|
||||
|
|
|
|||
3
config.h
3
config.h
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
92
elem.c
|
|
@ -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
28
elem.h
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
5
handle.c
5
handle.c
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
67
scan.c
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
56
snapraid.d
56
snapraid.d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
67
snapraid.txt
67
snapraid.txt
|
|
@ -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
22
state.c
|
|
@ -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);
|
||||
|
|
|
|||
3
state.h
3
state.h
|
|
@ -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
2
sync.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
26
tommylist.h
26
tommylist.h
|
|
@ -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
|
||||
|
||||
|
|
|
|||
11
tommytypes.h
11
tommytypes.h
|
|
@ -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
192
util.c
|
|
@ -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
177
util.h
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue