Add the fix command

Split in various files
Add --force switch
Add --start switch
This commit is contained in:
Andrea Mazzoleni 2011-03-04 20:59:21 +01:00
parent 8e953adb27
commit 4bd4f9dd79
16 changed files with 2335 additions and 1532 deletions

316
check.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

490
state.c Normal file
View 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
View 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
View 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);
}

View file

@ -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
View 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
View file

@ -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