diff --git a/state.c b/state.c index e2845fa5..942948c6 100644 --- a/state.c +++ b/state.c @@ -53,7 +53,7 @@ void state_done(struct snapraid_state* state) void state_config(struct snapraid_state* state, const char* path, int verbose, int gui, int force_zero, int force_empty, int expect_unrecoverable, int expect_recoverable) { - FILE* f; + STREAM* f; unsigned line; unsigned content_count; @@ -70,7 +70,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i fflush(stderr); } - f = fopen(path, "rt"); + f = sopen(path); if (!f) { if (errno == ENOENT) { fprintf(stderr, "No configuration file found at '%s'\n", path); @@ -91,20 +91,20 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i int c; /* skip initial spaces */ - strgetspace(f); + sgetspace(f); /* read the command */ - ret = strgettoken(f, tag, sizeof(tag)); + ret = sgettok(f, tag, sizeof(tag)); if (ret != 0) { fprintf(stderr, "Error reading the configuration file '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } /* skip spaces after the command */ - strgetspace(f); + sgetspace(f); if (strcmp(tag, "block_size") == 0) { - ret = strgetu32(f, &state->block_size); + ret = sgetu32(f, &state->block_size); if (ret != 0) { fprintf(stderr, "Invalid 'block_size' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -129,7 +129,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i exit(EXIT_FAILURE); } - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'parity' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -142,7 +142,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i exit(EXIT_FAILURE); } - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'q-parity' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -153,7 +153,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i } else if (strcmp(tag, "content") == 0) { struct snapraid_content* content; - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'content' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -166,15 +166,15 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i } else if (strcmp(tag, "disk") == 0) { char dir[PATH_MAX]; - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(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); + sgetspace(f); - ret = strgettoken(f, dir, sizeof(dir)); + ret = sgettok(f, dir, sizeof(dir)); if (ret != 0) { fprintf(stderr, "Invalid 'disk' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -184,7 +184,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i } else if (strcmp(tag, "exclude") == 0) { struct snapraid_filter* filter; - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'exclude' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -199,7 +199,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i } else if (strcmp(tag, "include") == 0) { struct snapraid_filter* filter; - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'include' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -214,37 +214,37 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i } else if (tag[0] == 0) { /* allow empty lines */ } else if (tag[0] == '#') { - ret = strgetline(f, buffer, sizeof(buffer)); + ret = sgetline(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' at line %u\n", tag, path, line); + fprintf(stderr, "Invalid command '%s' in '%s' at line %u\n", tag, path, line); exit(EXIT_FAILURE); } /* skip final spaces */ - strgetspace(f); + sgetspace(f); /* next line */ - c = strgetcnl(f); + c = sgeteol(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); + fprintf(stderr, "Extra data in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } ++line; } - if (ferror(f)) { + if (serror(f)) { fprintf(stderr, "Error reading the configuration file '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - fclose(f); + sclose(f); if (state->parity[0] == 0) { fprintf(stderr, "No 'parity' specification in '%s'\n", path); @@ -279,7 +279,7 @@ void state_config(struct snapraid_state* state, const char* path, int verbose, i void state_read(struct snapraid_state* state) { - FILE* f; + STREAM* f; char path[PATH_MAX]; struct snapraid_disk* disk; struct snapraid_file* file; @@ -305,7 +305,7 @@ void state_read(struct snapraid_state* state) } printf("Loading state from %s...\n", path); - f = fopen(path, "rt"); + f = sopen(path); if (f != 0) { /* if openend stop the search */ break; @@ -342,56 +342,24 @@ void state_read(struct snapraid_state* state) while (1) { char buffer[PATH_MAX]; + char tag[PATH_MAX]; int ret; - int cmd; int c; - /* 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); + /* read the command */ + ret = sgettok(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 (cmd == 'b' || cmd == 'i') { + /* skip only one space if present */ + c = sgetc(f); + if (c != ' ') { + sungetc(c, f); + } + + if (strcmp(tag, "blk") == 0 || strcmp(tag, "inv") == 0) { /* "blk"/"inv" command */ block_off_t v_pos; struct snapraid_block* block; @@ -401,7 +369,7 @@ void state_read(struct snapraid_state* state) exit(EXIT_FAILURE); } - ret = strgetu32(f, &v_pos); + ret = sgetu32(f, &v_pos); if (ret != 0) { fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -422,10 +390,10 @@ void state_read(struct snapraid_state* state) block->parity_pos = v_pos; /* check if we are at the end of the line */ - c = strgetcnl(f); + c = sgeteol(f); if (c == '\n') { /* no hash present */ - ungetc(c, f); + sungetc(c, f); } else { if (c != ' ') { fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line); @@ -433,7 +401,7 @@ void state_read(struct snapraid_state* state) } /* set the hash only if present */ - ret = strgethex(f, block->hash, HASH_SIZE); + ret = sgethex(f, block->hash, HASH_SIZE); if (ret != 0) { fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -443,7 +411,7 @@ void state_read(struct snapraid_state* state) } /* set the parity only if present */ - if (cmd == 'b') + if (strcmp(tag, "blk") == 0) block_flag_set(block, BLOCK_HAS_PARITY); /* parity implies hash */ @@ -465,7 +433,7 @@ void state_read(struct snapraid_state* state) /* stat */ ++count_block; - } else if (cmd == 'f') { + } else if (strcmp(tag, "file") == 0) { /* file */ char buffer[PATH_MAX]; char sub[PATH_MAX]; @@ -479,55 +447,55 @@ void state_read(struct snapraid_state* state) exit(EXIT_FAILURE); } - ret = strgettoken(f, buffer, sizeof(buffer)); + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - c = strgetc(f); + c = sgetc(f); if (c != ' ') { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - ret = strgetu64(f, &v_size); + ret = sgetu64(f, &v_size); if (ret != 0) { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - c = strgetc(f); + c = sgetc(f); if (c != ' ') { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - ret = strgetu64(f, &v_mtime); + ret = sgetu64(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); + c = sgetc(f); if (c != ' ') { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - ret = strgetu64(f, &v_inode); + ret = sgetu64(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); + c = sgetc(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)); + ret = sgetline(f, sub, sizeof(sub)); if (ret != 0) { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -567,8 +535,8 @@ void state_read(struct snapraid_state* state) /* stat */ ++count_file; - } else if (cmd == 'c') { - ret = strgettoken(f, buffer, sizeof(buffer)); + } else if (strcmp(tag, "checksum") == 0) { + ret = sgettok(f, buffer, sizeof(buffer)); if (ret != 0) { fprintf(stderr, "Invalid 'checksum' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -582,10 +550,10 @@ void state_read(struct snapraid_state* state) fprintf(stderr, "Invalid 'checksum' specification '%s' in '%s' at line %u\n", buffer, path, line); exit(EXIT_FAILURE); } - } else if (cmd == 's') { + } else if (strcmp(tag, "blksize") == 0) { block_off_t blksize; - ret = strgetu32(f, &blksize); + ret = sgetu32(f, &blksize); if (ret != 0) { fprintf(stderr, "Invalid 'blksize' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -596,21 +564,33 @@ 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 (tag[0] == 0) { + /* allow empty lines */ + sgetspace(f); + } else if (tag[0] == '#') { + ret = sgetline(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, "Invalid command '%s' in '%s' at line %u\n", tag, path, line); + exit(EXIT_FAILURE); } /* next line */ - c = strgetcnl(f); + c = sgeteol(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); + fprintf(stderr, "Extra data in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } ++line; } - if (ferror(f)) { + if (serror(f)) { fprintf(stderr, "Error reading the content file '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } @@ -620,7 +600,7 @@ void state_read(struct snapraid_state* state) exit(EXIT_FAILURE); } - fclose(f); + sclose(f); if (state->verbose) { printf("\tfile %u\n", count_file); diff --git a/util.c b/util.c index 880a0bd1..44a8d9e6 100644 --- a/util.c +++ b/util.c @@ -84,28 +84,77 @@ char* strdechex(void* void_data, unsigned data_len, char* str) } /****************************************************************************/ -/* get */ +/* stream */ -int strgettoken(FILE* f, char* s, unsigned size) +STREAM* sopen(const char* file) { - char* i = s; - char* send = s + size; + STREAM* s = malloc_nofail(sizeof(STREAM)); + + s->f = open(file, O_RDONLY | O_BINARY | O_SEQUENTIAL); + if (s->f == -1) { + free(s); + return 0; + } + + s->buffer = malloc_nofail(STREAM_SIZE); + s->pos = s->buffer; + s->end = s->buffer; + s->state = STREAM_OK; + + return s; +} + +void sclose(STREAM* s) +{ + close(s->f); + free(s->buffer); + free(s); +} + +int sflow(STREAM* s) +{ + ssize_t ret; + + if (s->state != STREAM_OK) + return EOF; + + ret = read(s->f, s->buffer, STREAM_SIZE); + + if (ret < 0) { + s->state = STREAM_ERROR; + return EOF; + } + if (ret == 0) { + s->state = STREAM_EOF; + return EOF; + } + + s->pos = s->buffer; + s->end = s->buffer + ret; + + return *s->pos++; +} + +int sgettok(STREAM* f, char* str, int size) +{ + char* i = str; + char* send = str + size; int c; while (1) { - c = strgetc(f); + c = sgetc(f); if (c == EOF) { break; } if (c == ' ' || c == '\t') { - ungetc(c, f); + sungetc(c, f); break; } if (c == '\n') { /* remove ending carrige return to support the Windows CR+LF format */ - if (i != s && i[-1] == '\r') + if (i != str && i[-1] == '\r') --i; - ungetc(c, f); + sungetc(c, f); break; } @@ -120,29 +169,52 @@ int strgettoken(FILE* f, char* s, unsigned size) return 0; } -int strgetline(FILE* f, char* s, unsigned size) +int sgetline(STREAM* f, char* str, int size) { - char* i = s; - char* send = s + size; + char* i = str; + char* send = str + 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; + /* if there is enough data in memory */ + if (sptrlookup(f, size)) { + unsigned char* pos = sptrget(f); + + while (1) { + c = *pos++; + if (c == '\n') { + /* remove ending carrige return to support the Windows CR+LF format */ + if (i != str && i[-1] == '\r') + --i; + --pos; + break; + } + + *i++ = c; + + if (i == send) + return -1; } - *i++ = c; + sptrset(f, pos); + } else { + while (1) { + c = sgetc(f); + if (c == EOF) { + break; + } + if (c == '\n') { + /* remove ending carrige return to support the Windows CR+LF format */ + if (i != str && i[-1] == '\r') + --i; + sungetc(c, f); + break; + } - if (i == send) - return -1; + *i++ = c; + + if (i == send) + return -1; + } } *i = 0; @@ -150,26 +222,26 @@ int strgetline(FILE* f, char* s, unsigned size) return 0; } -int strgetu32(FILE* f, uint32_t* value) +int sgetu32(STREAM* f, uint32_t* value) { int c; - c = strgetc(f); + c = sgetc(f); if (c>='0' && c<='9') { uint32_t v; v = c - '0'; - c = strgetc(f); + c = sgetc(f); while (c>='0' && c<='9') { v *= 10; v += c - '0'; - c = strgetc(f); + c = sgetc(f); } *value = v; - ungetc(c, f); + sungetc(c, f); return 0; } else { /* nothing read */ @@ -177,26 +249,26 @@ int strgetu32(FILE* f, uint32_t* value) } } -int strgetu64(FILE* f, uint64_t* value) +int sgetu64(STREAM* f, uint64_t* value) { int c; - c = strgetc(f); + c = sgetc(f); if (c>='0' && c<='9') { uint64_t v; v = c - '0'; - c = strgetc(f); + c = sgetc(f); while (c>='0' && c<='9') { v *= 10; v += c - '0'; - c = strgetc(f); + c = sgetc(f); } *value = v; - ungetc(c, f); + sungetc(c, f); return 0; } else { /* nothing read */ @@ -204,33 +276,63 @@ int strgetu64(FILE* f, uint64_t* value) } } -int strgethex(FILE* f, void* void_data, unsigned data_len) +int sgethex(STREAM* f, void* void_data, int size) { unsigned char* data = void_data; - unsigned i; - for(i=0;i 0xFF) { return -1; - b0 = strdecset[c]; + } - c = strgetc(f); - if (c == EOF) - return -1; - b1 = strdecset[c]; + sptrset(f, pos); + } else { + /* standard version using sgetc() */ + while (size--) { + unsigned b0; + unsigned b1; + unsigned b; + int c; - b = (b0 << 4) | b1; + c = sgetc(f); + if (c == EOF) + return -1; + b0 = strdecset[c]; - if (b > 0xFF) - return -1; + c = sgetc(f); + if (c == EOF) + return -1; + b1 = strdecset[c]; - *data++ = b; + b = (b0 << 4) | b1; + + if (b > 0xFF) + return -1; + + *data++ = b; + } } return 0; diff --git a/util.h b/util.h index 21c8e83f..86ae6b8f 100644 --- a/util.h +++ b/util.h @@ -31,32 +31,105 @@ void strenchex(char* str, const void* void_data, unsigned data_len); */ char* strdechex(void* void_data, unsigned data_len, char* str); - /****************************************************************************/ -/* get */ +/* stream */ + +#define STREAM_SIZE (64*1024) + +#define STREAM_OK 0 +#define STREAM_ERROR -1 +#define STREAM_EOF 1 + +struct stream { + unsigned char* buffer; + unsigned char* pos; + unsigned char* end; + int state; + int f; +}; /** - * Get a char from a stream. + * Opaque STREAM type. Like ::FILE. */ -static inline int strgetc(FILE* f) +typedef struct stream STREAM; + +/** + * Open a stream for reading. Like fopen(). + */ +STREAM* sopen(const char* file); + +/** + * Close a stream. Like fclose(). + */ +void sclose(STREAM* s); + +/** + * Fill the stream buffer and read a char. + */ +int sflow(STREAM* s); + +/** + * Checks if the buffer has enough data loaded. + */ +static inline int sptrlookup(STREAM* s, int size) { -#if HAVE_GETC_UNLOCKED - return getc_unlocked(f); -#else - return getc(f); -#endif + return s->pos + size <= s->end; +} + +/** + * Gets the current stream ptr. + */ +unsigned char* sptrget(STREAM* s) +{ + return s->pos; +} + +/** + * Set the current stream ptr. + */ +void sptrset(STREAM* s, unsigned char* ptr) +{ + s->pos = ptr; +} + +/** + * Read a char. Like fgetc(). + */ +static inline int sgetc(STREAM* s) +{ + if (s->pos == s->end) + return sflow(s); + return *s->pos++; +} + +/** + * Unread a char. + * Like ungetc() but you have to unget the same char read. + */ +static inline void sungetc(int c, STREAM* s) +{ + if (c != EOF) + --s->pos; +} + +/** + * Check the error status. Like ferror(). + */ +static inline int serror(STREAM* s) +{ + return s->state == STREAM_ERROR; } /** * Get a char from a stream, ignoring one '\r'. */ -static inline int strgetcnl(FILE* f) +static inline int sgeteol(STREAM* f) { int c; - c = strgetc(f); + c = sgetc(f); if (c == '\r') - c = strgetc(f); + c = sgetc(f); return c; } @@ -65,18 +138,18 @@ static inline int strgetcnl(FILE* f) * Read all the spaces and tabs. * Returns the number of spaces and tabs read. */ -static inline int strgetspace(FILE* f) +static inline int sgetspace(STREAM* f) { int count = 0; int c; - c = strgetc(f); + c = sgetc(f); while (c == ' ' || c == '\t') { ++count; - c = strgetc(f); + c = sgetc(f); } - ungetc(c, f); + sungetc(c, f); return count; } @@ -85,34 +158,34 @@ static inline int strgetspace(FILE* f) * Stops at the first ' ', '\t', '\n' or EOF. * Returns error if the buffer is too small. */ -int strgettoken(FILE* f, char* str, unsigned size); +int sgettok(STREAM* f, char* str, int 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); +int sgetline(STREAM* f, char* str, int 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); +int sgetu32(STREAM* 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); +int sgetu64(STREAM* 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); +int sgethex(STREAM* f, void* data, int size); /****************************************************************************/ /* path */