mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Add the fix command
Split in various files Add --force switch Add --start switch
This commit is contained in:
parent
8e953adb27
commit
4bd4f9dd79
16 changed files with 2335 additions and 1532 deletions
316
check.c
Normal file
316
check.c
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
#include "state.h"
|
||||
#include "parity.h"
|
||||
#include "handle.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* check */
|
||||
|
||||
static int state_check_process(int ret_on_error, struct snapraid_state* state, int fix, int parity_f, pos_t blockstart, pos_t blockmax)
|
||||
{
|
||||
struct snapraid_handle* handle;
|
||||
unsigned diskmax = tommy_array_size(&state->diskarr);
|
||||
pos_t i;
|
||||
unsigned j;
|
||||
unsigned char* block_buffer;
|
||||
unsigned char* xor_buffer;
|
||||
int ret;
|
||||
uint64_t count_size;
|
||||
uint64_t count_block;
|
||||
time_t start;
|
||||
time_t last;
|
||||
unsigned error;
|
||||
unsigned unrecoverable_error;
|
||||
unsigned recovered_error;
|
||||
|
||||
block_buffer = malloc_nofail(state->block_size);
|
||||
xor_buffer = malloc_nofail(state->block_size);
|
||||
|
||||
handle = malloc_nofail(diskmax * sizeof(struct snapraid_handle));
|
||||
for(i=0;i<diskmax;++i) {
|
||||
handle[i].disk = tommy_array_get(&state->diskarr, i);
|
||||
handle[i].file = 0;
|
||||
handle[i].f = -1;
|
||||
}
|
||||
error = 0;
|
||||
unrecoverable_error = 0;
|
||||
recovered_error = 0;
|
||||
|
||||
count_size = 0;
|
||||
count_block = 0;
|
||||
start = time(0);
|
||||
last = start;
|
||||
|
||||
for(i=blockstart;i<blockmax;++i) {
|
||||
int hashed;
|
||||
int failed;
|
||||
struct snapraid_block* failed_block;
|
||||
struct snapraid_handle* failed_handle;
|
||||
|
||||
/* for each disk, search for a hashed block */
|
||||
hashed = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
if (block && block->is_hashed) {
|
||||
hashed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there is at least an hashed block we have to check */
|
||||
if (!hashed)
|
||||
continue;
|
||||
|
||||
/* start with 0 */
|
||||
memset(xor_buffer, 0, state->block_size);
|
||||
|
||||
/* for each disk, process the block */
|
||||
failed = 0;
|
||||
failed_block = 0;
|
||||
failed_handle = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
int read_size;
|
||||
struct md5_t md5;
|
||||
unsigned char hash[HASH_MAX];
|
||||
struct snapraid_block* block;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
/* we can check and fix only if the block is hashed */
|
||||
if (!block->is_hashed)
|
||||
continue;
|
||||
|
||||
if (fix) {
|
||||
/* if fixing, create the file and open for writing */
|
||||
handle_create(&handle[j], block->file);
|
||||
} else {
|
||||
/* if checking, open the file for reading */
|
||||
ret = handle_open(-1, &handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
/* save the failed block for the parity check */
|
||||
++failed;
|
||||
failed_block = block;
|
||||
failed_handle = &handle[j];
|
||||
|
||||
fprintf(stderr, "%u: Open error for file %s at position %u\n", i, block->file->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
read_size = handle_read(-1, &handle[j], block, block_buffer, state->block_size);
|
||||
if (read_size == -1) {
|
||||
/* save the failed block for the parity check */
|
||||
++failed;
|
||||
failed_block = block;
|
||||
failed_handle = &handle[j];
|
||||
|
||||
fprintf(stderr, "%u: Read error for file %s at position %u\n", i, block->file->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* now compute the hash */
|
||||
md5_init(&md5);
|
||||
md5_update(&md5, block_buffer, read_size);
|
||||
md5_final(&md5, hash);
|
||||
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_MAX) != 0) {
|
||||
/* save the failed block for the parity check */
|
||||
++failed;
|
||||
failed_block = block;
|
||||
failed_handle = &handle[j];
|
||||
|
||||
fprintf(stderr, "%u: Data error for file %s at position %u\n", i, block->file->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* compute the parity */
|
||||
memxor(xor_buffer, block_buffer, read_size);
|
||||
|
||||
count_size += read_size;
|
||||
}
|
||||
|
||||
if (failed == 0) {
|
||||
int ret;
|
||||
int fixable;
|
||||
|
||||
fixable = 0;
|
||||
|
||||
/* read the parity */
|
||||
ret = parity_read(-1, state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "%u: Parity read error\n", i);
|
||||
++error;
|
||||
|
||||
fixable = 1;
|
||||
} else {
|
||||
/* compare it */
|
||||
if (memcmp(xor_buffer, block_buffer, state->block_size) != 0) {
|
||||
fprintf(stderr, "%u: Parity data error\n", i);
|
||||
++error;
|
||||
|
||||
fixable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fix && fixable) {
|
||||
/* fix the parity */
|
||||
/* if all the file hashes are matching, we can recompute the parity */
|
||||
parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
|
||||
fprintf(stderr, "%u: Fixed\n", i);
|
||||
++recovered_error;
|
||||
}
|
||||
} else if (failed == 1) {
|
||||
struct md5_t md5;
|
||||
unsigned char hash[HASH_MAX];
|
||||
unsigned failed_size;
|
||||
int ret;
|
||||
int fixable;
|
||||
|
||||
fixable = 0;
|
||||
|
||||
/* read the parity */
|
||||
ret = parity_read(-1, state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "%u: Parity read error\n", i);
|
||||
fprintf(stderr, "%u: UNRECOVERABLE errors for this block\n", i);
|
||||
++unrecoverable_error;
|
||||
} else {
|
||||
/* compute the failed block */
|
||||
memxor(block_buffer, xor_buffer, state->block_size);
|
||||
|
||||
failed_size = block_file_size(failed_block, state->block_size);
|
||||
|
||||
/* now compute the hash */
|
||||
md5_init(&md5);
|
||||
md5_update(&md5, block_buffer, failed_size);
|
||||
md5_final(&md5, hash);
|
||||
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, failed_block->hash, HASH_MAX) != 0) {
|
||||
fprintf(stderr, "%u: Parity data error\n", i);
|
||||
fprintf(stderr, "%u: UNRECOVERABLE erros for this block\n", i);
|
||||
++unrecoverable_error;
|
||||
} else {
|
||||
fixable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fix && fixable) {
|
||||
/* fix the file */
|
||||
/* if only one file is wrong, we already have recomputed it */
|
||||
handle_write(failed_handle, failed_block, block_buffer, state->block_size);
|
||||
|
||||
fprintf(stderr, "%u: Fixed\n", i);
|
||||
++recovered_error;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%u: UNRECOVERABLE errors for this block\n", i);
|
||||
++unrecoverable_error;
|
||||
}
|
||||
|
||||
/* count the number of processed block */
|
||||
++count_block;
|
||||
|
||||
/* progress */
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size))
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%u%% completed, %u MB processed\n", i * 100 / blockmax, (unsigned)(count_size / (1024*1024)));
|
||||
|
||||
if (error || unrecoverable_error) {
|
||||
if (error)
|
||||
printf("%u recoverable errors\n", error);
|
||||
else
|
||||
printf("No recoverable errors\n");
|
||||
if (fix)
|
||||
printf("%u fixed errors\n", recovered_error);
|
||||
if (unrecoverable_error)
|
||||
printf("%u UNRECOVERABLE errors\n", unrecoverable_error);
|
||||
else
|
||||
printf("No unrecoverable errors\n");
|
||||
} else {
|
||||
printf("No error\n");
|
||||
}
|
||||
|
||||
for(j=0;j<diskmax;++j) {
|
||||
handle_close(&handle[j]);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
free(block_buffer);
|
||||
free(xor_buffer);
|
||||
|
||||
return unrecoverable_error != 0 ? ret_on_error : 0;
|
||||
}
|
||||
|
||||
void state_check(struct snapraid_state* state, int fix, pos_t blockstart)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
pos_t blockmax;
|
||||
off_t size;
|
||||
int ret;
|
||||
int f;
|
||||
|
||||
if (fix)
|
||||
printf("Checking and fixing...\n");
|
||||
else
|
||||
printf("Checking...\n");
|
||||
|
||||
blockmax = parity_resize(state);
|
||||
size = blockmax * (off_t)state->block_size;
|
||||
|
||||
if (blockstart >= blockmax) {
|
||||
fprintf(stderr, "The specified starting block %u is bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s", state->parity);
|
||||
if (fix) {
|
||||
/* if fixing, create the file and open for writing */
|
||||
/* it never fails */
|
||||
f = parity_create(path, size);
|
||||
} else {
|
||||
/* if checking, open the file for reading */
|
||||
/* it may fail if the file doesn't exist */
|
||||
f = parity_open(-1, path);
|
||||
}
|
||||
|
||||
ret = state_check_process(-1, state, fix, f, blockstart, blockmax);
|
||||
if (ret == -1) {
|
||||
/* no extra message */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (f != -1) {
|
||||
parity_close(path, f);
|
||||
}
|
||||
}
|
||||
|
||||
228
config.h
Normal file
228
config.h
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if building universal (internal helper macro) */
|
||||
/* #undef AC_APPLE_UNIVERSAL_BUILD */
|
||||
|
||||
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
#define HAVE_DIRENT_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `fsync' function. */
|
||||
#define HAVE_FSYNC 1
|
||||
|
||||
/* Define to 1 if you have the `ftruncate' function. */
|
||||
#define HAVE_FTRUNCATE 1
|
||||
|
||||
/* Define to 1 if you have the `getopt' function. */
|
||||
#define HAVE_GETOPT 1
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#define HAVE_GETOPT_H 1
|
||||
|
||||
/* Define to 1 if you have the `getopt_long' function. */
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#define HAVE_MALLOC 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#define HAVE_MEMSET 1
|
||||
|
||||
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
|
||||
/* #undef HAVE_NDIR_H */
|
||||
|
||||
/* Define to 1 if you have the `posix_fadvise' function. */
|
||||
#define HAVE_POSIX_FADVISE 1
|
||||
|
||||
/* Define to 1 if you have the `posix_fallocate' function. */
|
||||
#define HAVE_POSIX_FALLOCATE 1
|
||||
|
||||
/* Define to 1 if you have the `pread' function. */
|
||||
#define HAVE_PREAD 1
|
||||
|
||||
/* Define to 1 if you have the `pwrite' function. */
|
||||
#define HAVE_PWRITE 1
|
||||
|
||||
/* Define to 1 if you have the `snprintf' function. */
|
||||
#define HAVE_SNPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the <stddef.h> header file. */
|
||||
#define HAVE_STDDEF_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#define HAVE_STRCHR 1
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strrchr' function. */
|
||||
#define HAVE_STRRCHR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
/* #undef HAVE_SYS_DIR_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
/* #undef HAVE_SYS_NDIR_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the <utime.h> header file. */
|
||||
#define HAVE_UTIME_H 1
|
||||
|
||||
/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
|
||||
#define HAVE_UTIME_NULL 1
|
||||
|
||||
/* Define to 1 if you have the `vsnprintf' function. */
|
||||
#define HAVE_VSNPRINTF 1
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "snapraid"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "snapraid"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "snapraid 0.1"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "snapraid"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "0.1"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "0.1"
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
/* # undef WORDS_BIGENDIAN */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
/* Enable GLIBC symbols */
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
/* #undef _LARGE_FILES */
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
/* #undef _UINT32_T */
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
/* #undef _UINT64_T */
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
/* #undef _UINT8_T */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
/* #undef inline */
|
||||
#endif
|
||||
|
||||
/* Define to rpl_malloc if the replacement function should be used. */
|
||||
/* #undef malloc */
|
||||
|
||||
/* Define to `long int' if <sys/types.h> does not define. */
|
||||
/* #undef off_t */
|
||||
|
||||
/* Define to the equivalent of the C99 'restrict' keyword, or to
|
||||
nothing if this is not supported. Do not define if restrict is
|
||||
supported directly. */
|
||||
#define restrict __restrict
|
||||
/* Work around a bug in Sun C++: it does not support _Restrict or
|
||||
__restrict__, even though the corresponding Sun C compiler ends up with
|
||||
"#define restrict _Restrict" or "#define restrict __restrict__" in the
|
||||
previous line. Perhaps some future version of Sun C++ will work with
|
||||
restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
|
||||
#if defined __SUNPRO_CC && !defined __RESTRICT
|
||||
# define _Restrict
|
||||
# define __restrict__
|
||||
#endif
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef ssize_t */
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
/* #undef uint32_t */
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 64 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
/* #undef uint64_t */
|
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||
such a type exists and the standard includes do not define it. */
|
||||
/* #undef uint8_t */
|
||||
|
||||
/* Define to empty if the keyword `volatile' does not work. Warning: valid
|
||||
code using `volatile' can become incorrect without. Disable with care. */
|
||||
/* #undef volatile */
|
||||
118
elem.c
Normal file
118
elem.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "util.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* snapraid */
|
||||
|
||||
pos_t block_file_pos(struct snapraid_block* block)
|
||||
{
|
||||
struct snapraid_file* file = block->file;
|
||||
|
||||
if (block < file->blockvec || block >= file->blockvec + file->blockmax) {
|
||||
fprintf(stderr, "Internal inconsistency in block %u ownership\n", block->parity_pos);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return block - file->blockvec;
|
||||
}
|
||||
|
||||
unsigned block_file_size(struct snapraid_block* block, unsigned block_size)
|
||||
{
|
||||
pos_t pos = block_file_pos(block);
|
||||
|
||||
/* if it's the last block */
|
||||
if (pos + 1 == block->file->blockmax) {
|
||||
unsigned remainder;
|
||||
if (block->file->size == 0)
|
||||
return 0;
|
||||
remainder = block->file->size % block_size;
|
||||
if (remainder == 0)
|
||||
remainder = block_size;
|
||||
return remainder;
|
||||
}
|
||||
|
||||
return block_size;
|
||||
}
|
||||
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, time_t mtime)
|
||||
{
|
||||
struct snapraid_file* file;
|
||||
pos_t i;
|
||||
|
||||
file = malloc_nofail(sizeof(struct snapraid_file));
|
||||
snprintf(file->sub, sizeof(file->sub), "%s", sub);
|
||||
file->size = size;
|
||||
file->blockmax = (size + block_size - 1) / block_size;
|
||||
file->mtime = mtime;
|
||||
file->is_present = 0;
|
||||
file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block));
|
||||
|
||||
/* set the back pointer */
|
||||
for(i=0;i<file->blockmax;++i) {
|
||||
file->blockvec[i].parity_pos = POS_INVALID;
|
||||
file->blockvec[i].is_hashed = 0;
|
||||
file->blockvec[i].file = file;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
void file_free(struct snapraid_file* file)
|
||||
{
|
||||
free(file->blockvec);
|
||||
free(file);
|
||||
}
|
||||
|
||||
int file_compare(const void* void_arg, const void* void_data)
|
||||
{
|
||||
const char* arg = void_arg;
|
||||
const struct snapraid_file* file = void_data;
|
||||
return strcmp(arg, file->sub);
|
||||
}
|
||||
|
||||
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);
|
||||
disk->first_free_block = 0;
|
||||
tommy_list_init(&disk->filelist);
|
||||
tommy_hashdyn_init(&disk->fileset);
|
||||
tommy_array_init(&disk->blockarr);
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
||||
void disk_free(struct snapraid_disk* disk)
|
||||
{
|
||||
tommy_node* node = disk->filelist;
|
||||
while (node) {
|
||||
struct snapraid_file* file = node->data;
|
||||
node = node->next;
|
||||
file_free(file);
|
||||
}
|
||||
tommy_hashdyn_done(&disk->fileset);
|
||||
tommy_array_done(&disk->blockarr);
|
||||
free(disk);
|
||||
}
|
||||
121
elem.h
Normal file
121
elem.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ELEM_H
|
||||
#define __ELEM_H
|
||||
|
||||
#include "md5.h"
|
||||
#include "tommylist.h"
|
||||
#include "tommyhash.h"
|
||||
#include "tommyhashdyn.h"
|
||||
#include "tommyarray.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* snapraid */
|
||||
|
||||
/**
|
||||
* Size of the hash used as a checksum.
|
||||
*/
|
||||
#define HASH_MAX MD5_SIZE
|
||||
|
||||
/**
|
||||
* Invalid position.
|
||||
*/
|
||||
#define POS_INVALID -1
|
||||
|
||||
/**
|
||||
* Basic position type.
|
||||
* With a 32 bit and 128k block you can address 256 TB.
|
||||
*/
|
||||
typedef unsigned pos_t;
|
||||
|
||||
struct snapraid_file;
|
||||
|
||||
/**
|
||||
* Block of a file.
|
||||
*/
|
||||
struct snapraid_block {
|
||||
pos_t parity_pos; /**< Position of the block in the parity. */
|
||||
int is_hashed; /**< If the hash of the block is valid. */
|
||||
struct snapraid_file* file; /**< Back pointer to the file owning this block. */
|
||||
unsigned char hash[HASH_MAX]; /**< Hash of the block. */
|
||||
};
|
||||
|
||||
/**
|
||||
* File.
|
||||
*/
|
||||
struct snapraid_file {
|
||||
char sub[PATH_MAX]; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
|
||||
uint64_t size; /**< Size of the file. */
|
||||
struct snapraid_block* blockvec; /**< All the blocks of the file. */
|
||||
unsigned blockmax; /**< Number of blocks. */
|
||||
time_t mtime; /**< Modification time. */
|
||||
int is_present; /**< If it's seen as present. */
|
||||
|
||||
/* nodes for data structures */
|
||||
tommy_node nodelist;
|
||||
tommy_hashdyn_node nodeset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disk.
|
||||
*/
|
||||
struct snapraid_disk {
|
||||
char name[PATH_MAX]; /**< Name of the disk. */
|
||||
char dir[PATH_MAX]; /**< Mount point of the disk. */
|
||||
pos_t first_free_block; /**< First free searching block. */
|
||||
tommy_list filelist; /**< List of all the files. */
|
||||
tommy_hashdyn fileset; /**< Hashtable by sub of all the files. */
|
||||
tommy_array blockarr; /**< Block array of the disk. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the relative position of a block inside the file.
|
||||
*/
|
||||
pos_t block_file_pos(struct snapraid_block* block);
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the block.
|
||||
* If it's the last block of a file it could less than block_size.
|
||||
*/
|
||||
unsigned block_file_size(struct snapraid_block* block, unsigned block_size);
|
||||
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, time_t mtime);
|
||||
|
||||
void file_free(struct snapraid_file* file);
|
||||
|
||||
int file_compare(const void* void_arg, const void* void_data);
|
||||
|
||||
static inline tommy_uint32_t file_hash(const char* sub)
|
||||
{
|
||||
return tommy_hash_u32(0, sub, strlen(sub));
|
||||
}
|
||||
|
||||
struct snapraid_disk* disk_alloc(const char* name, const char* dir);
|
||||
|
||||
void disk_free(struct snapraid_disk* disk);
|
||||
|
||||
static inline struct snapraid_block* disk_block_get(struct snapraid_disk* disk, pos_t pos)
|
||||
{
|
||||
if (pos < tommy_array_size(&disk->blockarr))
|
||||
return tommy_array_get(&disk->blockarr, pos);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
203
handle.c
Normal file
203
handle.c
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "handle.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* handle */
|
||||
|
||||
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
/* if already opened, nothing to do */
|
||||
if (handle->file == file && handle->f != -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close if already open */
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(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) {
|
||||
fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* just opened */
|
||||
handle->file = file;
|
||||
|
||||
/* set the size */
|
||||
ret = fstat(handle->f, &st);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (st.st_size < file->size) {
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
/* allocate real space */
|
||||
ret = posix_fallocate(handle->f, 0, file->size);
|
||||
#else
|
||||
/* allocate using a sparse file */
|
||||
ret = ftruncate(handle->f, file->size);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
if (errno == ENOSPC) {
|
||||
fprintf(stderr, "Failed to grow file '%s' due lack of space.\n", handle->path);
|
||||
} else {
|
||||
fprintf(stderr, "Error growing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (st.st_size > file->size) {
|
||||
ret = ftruncate(handle->f, file->size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/* advise sequential access */
|
||||
ret = posix_fadvise(handle->f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* if already opened, nothing to do */
|
||||
if (handle->file == file && handle->f != -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close if already open */
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as close is supposed to never fail */
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(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) {
|
||||
/* invalidate for error */
|
||||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* just opened */
|
||||
handle->file = file;
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/* advise sequential access */
|
||||
ret = posix_fadvise(handle->f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as advise is supposed to never fail */
|
||||
fprintf(stderr, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_close(struct snapraid_handle* handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
}
|
||||
|
||||
int handle_read(int ret_on_error, struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t read_ret;
|
||||
off_t offset;
|
||||
unsigned read_size;
|
||||
|
||||
offset = block_file_pos(block) * (off_t)block_size;
|
||||
|
||||
read_size = block_file_size(block, block_size);
|
||||
|
||||
read_ret = pread(handle->f, block_buffer, read_size, offset);
|
||||
if (read_ret != read_size) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error reading file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return read_size;
|
||||
}
|
||||
|
||||
void handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t write_ret;
|
||||
off_t offset;
|
||||
unsigned write_size;
|
||||
|
||||
offset = block_file_pos(block) * (off_t)block_size;
|
||||
|
||||
write_size = block_file_size(block, block_size);
|
||||
|
||||
write_ret = pwrite(handle->f, block_buffer, write_size, offset);
|
||||
if (write_ret != write_size) {
|
||||
fprintf(stderr, "Error writing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
50
handle.h
Normal file
50
handle.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __HANDLE_H
|
||||
#define __HANDLE_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* handle */
|
||||
|
||||
struct snapraid_handle {
|
||||
char path[PATH_MAX]; /**< Path of the file. */
|
||||
struct snapraid_disk* disk; /**< Disk of the file. */
|
||||
struct snapraid_file* file; /**< File opened. */
|
||||
int f; /**< Handle of the file. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a file.
|
||||
* The file is created if missing, and opening with write access.
|
||||
*/
|
||||
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
|
||||
/**
|
||||
* Open a file.
|
||||
* The file is opened for reading.
|
||||
*/
|
||||
int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
|
||||
void handle_close(struct snapraid_handle* handle);
|
||||
|
||||
int handle_read(int ret_on_error, struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
void handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
#endif
|
||||
|
||||
191
parity.c
Normal file
191
parity.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "state.h"
|
||||
#include "parity.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* parity */
|
||||
|
||||
pos_t parity_resize(struct snapraid_state* state)
|
||||
{
|
||||
unsigned disk_count = tommy_array_size(&state->diskarr);
|
||||
pos_t parity_block;
|
||||
unsigned i;
|
||||
|
||||
/* compute the size of the parity file */
|
||||
parity_block = 0;
|
||||
for(i=0;i<disk_count;++i) {
|
||||
struct snapraid_disk* disk = tommy_array_get(&state->diskarr, i);
|
||||
|
||||
/* start from the declared size */
|
||||
pos_t block = tommy_array_size(&disk->blockarr);
|
||||
|
||||
/* decrease the block until an allocated block */
|
||||
while (block > 0 && tommy_array_get(&disk->blockarr, block - 1) == 0)
|
||||
--block;
|
||||
|
||||
if (i == 0 || parity_block < block)
|
||||
parity_block = block;
|
||||
}
|
||||
|
||||
return parity_block;
|
||||
}
|
||||
|
||||
int parity_create(const char* path, off_t size)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
int f;
|
||||
|
||||
f = open(path, O_RDWR | O_CREAT | O_BINARY, 0600);
|
||||
if (f == -1) {
|
||||
fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = fstat(f, &st);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error accessing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (st.st_size < size) {
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
/* allocate real space */
|
||||
ret = posix_fallocate(f, 0, size);
|
||||
#else
|
||||
/* allocate using a sparse file */
|
||||
ret = ftruncate(f, size);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
if (errno == ENOSPC) {
|
||||
fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", path);
|
||||
} else {
|
||||
fprintf(stderr, "Error growing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (st.st_size > size) {
|
||||
ret = ftruncate(f, size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error truncating parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/* advise sequential access */
|
||||
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
int parity_open(int ret_on_error, const char* path)
|
||||
{
|
||||
int ret;
|
||||
int f;
|
||||
|
||||
f = open(path, O_RDONLY | O_BINARY);
|
||||
if (f == -1) {
|
||||
if (ret_on_error)
|
||||
return -1;
|
||||
fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/* advise sequential access */
|
||||
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as advise is supposed to never fail */
|
||||
fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void parity_close(const char* path, int f)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* ensure that data changes are written to disk */
|
||||
/* this is required to ensure that parity is more updated than content */
|
||||
/* in case of a system crash */
|
||||
ret = fsync(f);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error synching parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = close(f);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error closing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void parity_write(const char* path, int f, pos_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t write_ret;
|
||||
off_t offset;
|
||||
|
||||
offset = pos * (off_t)block_size;
|
||||
|
||||
write_ret = pwrite(f, block_buffer, block_size, offset);
|
||||
if (write_ret != block_size) {
|
||||
if (errno == ENOSPC) {
|
||||
fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", path);
|
||||
} else {
|
||||
fprintf(stderr, "Error writing file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int parity_read(int ret_on_error, const char* path, int f, pos_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t read_ret;
|
||||
off_t offset;
|
||||
|
||||
/* if the file is not opened, return an error without trying */
|
||||
if (f == -1 && ret_on_error)
|
||||
return -1;
|
||||
|
||||
offset = pos * (off_t)block_size;
|
||||
|
||||
read_ret = pread(f, block_buffer, block_size, offset);
|
||||
if (read_ret != block_size) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
fprintf(stderr, "Error reading file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return block_size;
|
||||
}
|
||||
|
||||
40
parity.h
Normal file
40
parity.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PARITY_H
|
||||
#define __PARITY_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* parity */
|
||||
|
||||
/**
|
||||
* Computes the size of the parity data in number of blocks.
|
||||
*/
|
||||
pos_t parity_resize(struct snapraid_state* state);
|
||||
|
||||
int parity_create(const char* path, off_t size);
|
||||
|
||||
int parity_open(int ret_on_error, const char* path);
|
||||
|
||||
void parity_close(const char* path, int f);
|
||||
|
||||
void parity_write(const char* parity_path, int parity_f, pos_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
int parity_read(int ret_on_error, const char* parity_path, int parity_f, pos_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
#endif
|
||||
|
||||
236
scan.c
Normal file
236
scan.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
#include "state.h"
|
||||
|
||||
struct snapraid_scan {
|
||||
unsigned count_equal;
|
||||
unsigned count_change;
|
||||
unsigned count_remove;
|
||||
unsigned count_insert;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the specified file from the data set.
|
||||
*/
|
||||
static void scan_file_remove(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
|
||||
{
|
||||
pos_t i;
|
||||
|
||||
/* free all the blocks of the file */
|
||||
for(i=0;i<file->blockmax;++i) {
|
||||
pos_t block_pos = file->blockvec[i].parity_pos;
|
||||
|
||||
/* adjust the first free position */
|
||||
if (disk->first_free_block > block_pos)
|
||||
disk->first_free_block = block_pos;
|
||||
|
||||
tommy_array_set(&disk->blockarr, block_pos, 0);
|
||||
}
|
||||
|
||||
/* remove the file from the file containers */
|
||||
tommy_hashdyn_remove_existing(&disk->fileset, &file->nodeset);
|
||||
tommy_list_remove_existing(&disk->filelist, &file->nodelist);
|
||||
|
||||
/* deallocate */
|
||||
file_free(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the specified file in the data set.
|
||||
*/
|
||||
static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
|
||||
{
|
||||
pos_t i;
|
||||
pos_t block_max;
|
||||
pos_t block_pos;
|
||||
|
||||
/* allocate the blocks of the file */
|
||||
block_pos = disk->first_free_block;
|
||||
block_max = tommy_array_size(&disk->blockarr);
|
||||
for(i=0;i<file->blockmax;++i) {
|
||||
/* find a free block */
|
||||
while (block_pos < block_max && tommy_array_get(&disk->blockarr, block_pos) != 0)
|
||||
++block_pos;
|
||||
|
||||
/* if not found, allocate a new one */
|
||||
if (block_pos == block_max) {
|
||||
++block_max;
|
||||
tommy_array_grow(&disk->blockarr, block_max);
|
||||
}
|
||||
|
||||
/* set it */
|
||||
file->blockvec[i].parity_pos = block_pos;
|
||||
tommy_array_set(&disk->blockarr, block_pos, &file->blockvec[i]);
|
||||
}
|
||||
if (file->blockmax) {
|
||||
/* set the new free position, but only if allocated something */
|
||||
disk->first_free_block = block_pos + 1;
|
||||
}
|
||||
|
||||
/* insert the file in the file containers */
|
||||
tommy_hashdyn_insert(&disk->fileset, &file->nodeset, file, file_hash(file->sub));
|
||||
tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
|
||||
}
|
||||
|
||||
static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, struct snapraid_disk* disk, const char* path, const char* sub)
|
||||
{
|
||||
struct stat st;
|
||||
struct snapraid_file* file;
|
||||
|
||||
if (stat(path, &st) != 0) {
|
||||
fprintf(stderr, "Error in stat file '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* check if the file already exists */
|
||||
file = tommy_hashdyn_search(&disk->fileset, file_compare, sub, file_hash(sub));
|
||||
if (file) {
|
||||
/* check if the file is the same */
|
||||
if (file->size == st.st_size && file->mtime == st.st_mtime) {
|
||||
/* mark as present */
|
||||
++scan->count_equal;
|
||||
file->is_present = 1;
|
||||
return;
|
||||
} else {
|
||||
/* remove and reinsert it */
|
||||
++scan->count_change;
|
||||
--scan->count_insert;
|
||||
scan_file_remove(state, disk, file);
|
||||
/* continue to insert it */
|
||||
}
|
||||
}
|
||||
|
||||
/* create the new file */
|
||||
++scan->count_insert;
|
||||
file = file_alloc(state->block_size, sub, st.st_size, st.st_mtime);
|
||||
file->is_present = 1;
|
||||
|
||||
/* insert it */
|
||||
scan_file_insert(state, disk, file);
|
||||
}
|
||||
|
||||
static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, struct snapraid_disk* disk, const char* dir, const char* sub)
|
||||
{
|
||||
DIR* d;
|
||||
struct dirent* dd;
|
||||
|
||||
d = opendir(dir);
|
||||
if (!d) {
|
||||
fprintf(stderr, "Error accessing directory '%s'. %s.\n", dir, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while ((dd = readdir(d)) != 0) {
|
||||
if (dd->d_type == DT_REG) {
|
||||
char path_next[PATH_MAX];
|
||||
char sub_next[PATH_MAX];
|
||||
snprintf(path_next, sizeof(path_next), "%s%s", dir, dd->d_name);
|
||||
snprintf(sub_next, sizeof(sub_next), "%s%s", sub, dd->d_name);
|
||||
scan_file(scan, state, disk, path_next, sub_next);
|
||||
} else if (dd->d_type == DT_DIR) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if (state->verbose) {
|
||||
printf("warning: File '%s/%s' not processed\n", dir, dd->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
void state_scan(struct snapraid_state* state)
|
||||
{
|
||||
unsigned diskmax = tommy_array_size(&state->diskarr);
|
||||
unsigned i;
|
||||
struct snapraid_scan* scan;
|
||||
|
||||
scan = malloc_nofail(diskmax * sizeof(struct snapraid_scan));
|
||||
for(i=0;i<diskmax;++i) {
|
||||
scan[i].count_equal = 0;
|
||||
scan[i].count_change = 0;
|
||||
scan[i].count_remove = 0;
|
||||
scan[i].count_insert = 0;
|
||||
}
|
||||
|
||||
for(i=0;i<diskmax;++i) {
|
||||
struct snapraid_disk* disk = tommy_array_get(&state->diskarr, i);
|
||||
tommy_node* node;
|
||||
|
||||
printf("Scanning disk %s...\n", disk->name);
|
||||
|
||||
scan_dir(&scan[i], state, disk, disk->dir, "");
|
||||
|
||||
/* check for removed files */
|
||||
node = disk->filelist;
|
||||
while (node) {
|
||||
struct snapraid_file* file = node->data;
|
||||
|
||||
/* next node */
|
||||
node = node->next;
|
||||
|
||||
/* remove if not present */
|
||||
if (!file->is_present) {
|
||||
scan_file_remove(state, disk, file);
|
||||
++scan[i].count_remove;
|
||||
}
|
||||
}
|
||||
|
||||
/* if all the previous file were removed */
|
||||
if (scan[i].count_equal == 0 && scan[i].count_remove != 0) {
|
||||
if (!state->force) {
|
||||
fprintf(stderr, "All the file in disk '%s' at dir '%s' are missing!\n", disk->name, disk->dir);
|
||||
fprintf(stderr, "If it's really what you want to do, use 'snapraid -f sync\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state->verbose) {
|
||||
struct snapraid_scan total;
|
||||
|
||||
total.count_equal = 0;
|
||||
total.count_change = 0;
|
||||
total.count_remove = 0;
|
||||
total.count_insert = 0;
|
||||
|
||||
for(i=0;i<diskmax;++i) {
|
||||
total.count_equal += scan[i].count_equal;
|
||||
total.count_change += scan[i].count_change;
|
||||
total.count_remove += scan[i].count_remove;
|
||||
total.count_insert += scan[i].count_insert;
|
||||
}
|
||||
|
||||
printf("\tequal %d\n", total.count_equal);
|
||||
printf("\tchanged %d\n", total.count_change);
|
||||
printf("\tremoved %d\n", total.count_remove);
|
||||
printf("\tadded %d\n", total.count_insert);
|
||||
}
|
||||
}
|
||||
|
||||
1545
snapraid.c
1545
snapraid.c
File diff suppressed because it is too large
Load diff
490
state.c
Normal file
490
state.c
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* Max length of a line in the configuration and state files.
|
||||
*/
|
||||
#define LINE_MAX 1024
|
||||
|
||||
void state_init(struct snapraid_state* state)
|
||||
{
|
||||
state->verbose = 0;
|
||||
state->block_size = 128*1024; /* default 128 kB */
|
||||
state->content[0] = 0;
|
||||
state->parity[0] = 0;
|
||||
tommy_array_init(&state->diskarr);
|
||||
|
||||
if (sizeof(off_t) < sizeof(uint64_t)) {
|
||||
fprintf(stderr, "Internal inconsistency in off_t type size\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void state_config(struct snapraid_state* state, const char* path, int verbose, int force)
|
||||
{
|
||||
FILE* f;
|
||||
unsigned line;
|
||||
|
||||
state->verbose = verbose;
|
||||
state->force = force;
|
||||
|
||||
f = fopen(path, "rt");
|
||||
if (!f) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "No configuration file found at '%s'\n", path);
|
||||
} else if (errno == EACCES) {
|
||||
fprintf(stderr, "You do not have rights to access the configuration file '%s'\n", path);
|
||||
} else {
|
||||
fprintf(stderr, "Error opening the configuration file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
line = 0;
|
||||
while (1) {
|
||||
char buffer[LINE_MAX];
|
||||
char* tag;
|
||||
char* s;
|
||||
int ret;
|
||||
|
||||
++line;
|
||||
|
||||
ret = strgets(buffer, LINE_MAX, f);
|
||||
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;
|
||||
s = strskip(s);
|
||||
|
||||
/* ignore commens and empty lines */
|
||||
if (*s == '#' || *s == 0)
|
||||
continue;
|
||||
|
||||
tag = s;
|
||||
s = strtoken(s);
|
||||
|
||||
if (strcmp(tag, "block_size") == 0) {
|
||||
ret = stru(s, &state->block_size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid 'block_size' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (state->block_size < 1) {
|
||||
fprintf(stderr, "Too small 'block_size' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (state->block_size > 16*1024) {
|
||||
fprintf(stderr, "Too big 'block_size' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* check if it's a power of 2 */
|
||||
if ((state->block_size & (state->block_size - 1)) != 0) {
|
||||
fprintf(stderr, "Not power of 2 'block_size' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
state->block_size *= 1024;
|
||||
} else if (strcmp(tag, "parity") == 0) {
|
||||
if (*state->parity) {
|
||||
fprintf(stderr, "Multiple 'parity' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
snprintf(state->parity, sizeof(state->parity), "%s", 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);
|
||||
} else if (strcmp(tag, "disk") == 0) {
|
||||
char* name = s;
|
||||
s = strtoken(s);
|
||||
tommy_array_insert(&state->diskarr, disk_alloc(name, s));
|
||||
} else {
|
||||
fprintf(stderr, "Unknown tag '%s' in '%s'\n", tag, path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (!state->parity) {
|
||||
fprintf(stderr, "No 'parity' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!state->content) {
|
||||
fprintf(stderr, "No 'content' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void state_read(struct snapraid_state* state)
|
||||
{
|
||||
FILE* f;
|
||||
char path[PATH_MAX];
|
||||
struct snapraid_disk* disk;
|
||||
struct snapraid_file* file;
|
||||
unsigned block_index;
|
||||
unsigned line;
|
||||
unsigned count_file;
|
||||
unsigned count_block;
|
||||
|
||||
count_file = 0;
|
||||
count_block = 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s", state->content);
|
||||
f = fopen(state->content, "r");
|
||||
if (!f) {
|
||||
/* if not found, assume empty */
|
||||
if (errno == ENOENT)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "Error opening the state file '%s'\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Loading state...\n");
|
||||
|
||||
disk = 0;
|
||||
file = 0;
|
||||
line = 0;
|
||||
block_index = 0;
|
||||
while (1) {
|
||||
char buffer[LINE_MAX];
|
||||
char* tag;
|
||||
char* s;
|
||||
int ret;
|
||||
|
||||
++line;
|
||||
|
||||
ret = strgets(buffer, LINE_MAX, f);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error reading the state file '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
/* start */
|
||||
s = buffer;
|
||||
s = strskip(s);
|
||||
|
||||
/* ignore commens and empty lines */
|
||||
if (*s == '#' || *s == 0)
|
||||
continue;
|
||||
|
||||
tag = s;
|
||||
s = strtoken(s);
|
||||
|
||||
if (strcmp(tag, "file") == 0) {
|
||||
char* name;
|
||||
char* size;
|
||||
char* mtime;
|
||||
char* sub;
|
||||
uint64_t v_size;
|
||||
uint64_t v_mtime;
|
||||
unsigned i;
|
||||
|
||||
if (file) {
|
||||
fprintf(stderr, "Missing 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
name = s;
|
||||
s = strtoken(s);
|
||||
size = s;
|
||||
s = strtoken(s);
|
||||
mtime = s;
|
||||
s = strtoken(s);
|
||||
sub = s;
|
||||
|
||||
ret = stru64(size, &v_size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = stru64(mtime, &v_mtime);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!*sub) {
|
||||
fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
break;
|
||||
}
|
||||
if (i == tommy_array_size(&state->diskarr)) {
|
||||
fprintf(stderr, "Disk named '%s' not found in '%s' at line %u\n", name, path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allocate the file */
|
||||
file = file_alloc(state->block_size, sub, v_size, v_mtime);
|
||||
|
||||
/* insert the file in the file containers */
|
||||
tommy_hashdyn_insert(&disk->fileset, &file->nodeset, file, file_hash(file->sub));
|
||||
tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
|
||||
|
||||
/* start the block allocation of the file */
|
||||
block_index = 0;
|
||||
|
||||
/* check for empty file */
|
||||
if (block_index == file->blockmax) {
|
||||
file = 0;
|
||||
disk = 0;
|
||||
}
|
||||
|
||||
/* stat */
|
||||
++count_file;
|
||||
} else if (strcmp(tag, "blk") == 0) {
|
||||
char* pos;
|
||||
char* hash;
|
||||
pos_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 = stru(pos, &v_pos);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (block_index >= file->blockmax) {
|
||||
fprintf(stderr, "Internal inconsistency in 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
block = &file->blockvec[block_index];
|
||||
|
||||
if (block->parity_pos != POS_INVALID) {
|
||||
fprintf(stderr, "Internal inconsistency in 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
block->parity_pos = v_pos;
|
||||
|
||||
/* set the hash only if present */
|
||||
if (*hash != 0) {
|
||||
e = strdechex(block->hash, HASH_MAX, hash);
|
||||
if (e) {
|
||||
fprintf(stderr, "Invalid 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
block->is_hashed = 1;
|
||||
}
|
||||
|
||||
/* insert the block in the block array */
|
||||
tommy_array_grow(&disk->blockarr, block->parity_pos + 1);
|
||||
tommy_array_set(&disk->blockarr, block->parity_pos, block);
|
||||
|
||||
/* check for termination of the block list */
|
||||
++block_index;
|
||||
if (block_index == file->blockmax) {
|
||||
file = 0;
|
||||
disk = 0;
|
||||
}
|
||||
|
||||
/* stat */
|
||||
++count_block;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown tag '%s' in '%s' at line%u\n", tag, path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
fprintf(stderr, "Missing 'blk' specification in '%s' at line %u\n", path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (state->verbose) {
|
||||
printf("\tfile %u\n", count_file);
|
||||
printf("\tblock %u\n", count_block);
|
||||
}
|
||||
}
|
||||
|
||||
void state_write(struct snapraid_state* state)
|
||||
{
|
||||
FILE* f;
|
||||
char path[PATH_MAX];
|
||||
unsigned i;
|
||||
unsigned count_file;
|
||||
unsigned count_block;
|
||||
|
||||
count_file = 0;
|
||||
count_block = 0;
|
||||
|
||||
printf("Saving state...\n");
|
||||
|
||||
snprintf(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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* for each disk */
|
||||
for(i=0;i<tommy_array_size(&state->diskarr);++i) {
|
||||
tommy_node* j;
|
||||
struct snapraid_disk* disk = tommy_array_get(&state->diskarr, i);
|
||||
|
||||
/* for each file */
|
||||
for(j=disk->filelist;j!=0;j=j->next) {
|
||||
pos_t k;
|
||||
struct snapraid_file* file = j->data;
|
||||
int ret;
|
||||
|
||||
ret = fprintf(f,"file %s %lld %ld %s\n", disk->name, file->size, file->mtime, file->sub);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing the state file '%s' in fprintf(). %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* for each block */
|
||||
for(k=0;k<file->blockmax;++k) {
|
||||
struct snapraid_block* block = &file->blockvec[k];
|
||||
|
||||
if (block->is_hashed) {
|
||||
char s_hash[HASH_MAX*2+1];
|
||||
strenchex(s_hash, block->hash, HASH_MAX);
|
||||
s_hash[HASH_MAX*2] = 0;
|
||||
ret = fprintf(f, "blk %u %s\n", block->parity_pos, s_hash);
|
||||
} else {
|
||||
ret = fprintf(f, "blk %u\n", block->parity_pos);
|
||||
}
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing the state file '%s' in fprintf(). %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
++count_block;
|
||||
}
|
||||
|
||||
++count_file;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
|
||||
/* than even in a system crash event we have one valid copy of the file. */
|
||||
|
||||
if (fflush(f) != 0) {
|
||||
fprintf(stderr, "Error writing the state file '%s', in fflush(). %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (fsync(fileno(f)) != 0) {
|
||||
fprintf(stderr, "Error writing the state file '%s' in fsync(). %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (fclose(f) != 0) {
|
||||
fprintf(stderr, "Error writing the state file '%s' in close(). %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (rename(path, state->content) != 0) {
|
||||
fprintf(stderr, "Error renaming the state file '%s' to '%s' in rename(). %s.\n", path, state->content, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (state->verbose) {
|
||||
printf("\tfile %u\n", count_file);
|
||||
printf("\tblock %u\n", count_block);
|
||||
}
|
||||
}
|
||||
|
||||
#define PROGRESS_CLEAR " "
|
||||
|
||||
int state_progress(time_t* start, time_t* last, pos_t blockpos, pos_t blockmax, uint64_t count_block, uint64_t count_size)
|
||||
{
|
||||
time_t now;
|
||||
|
||||
now = time(0);
|
||||
|
||||
if (*last != now) {
|
||||
time_t delta = now - *start;
|
||||
|
||||
printf("%u%%, %u MB", blockpos * 100 / blockmax, (unsigned)(count_size / (1024*1024)));
|
||||
|
||||
if (delta != 0) {
|
||||
printf(", %u MB/s", (unsigned)(count_size / (1024*1024) / delta));
|
||||
}
|
||||
|
||||
if (delta > 5 && count_block > 0) {
|
||||
unsigned m, h;
|
||||
uint64_t todo = blockmax - blockpos;
|
||||
|
||||
m = todo * delta / (60 * count_block);
|
||||
|
||||
h = m / 60;
|
||||
m = m % 60;
|
||||
|
||||
printf(", %u:%02u ETA%s", h, m, PROGRESS_CLEAR);
|
||||
}
|
||||
printf("\r");
|
||||
fflush(stdout);
|
||||
*last = now;
|
||||
}
|
||||
|
||||
|
||||
/* stop if requested */
|
||||
if (global_interrupt) {
|
||||
printf("\rStopping for interruption%s\n", PROGRESS_CLEAR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
71
state.h
Normal file
71
state.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __STATE_H
|
||||
#define __STATE_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* state */
|
||||
|
||||
/**
|
||||
* Global variable to identify if Ctrl+C is pressed.
|
||||
*/
|
||||
extern volatile int global_interrupt;
|
||||
|
||||
struct snapraid_state {
|
||||
int verbose; /**< Verbose output. */
|
||||
int force; /**< Forced dangerous operations. */
|
||||
unsigned 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. */
|
||||
};
|
||||
|
||||
void state_init(struct snapraid_state* state);
|
||||
|
||||
void state_done(struct snapraid_state* state);
|
||||
|
||||
void state_config(struct snapraid_state* state, const char* path, int verbose, int force);
|
||||
|
||||
/**
|
||||
* Read the state.
|
||||
*/
|
||||
void state_read(struct snapraid_state* state);
|
||||
|
||||
/**
|
||||
* Write the new state.
|
||||
*/
|
||||
void state_write(struct snapraid_state* state);
|
||||
|
||||
/**
|
||||
* Update the internal state to represent the new state.
|
||||
*/
|
||||
void state_scan(struct snapraid_state* state);
|
||||
|
||||
/**
|
||||
* Sync.
|
||||
*/
|
||||
void state_sync(struct snapraid_state* state, pos_t blockstart);
|
||||
|
||||
/**
|
||||
* Check.
|
||||
*/
|
||||
void state_check(struct snapraid_state* state, int fix, pos_t blockstart);
|
||||
|
||||
int state_progress(time_t* start, time_t* last, pos_t blockpos, pos_t blockmax, uint64_t count_block, uint64_t count_size);
|
||||
|
||||
#endif
|
||||
164
sync.c
Normal file
164
sync.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
#include "state.h"
|
||||
#include "parity.h"
|
||||
#include "handle.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* sync */
|
||||
|
||||
static void state_sync_process(struct snapraid_state* state, int parity_f, pos_t blockstart, pos_t blockmax)
|
||||
{
|
||||
struct snapraid_handle* handle;
|
||||
unsigned diskmax = tommy_array_size(&state->diskarr);
|
||||
pos_t i;
|
||||
unsigned j;
|
||||
unsigned char* block_buffer;
|
||||
unsigned char* xor_buffer;
|
||||
uint64_t count_size;
|
||||
uint64_t count_block;
|
||||
time_t start;
|
||||
time_t last;
|
||||
|
||||
block_buffer = malloc_nofail(state->block_size);
|
||||
xor_buffer = malloc_nofail(state->block_size);
|
||||
|
||||
handle = malloc_nofail(diskmax * sizeof(struct snapraid_handle));
|
||||
for(i=0;i<diskmax;++i) {
|
||||
handle[i].disk = tommy_array_get(&state->diskarr, i);
|
||||
handle[i].file = 0;
|
||||
handle[i].f = -1;
|
||||
}
|
||||
|
||||
count_size = 0;
|
||||
count_block = 0;
|
||||
start = time(0);
|
||||
last = start;
|
||||
|
||||
for(i=blockstart;i<blockmax;++i) {
|
||||
int unhashed;
|
||||
|
||||
/* for each disk, search for an unhashed block */
|
||||
unhashed = 0;
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block = disk_block_get(handle[j].disk, i);
|
||||
if (block && !block->is_hashed) {
|
||||
unhashed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unhashed)
|
||||
continue;
|
||||
|
||||
/* start with 0 */
|
||||
memset(xor_buffer, 0, state->block_size);
|
||||
|
||||
/* for each disk, process the block */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
int read_size;
|
||||
struct md5_t md5;
|
||||
unsigned char hash[HASH_MAX];
|
||||
struct snapraid_block* block;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
handle_open(0, &handle[j], block->file);
|
||||
|
||||
read_size = handle_read(0, &handle[j], block, block_buffer, state->block_size);
|
||||
|
||||
/* now compute the hash */
|
||||
md5_init(&md5);
|
||||
md5_update(&md5, block_buffer, read_size);
|
||||
md5_final(&md5, hash);
|
||||
|
||||
if (block->is_hashed) {
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_MAX) != 0) {
|
||||
fprintf(stderr, "%u: Data error for file %s at position %u\n", i, block->file->sub, block_file_pos(block));
|
||||
fprintf(stderr, "Stopping to allow recovery. Try with 'snapraid -s %u fix'\n", i);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* copy the hash */
|
||||
memcpy(block->hash, hash, HASH_MAX);
|
||||
|
||||
/* mark the block as hashed */
|
||||
block->is_hashed = 1;
|
||||
}
|
||||
|
||||
/* compute the parity */
|
||||
memxor(xor_buffer, block_buffer, read_size);
|
||||
|
||||
count_size += read_size;
|
||||
}
|
||||
|
||||
/* write the parity */
|
||||
parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
|
||||
/* count the number of processed block */
|
||||
++count_block;
|
||||
|
||||
/* progress */
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size))
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%u%% completed, %u MB processed\n", i * 100 / blockmax, (unsigned)(count_size / (1024*1024)));
|
||||
|
||||
printf("Done\n");
|
||||
|
||||
for(j=0;j<diskmax;++j) {
|
||||
handle_close(&handle[j]);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
free(block_buffer);
|
||||
free(xor_buffer);
|
||||
}
|
||||
|
||||
void state_sync(struct snapraid_state* state, pos_t blockstart)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
pos_t blockmax;
|
||||
off_t size;
|
||||
int f;
|
||||
|
||||
printf("Syncing...\n");
|
||||
|
||||
blockmax = parity_resize(state);
|
||||
size = blockmax * (off_t)state->block_size;
|
||||
|
||||
if (blockstart >= blockmax) {
|
||||
fprintf(stderr, "The specified starting block %u is bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s", state->parity);
|
||||
f = parity_create(path, size);
|
||||
|
||||
state_sync_process(state, f, blockstart, blockmax);
|
||||
|
||||
parity_close(path, f);
|
||||
}
|
||||
8
tommy.c
8
tommy.c
|
|
@ -17,13 +17,13 @@
|
|||
|
||||
#include "portable.h"
|
||||
|
||||
/* redefine a safe malloc for tommy use */
|
||||
#include "util.h" /* for safe_malloc */
|
||||
|
||||
/* redefine the malloc for tommy use */
|
||||
#define tommy_malloc malloc_nofail
|
||||
#define tommy_free free
|
||||
|
||||
void* malloc_nofail(size_t size);
|
||||
|
||||
/* direct include tommy source */
|
||||
/* include tommy source */
|
||||
#include "tommylist.c"
|
||||
#include "tommyhash.c"
|
||||
#include "tommyhashdyn.c"
|
||||
|
|
|
|||
67
util.c
Normal file
67
util.c
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void* malloc_nofail(size_t size)
|
||||
{
|
||||
void* ptr = malloc(size);
|
||||
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "Low memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void memxor(unsigned char* xor, const unsigned char* block, unsigned size)
|
||||
{
|
||||
while (size >= 16) {
|
||||
uint32_t* xor32 = (uint32_t*)xor;
|
||||
const uint32_t* block32 = (const uint32_t*)block;
|
||||
xor32[0] ^= block32[0];
|
||||
xor32[1] ^= block32[1];
|
||||
xor32[2] ^= block32[2];
|
||||
xor32[3] ^= block32[3];
|
||||
|
||||
xor += 16;
|
||||
block += 16;
|
||||
size -= 16;
|
||||
}
|
||||
|
||||
while (size >= 4) {
|
||||
uint32_t* xor32 = (uint32_t*)xor;
|
||||
const uint32_t* block32 = (const uint32_t*)block;
|
||||
xor32[0] ^= block32[0];
|
||||
|
||||
xor += 4;
|
||||
block += 4;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
while (size != 0) {
|
||||
xor[0] ^= block[0];
|
||||
|
||||
xor += 1;
|
||||
block += 1;
|
||||
size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
19
util.h
19
util.h
|
|
@ -15,6 +15,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_H
|
||||
#define __UTIL_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* string */
|
||||
|
||||
|
|
@ -168,3 +171,19 @@ static inline char* strdechex(void* void_data, unsigned data_len, char* str)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* memory */
|
||||
|
||||
/**
|
||||
* Safe malloc.
|
||||
* If no memory is available, it aborts.
|
||||
*/
|
||||
void* malloc_nofail(size_t size);
|
||||
|
||||
/**
|
||||
* Xor two memory blocks.
|
||||
*/
|
||||
void memxor(unsigned char* xor, const unsigned char* block, unsigned size);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue