diff --git a/HISTORY b/HISTORY index 2d65d20a..38f0c3e6 100644 --- a/HISTORY +++ b/HISTORY @@ -26,6 +26,7 @@ SnapRAID HISTORY when you replace one disk with another copying manually the files. * If 'fix' cannot recover a file, it's renamed adding the '.unrecoverable' extension. +* Checking and fixing also empty files with size 0. 1.6 2011/9 - THIS UPDATE IS HIGHLY RECOMMENDED! =============================================== diff --git a/TODO b/TODO index 2caf2353..5701ad95 100644 --- a/TODO +++ b/TODO @@ -3,20 +3,17 @@ SnapRAID TODO This is the list of TODO items for SnapRAID. -- v1.7 - -* When scanning, ensure that each file is in the expected disk. - -* Check and fix also empty files. - -* Put a note about configuring readahead size in Linux to improve performance. -Check https://sourceforge.net/projects/snapraid/forums/forum/1677233/topic/4747452/index/page/1 - - v1.8 +* In fix an existing symlink with the same name of a file to be recovered may stop +the process making the create() operation to fail. + * Check why saving the content files is so slow. fprintf() is the bottleneck. -* Save all the content files in parallel using multiple threads. +* Save all the content files in parallel. We can use a single thread writing +the same stuffs to all the file descriptors. +This is still safe, as the written data is going to replace the old one +with an atomic rename operation. * Put the content files in the data disks in the default configuration file. diff --git a/check.c b/check.c index d8338b99..fa405d90 100644 --- a/check.c +++ b/check.c @@ -624,6 +624,95 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna } } + /* check all the files with empty size */ + /* they have no block, and so skipped by previous checks */ + for(i=0;ifilelist; + while (node) { + char path[PATH_MAX]; + struct stat st; + struct snapraid_file* file; + int failed = 0; + + file = node->data; + node = node->next; /* next node */ + + /* if not empty, it's already checked and continue to the next one */ + if (file->size != 0) { + continue; + } + + /* if excluded continue to the next one */ + if (file_flag_has(file, FILE_IS_EXCLUDED)) { + continue; + } + + /* stat the file */ + pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub); + ret = stat(path, &st); + if (ret == -1) { + failed = 1; + + fprintf(stderr, "Error stating empty file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "error:%s:%s: Empty file stat error\n", disk->name, file->sub); + ++error; + } else if (!S_ISREG(st.st_mode)) { + failed = 1; + + fprintf(stderr, "error:%s:%s: Empty file error for not regular file\n", disk->name, file->sub); + ++error; + } else if (st.st_size != 0) { + failed = 1; + + fprintf(stderr, "error:%s:%s: Empty file error for size '%"PRIu64"'\n", disk->name, file->sub, st.st_size); + ++error; + } + + if (fix && failed) { + int f; + + /* create the ancestor directories */ + ret = handle_ancestor(path); + if (ret != 0) { + fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n"); + printf("Stopping\n"); + ++unrecoverable_error; + goto bail; + } + + /* create it */ + /* do not follow links to ensure to open the real file */ + f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600); + if (f == -1) { + fprintf(stderr, "Error creating empty file '%s'. %s.\n", path, strerror(errno)); + if (errno == EACCES) { + fprintf(stderr, "WARNING! Please give write permission to the file.\n"); + } else { + /* we do not use DANGER because it could be ENOSPC which is not always correctly reported */ + fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n"); + } + printf("Stopping\n"); + ++unrecoverable_error; + goto bail; + } + + /* close it */ + close(f); + + fprintf(stderr, "fixed:%s:%s: Fixed empty file\n", disk->name, file->sub); + ++recovered_error; + } + } + } + /* check all the links */ for(i=0;i