diff --git a/cmdline/check.c b/cmdline/check.c index d91b09cb..f9f5d826 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -803,17 +803,6 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - /* check if the file was larger and now truncated */ - if (handle[j].truncated != 0) { - fprintf(stdlog, "File '%s' is larger than expected.\n", handle[j].path); - fprintf(stdlog, "error:%u:%s:%s: Size error\n", i, disk->name, file->sub); - ++error; - - /* this is already a recovered error */ - fprintf(stdlog, "fixed:%u:%s:%s: Fixed size\n", i, disk->name, file->sub); - ++recovered_error; - } - /* check if the file was just created */ if (handle[j].created != 0) { /* if fragmented, it may be reopened, so remember that the file */ @@ -836,19 +825,35 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna ++error; continue; } + } - /* check if it's a larger file, but not if already notified */ - if (!file_flag_has(file, FILE_IS_LARGER) - && handle[j].st.st_size > file->size - ) { - fprintf(stdlog, "File '%s' is larger than expected.\n", handle[j].path); - fprintf(stdlog, "error:%u:%s:%s: Size error\n", i, disk->name, file->sub); - ++error; + /* check if it's a larger file, but not if already notified or excluded */ + if (!file_flag_has(file, FILE_IS_LARGER) + && !file_flag_has(file, FILE_IS_EXCLUDED) + && handle[j].st.st_size > file->size + ) { + fprintf(stdlog, "File '%s' is larger than expected.\n", handle[j].path); + fprintf(stdlog, "error:%u:%s:%s: Size error\n", i, disk->name, file->sub); + ++error; - /* if fragmented, it may be reopened, so store the notification */ - /* to prevent to signal and count the error more than one time */ - file_flag_set(file, FILE_IS_LARGER); + if (fix) { + ret = handle_truncate(&handle[j], file); + if (ret == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "DANGER! Unexpected truncate error in a data disk, it isn't possible to fix.\n"); + printf("Stopping at block %u\n", i); + ++unrecoverable_error; + goto bail; + /* LCOV_EXCL_STOP */ + } + + fprintf(stdlog, "fixed:%u:%s:%s: Fixed size\n", i, disk->name, file->sub); + ++recovered_error; } + + /* if fragmented, it may be reopened, so store the notification */ + /* to prevent to report and/or truncate it again */ + file_flag_set(file, FILE_IS_LARGER); } } diff --git a/cmdline/handle.c b/cmdline/handle.c index 70076c8b..3ebea4e6 100644 --- a/cmdline/handle.c +++ b/cmdline/handle.c @@ -44,7 +44,6 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, in } /* initial values, changed later if required */ - handle->truncated = 0; handle->created = 0; /* flags for opening */ @@ -111,29 +110,6 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, in /* get the size of the existing data */ handle->valid_size = handle->st.st_size; - /* Here we only truncate the file and we don't grow it */ - /* as allocating real space for it would imply a too big performance drop, */ - /* and allocating sparse space wouldn't improve the safety of subsequent writes. */ - if (handle->st.st_size > file->size) { - ret = ftruncate(handle->f, file->size); - if (ret != 0) { - /* LCOV_EXCL_START */ - if (errno == EACCES) { - fprintf(stderr, "Failed to truncate file '%s' for missing write permission.\n", handle->path); - } else { - fprintf(stderr, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno)); - } - return -1; - /* LCOV_EXCL_STOP */ - } - - /* mark it as truncated */ - handle->truncated = 1; - - /* adjust the size to the truncated size */ - handle->valid_size = file->size; - } - #if HAVE_POSIX_FADVISE if ((mode & MODE_SEQUENTIAL) != 0) { /* advise sequential access */ @@ -150,6 +126,28 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, in return 0; } +int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file) +{ + int ret; + + ret = ftruncate(handle->f, file->size); + if (ret != 0) { + /* LCOV_EXCL_START */ + if (errno == EACCES) { + fprintf(stderr, "Failed to truncate file '%s' for missing write permission.\n", handle->path); + } else { + fprintf(stderr, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno)); + } + return -1; + /* LCOV_EXCL_STOP */ + } + + /* adjust the size to the truncated size */ + handle->valid_size = file->size; + + return 0; +} + int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, FILE* out) { int ret; @@ -162,8 +160,7 @@ int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub); - /* for sure neither created and truncated */ - handle->truncated = 0; + /* for sure not created */ handle->created = 0; /* flags for opening */ diff --git a/cmdline/handle.h b/cmdline/handle.h index 48279489..379b3d80 100644 --- a/cmdline/handle.h +++ b/cmdline/handle.h @@ -31,21 +31,23 @@ struct snapraid_handle { struct stat st; /**< Stat info of the opened file. */ data_off_t valid_size; /**< Size of the valid data. */ int created; /**< If the file was created, otherwise it was already existing. */ - int truncated; /**< If the file was truncated. */ }; /** * Creates a file. * The file is created if missing, and opened with write access. - * If the file has a different size than expected, it's enlarged or truncated to the correct size. * If the file is created, the handle->created is set. - * If the file is truncated, the handle->truncated is set. * The initial size of the file is stored in the file->st struct. * If the file cannot be opened for write access, it's opened with read-only access. * The read-only access works only if the file has already the correct size and doesn't need to be modified. */ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode); +/** + * Truncate a file if required. + */ +int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file); + /** * Opens a file. * The file is opened for reading.