Even faster content load.

This commit is contained in:
Andrea Mazzoleni 2011-06-23 20:28:52 +02:00
parent 7f400289b8
commit b21b3076f6
4 changed files with 486 additions and 261 deletions

View file

@ -114,6 +114,7 @@ int main(int argc, char* argv[])
block_off_t blockcount;
int ret;
tommy_list filterlist;
char* e;
/* defaults */
conf = CONF;
@ -149,13 +150,15 @@ int main(int argc, char* argv[])
tommy_list_insert_tail(&filterlist, &filter->node, filter);
} break;
case 's' :
if (stru32(optarg, &blockstart) != 0) {
blockstart = strtoul(optarg, &e, 0);
if (!e || *e) {
fprintf(stderr, "Invalid start position '%s'\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 't' :
if (stru32(optarg, &blockcount) != 0) {
blockcount = strtoul(optarg, &e, 0);
if (!e || *e) {
fprintf(stderr, "Invalid count number '%s'\n", optarg);
exit(EXIT_FAILURE);
}

364
state.c
View file

@ -21,11 +21,6 @@
#include "state.h"
#include "util.h"
/**
* Max length of a line in the configuration and content files.
*/
#define TEXT_LINE_MAX 1024
void state_init(struct snapraid_state* state)
{
state->verbose = 0;
@ -87,49 +82,29 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i
exit(EXIT_FAILURE);
}
line = 0;
line = 1;
content_count = 0;
while (1) {
char buffer[TEXT_LINE_MAX];
char* tag;
char* s;
char tag[PATH_MAX];
char buffer[PATH_MAX];
int ret;
int c;
++line;
/* skip initial spaces */
strgetspace(f);
ret = strgets(buffer, TEXT_LINE_MAX, f);
if (ret < 0) {
/* read the command */
ret = strgettoken(f, tag, sizeof(tag));
if (ret != 0) {
fprintf(stderr, "Error reading the configuration file '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
if (ret == 0)
break;
/* start */
s = buffer;
/* here we use strtoken_trim() to eat extra spaces inserted by the user */
/* skip ending spaces */
--ret;
while (ret>0 && isspace(s[ret-1])) {
--ret;
s[ret] = 0;
}
/* skip initial spaces */
while (*s && isspace(*s)) {
++s;
}
/* ignore comments and empty lines */
if (*s == '#' || *s == 0)
continue;
tag = s;
s = strtoken_trim(s);
/* skip spaces after the command */
strgetspace(f);
if (strcmp(tag, "block_size") == 0) {
ret = stru32(s, &state->block_size);
ret = strgetu32(f, &state->block_size);
if (ret != 0) {
fprintf(stderr, "Invalid 'block_size' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -153,40 +128,120 @@ 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);
}
pathcpy(state->parity, sizeof(state->parity), s);
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'parity' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
pathcpy(state->parity, sizeof(state->parity), buffer);
} else if (strcmp(tag, "q-parity") == 0) {
if (*state->qarity) {
fprintf(stderr, "Multiple 'q-parity' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
pathcpy(state->qarity, sizeof(state->qarity), s);
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'q-parity' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
pathcpy(state->qarity, sizeof(state->qarity), buffer);
state->level = 2;
} else if (strcmp(tag, "content") == 0) {
struct snapraid_content* content = content_alloc(s);
struct snapraid_content* content;
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'content' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
content = content_alloc(buffer);
tommy_list_insert_tail(&state->contentlist, &content->node, content);
++content_count;
} else if (strcmp(tag, "disk") == 0) {
char* name = s;
s = strtoken_trim(s);
tommy_array_insert(&state->diskarr, disk_alloc(name, s));
char dir[PATH_MAX];
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'disk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
strgetspace(f);
ret = strgettoken(f, dir, sizeof(dir));
if (ret != 0) {
fprintf(stderr, "Invalid 'disk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
tommy_array_insert(&state->diskarr, disk_alloc(buffer, dir));
} else if (strcmp(tag, "exclude") == 0) {
struct snapraid_filter* filter = filter_alloc(-1, s);
struct snapraid_filter* filter;
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'exclude' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
filter = filter_alloc(-1, buffer);
if (!filter) {
fprintf(stderr, "Invalid 'exclude' specification '%s' in '%s' at line %u\n", s, path, line);
fprintf(stderr, "Invalid 'exclude' specification '%s' in '%s' at line %u\n", buffer, path, line);
exit(EXIT_FAILURE);
}
tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
} else if (strcmp(tag, "include") == 0) {
struct snapraid_filter* filter = filter_alloc(1, s);
struct snapraid_filter* filter;
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'include' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
filter = filter_alloc(1, buffer);
if (!filter) {
fprintf(stderr, "Invalid 'include' specification '%s' in '%s' at line %u\n", s, path, line);
fprintf(stderr, "Invalid 'include' specification '%s' in '%s' at line %u\n", buffer, path, line);
exit(EXIT_FAILURE);
}
tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
} else if (tag[0] == 0) {
/* allow empty lines */
} else if (tag[0] == '#') {
ret = strgetline(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid comment in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
} else {
fprintf(stderr, "Unknown tag '%s' in '%s'\n", tag, path);
fprintf(stderr, "Unknown tag '%s' in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
}
/* skip final spaces */
strgetspace(f);
/* next line */
c = strgetcnl(f);
if (c == EOF) {
break;
}
if (c != '\n') {
fprintf(stderr, "Extra char '%c' after command specification in '%s' at line %u\n", (char)c, path, line);
exit(EXIT_FAILURE);
}
++line;
}
if (ferror(f)) {
fprintf(stderr, "Error reading the configuration file '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
fclose(f);
@ -282,53 +337,71 @@ void state_read(struct snapraid_state* state)
disk = 0;
file = 0;
line = 0;
line = 1;
blockidx = 0;
while (1) {
char buffer[TEXT_LINE_MAX];
char* tag;
char* s;
char buffer[PATH_MAX];
int ret;
int cmd;
int c;
++line;
ret = strgets(buffer, TEXT_LINE_MAX, f);
if (ret < 0) {
fprintf(stderr, "Error reading the content file '%s' at line %u\n", path, line);
/* decode the command */
cmd = 0;
c = strgetc(f);
if (c == 'b') {
if (strgetc(f) == 'l' && strgetc(f) == 'k') {
c = strgetc(f);
if (c == ' ') {
cmd = 'b';
} else if (c == 's' && strgetc(f) == 'i' && strgetc(f) == 'z' && strgetc(f) == 'e' && strgetc(f) == ' ') {
cmd = 's';
}
}
} else if (c == 'i') {
if (strgetc(f) == 'n' && strgetc(f) == 'v' && strgetc(f) == ' ') {
cmd = 'i';
}
} else if (c == 'f') {
if (strgetc(f) == 'i' && strgetc(f) == 'l' && strgetc(f) == 'e' && strgetc(f) == ' ') {
cmd = 'f';
}
} else if (c == 'c') {
if (strgetc(f) == 'h' && strgetc(f) == 'e' && strgetc(f) == 'c' && strgetc(f) == 'k'
&& strgetc(f) == 's' && strgetc(f) == 'u' && strgetc(f) == 'm' && strgetc(f) == ' ') {
cmd = 'c';
}
} else if (c == '\n') {
} else if (c == '\r') {
c = strgetc(f);
if (c != '\n') {
fprintf(stderr, "Unexpected carrige return in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
} else if (c == '#') {
int ret = strgetline(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid comment in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
} else if (c == EOF) {
break;
} else {
fprintf(stderr, "Unknown char '%c' in '%s' at line %u\n", (char)c, path, line);
exit(EXIT_FAILURE);
}
if (ret == 0)
break;
/* start */
s = buffer;
/* here we use strtoken() to allow filenames starting and ending with spaces */
/* ignore comments and empty lines */
if (*s == '#' || *s == 0)
continue;
tag = s;
s = strtoken(s);
if (strcmp(tag, "blk") == 0 || strcmp(tag, "inv") == 0) {
char* pos;
char* hash;
if (cmd == 'b' || cmd == 'i') {
/* "blk"/"inv" command */
block_off_t v_pos;
struct snapraid_block* block;
char* e;
if (!file) {
fprintf(stderr, "Unexpected 'blk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
pos = s;
s = strtoken(s);
hash = s;
ret = stru32(pos, &v_pos);
ret = strgetu32(f, &v_pos);
if (ret != 0) {
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -348,10 +421,20 @@ void state_read(struct snapraid_state* state)
block->parity_pos = v_pos;
/* set the hash only if present */
if (*hash != 0) {
e = strdechex(block->hash, HASH_SIZE, hash);
if (e) {
/* check if we are at the end of the line */
c = strgetcnl(f);
if (c == '\n') {
/* no hash present */
ungetc(c, f);
} else {
if (c != ' ') {
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
/* set the hash only if present */
ret = strgethex(f, block->hash, HASH_SIZE);
if (ret != 0) {
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
@ -360,7 +443,7 @@ void state_read(struct snapraid_state* state)
}
/* set the parity only if present */
if (tag[0] == 'b') /* blk */
if (cmd == 'b')
block_flag_set(block, BLOCK_HAS_PARITY);
/* parity implies hash */
@ -382,12 +465,10 @@ void state_read(struct snapraid_state* state)
/* stat */
++count_block;
} else if (strcmp(tag, "file") == 0) {
char* name;
char* size;
char* mtime;
char* inode;
char* sub;
} else if (cmd == 'f') {
/* file */
char buffer[PATH_MAX];
char sub[PATH_MAX];
uint64_t v_size;
uint64_t v_mtime;
uint64_t v_inode;
@ -398,29 +479,55 @@ void state_read(struct snapraid_state* state)
exit(EXIT_FAILURE);
}
name = s;
s = strtoken(s);
size = s;
s = strtoken(s);
mtime = s;
s = strtoken(s);
inode = s;
s = strtoken(s);
sub = s;
ret = stru64(size, &v_size);
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = stru64(mtime, &v_mtime);
c = strgetc(f);
if (c != ' ') {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = strgetu64(f, &v_size);
if (ret != 0) {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = stru64(inode, &v_inode);
c = strgetc(f);
if (c != ' ') {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = strgetu64(f, &v_mtime);
if (ret != 0) {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
c = strgetc(f);
if (c != ' ') {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = strgetu64(f, &v_inode);
if (ret != 0) {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
c = strgetc(f);
if (c != ' ') {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
ret = strgetline(f, sub, sizeof(sub));
if (ret != 0) {
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -434,11 +541,11 @@ void state_read(struct snapraid_state* state)
/* find the disk */
for(i=0;i<tommy_array_size(&state->diskarr);++i) {
disk = tommy_array_get(&state->diskarr, i);
if (strcmp(disk->name, name) == 0)
if (strcmp(disk->name, buffer) == 0)
break;
}
if (i == tommy_array_size(&state->diskarr)) {
fprintf(stderr, "Disk named '%s' not found in '%s' at line %u\n", name, path, line);
fprintf(stderr, "Disk named '%s' not found in '%s' at line %u\n", buffer, path, line);
exit(EXIT_FAILURE);
}
@ -460,10 +567,25 @@ void state_read(struct snapraid_state* state)
/* stat */
++count_file;
} else if (strcmp(tag, "blksize") == 0) {
} else if (cmd == 'c') {
ret = strgettoken(f, buffer, sizeof(buffer));
if (ret != 0) {
fprintf(stderr, "Invalid 'checksum' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
if (strcmp(buffer, "md5") == 0) {
state->hash = HASH_MD5;
} else if (strcmp(buffer, "murmur3") == 0) {
state->hash = HASH_MURMUR3;
} else {
fprintf(stderr, "Invalid 'checksum' specification '%s' in '%s' at line %u\n", buffer, path, line);
exit(EXIT_FAILURE);
}
} else if (cmd == 's') {
block_off_t blksize;
ret = stru32(s, &blksize);
ret = strgetu32(f, &blksize);
if (ret != 0) {
fprintf(stderr, "Invalid 'blksize' specification in '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
@ -474,19 +596,23 @@ void state_read(struct snapraid_state* state)
fprintf(stderr, "Please restore the 'block_size' value in the configuration file to '%u'\n", blksize / 1024);
exit(EXIT_FAILURE);
}
} else if (strcmp(tag, "checksum") == 0) {
if (strcmp(s, "md5") == 0) {
state->hash = HASH_MD5;
} else if (strcmp(s, "murmur3") == 0) {
state->hash = HASH_MURMUR3;
} else {
fprintf(stderr, "Invalid 'checksum' specification '%s' in '%s' at line %u\n", s, path, line);
exit(EXIT_FAILURE);
}
} else {
fprintf(stderr, "Unknown tag '%s' in '%s' at line%u\n", tag, path, line);
}
/* next line */
c = strgetcnl(f);
if (c == EOF) {
break;
}
if (c != '\n') {
fprintf(stderr, "Extra char '%c' after command specification in '%s' at line %u\n", (char)c, path, line);
exit(EXIT_FAILURE);
}
++line;
}
if (ferror(f)) {
fprintf(stderr, "Error reading the content file '%s' at line %u\n", path, line);
exit(EXIT_FAILURE);
}
if (file) {

242
util.c
View file

@ -22,95 +22,6 @@
/****************************************************************************/
/* string */
int strgets(char* s, unsigned size, FILE* f)
{
char* i = s;
char* send = s + size;
int c;
while (1) {
#if HAVE_GETC_UNLOCKED
c = getc_unlocked(f);
#else
c = getc(f);
#endif
if (c == EOF || c == '\n')
break;
*i++ = c;
if (i == send) {
return -1; /* return error if the buffer is full */
}
}
if (c == EOF) {
if (
#if HAVE_FERROR_UNLOCKED
ferror_unlocked(f)
#else
ferror(f)
#endif
) {
return -1;
}
if (i == s)
return 0; /* return EOF only if nothing was read */
}
/* remove ending carrige return to support the Windows CR+LF format */
if (i != s && i[-1] == '\r')
--i;
*i = 0;
return i - s + 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)
@ -172,6 +83,159 @@ char* strdechex(void* void_data, unsigned data_len, char* str)
return 0;
}
/****************************************************************************/
/* get */
int strgettoken(FILE* f, char* s, unsigned size)
{
char* i = s;
char* send = s + size;
int c;
while (1) {
c = strgetc(f);
if (c == EOF) {
break;
}
if (c == ' ' || c == '\t') {
ungetc(c, f);
break;
}
if (c == '\n') {
/* remove ending carrige return to support the Windows CR+LF format */
if (i != s && i[-1] == '\r')
--i;
ungetc(c, f);
break;
}
*i++ = c;
if (i == send)
return -1;
}
*i = 0;
return 0;
}
int strgetline(FILE* f, char* s, unsigned size)
{
char* i = s;
char* send = s + size;
int c;
while (1) {
c = strgetc(f);
if (c == EOF) {
break;
}
if (c == '\n') {
/* remove ending carrige return to support the Windows CR+LF format */
if (i != s && i[-1] == '\r')
--i;
ungetc(c, f);
break;
}
*i++ = c;
if (i == send)
return -1;
}
*i = 0;
return 0;
}
int strgetu32(FILE* f, uint32_t* value)
{
int c;
c = strgetc(f);
if (c>='0' && c<='9') {
uint32_t v;
v = c - '0';
c = strgetc(f);
while (c>='0' && c<='9') {
v *= 10;
v += c - '0';
c = strgetc(f);
}
*value = v;
ungetc(c, f);
return 0;
} else {
/* nothing read */
return -1;
}
}
int strgetu64(FILE* f, uint64_t* value)
{
int c;
c = strgetc(f);
if (c>='0' && c<='9') {
uint64_t v;
v = c - '0';
c = strgetc(f);
while (c>='0' && c<='9') {
v *= 10;
v += c - '0';
c = strgetc(f);
}
*value = v;
ungetc(c, f);
return 0;
} else {
/* nothing read */
return -1;
}
}
int strgethex(FILE* f, void* void_data, unsigned data_len)
{
unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
unsigned b0;
unsigned b1;
unsigned b;
int c;
c = strgetc(f);
if (c == EOF)
return -1;
b0 = strdecset[c];
c = strgetc(f);
if (c == EOF)
return -1;
b1 = strdecset[c];
b = (b0 << 4) | b1;
if (b > 0xFF)
return -1;
*data++ = b;
}
return 0;
}
/****************************************************************************/
/* path */

134
util.h
View file

@ -21,57 +21,6 @@
/****************************************************************************/
/* string */
/**
* Marks the end of the current token, and skip one and only one separator if present.
*/
static inline char* strtoken(char* s)
{
while (*s && *s != ' ')
++s;
if (*s) {
*s = 0;
++s;
}
return s;
}
/**
* Like strtoken() but trim spaces.
*/
static inline char* strtoken_trim(char* s)
{
while (*s && !isspace(*s))
++s;
if (*s) {
*s = 0;
++s;
}
/* skip spaces */
while (*s && isspace(*s))
++s;
return s;
}
/**
* Gets a text line from a file.
* Returns -1 on error, 0 on OEF, or the number of chars returned in the buffer, including the ending 0.
*/
int strgets(char* s, unsigned size, FILE* f);
/**
* Convert a string to a 32 bit unsigned.
* Returns 0 on success.
*/
int stru32(const char* s, uint32_t* value);
/**
* Convert a string to a 64 bit unsigned.
* Returns 0 on success.
*/
int stru64(const char* s, uint64_t* value);
/**
* Encodes to hex.
*/
@ -82,6 +31,89 @@ void strenchex(char* str, const void* void_data, unsigned data_len);
*/
char* strdechex(void* void_data, unsigned data_len, char* str);
/****************************************************************************/
/* get */
/**
* Get a char from a stream.
*/
static inline int strgetc(FILE* f)
{
#if HAVE_GETC_UNLOCKED
return getc_unlocked(f);
#else
return getc(f);
#endif
}
/**
* Get a char from a stream, ignoring one '\r'.
*/
static inline int strgetcnl(FILE* f)
{
int c;
c = strgetc(f);
if (c == '\r')
c = strgetc(f);
return c;
}
/**
* Read all the spaces and tabs.
* Returns the number of spaces and tabs read.
*/
static inline int strgetspace(FILE* f)
{
int count = 0;
int c;
c = strgetc(f);
while (c == ' ' || c == '\t') {
++count;
c = strgetc(f);
}
ungetc(c, f);
return count;
}
/**
* Read until the first space or tab.
* Stops at the first ' ', '\t', '\n' or EOF.
* Returns error if the buffer is too small.
*/
int strgettoken(FILE* f, char* str, unsigned size);
/**
* Read until the end of line.
* Stops at the first '\n' or EOF.
* Returns error if the buffer is too small.
*/
int strgetline(FILE* f, char* str, unsigned size);
/**
* Read a 32 bit number.
* Stops at the first not digit char or EOF.
* Returns error if there isn't enough to read.
*/
int strgetu32(FILE* f, uint32_t* value);
/**
* Read a 64 bit number.
* Stops at the first not digit char.
* Returns error if there isn't enough to read.
*/
int strgetu64(FILE* f, uint64_t* value);
/**
* Read an hexadecimal string of fixed length.
* Returns error if there isn't enough to read.
*/
int strgethex(FILE* f, void* void_data, unsigned data_len);
/****************************************************************************/
/* path */