Improve crash resilience in Linux

Syncing the directory is a required step to ensure resilience.
Also create the temporary file with O_EXCL to ensure to don't
use a stale copy that may link to the original copy.
This commit is contained in:
Andrea Mazzoleni 2018-06-04 22:14:23 +02:00
parent c696c56df1
commit 473819467f
5 changed files with 87 additions and 6 deletions

View file

@ -1,12 +1,13 @@
SnapRAID HISTORY
================
11.3 2018/01 (WIP)
11.3 2018/06 (WIP)
==================
* Fixed handing of Linux devices that have multiple slaves. This affects
the smart/list/devices/down commands [Valentin Hilbig].
* The 'list' command in verbose mode prints the full nanosecond
timestamp precision.
* After writing content files also sync their directory.
11.2 2017/12
============

View file

@ -41,6 +41,7 @@ void test(void)
s = sopen_multi_write(STREAM_MAX);
for (i = 0; i < STREAM_MAX; ++i) {
snprintf(file, sizeof(file), "stream%u.bin", i);
remove(file);
if (sopen_multi_file(s, i, file) != 0) {
/* LCOV_EXCL_START */
exit(EXIT_FAILURE);

View file

@ -3434,10 +3434,21 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
msg_progress("Saving state to %s...\n", content->content);
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
/* ensure to delete a previous stale file */
if (remove(tmp) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
f = sopen_write(tmp);
if (f == 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the content file '%s'. %s.\n", tmp, strerror(errno));
log_fatal("Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
@ -3562,12 +3573,24 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
/* ensure to delete a previous stale file */
if (remove(tmp) != 0) {
if (errno != ENOENT) {
/* LCOV_EXCL_START */
log_fatal("Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
if (sopen_multi_file(f, k, tmp) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the content file '%s'. %s.\n", tmp, strerror(errno));
log_fatal("Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
++k;
i = i->next;
}
@ -3851,11 +3874,10 @@ static void state_verify_content(struct snapraid_state* state, uint32_t crc)
msg_progress("Verifying %s...\n", content->content);
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
f = sopen_read(tmp);
if (f == 0) {
/* LCOV_EXCL_START */
log_fatal("Error reopening the content file '%s'. %s.\n", tmp, strerror(errno));
log_fatal("Error reopening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
@ -3924,6 +3946,59 @@ static void state_rename_content(struct snapraid_state* state)
{
tommy_node* i;
#if defined(_linux) /* this sequence is linux specific */
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
char tmp[PATH_MAX];
char dir[PATH_MAX];
char* slash;
int handle;
pathcpy(dir, sizeof(dir), content->content);
slash = strrchr(tmp, '/');
if (slash)
*slash = 0;
else
pathcpy(dir, sizeof(dir), ".");
/* open the directory to get the handle */
handle = open(dir, O_RDONLY | O_DIRECTORY);
if (handle < 0) {
/* LCOV_EXCL_START */
log_fatal("Error opening the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* now rename the just written copy with the correct name */
pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
if (rename(tmp, content->content) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error renaming the content file '%s' to '%s'. %s.\n", tmp, content->content, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* sync the directory */
if (fsync(handle) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error syncing the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (close(handle) != 0) {
/* LCOV_EXCL_START */
log_fatal("Error closing the directory '%s'. %s.\n", dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
i = i->next;
}
#else
i = tommy_list_head(&state->contentlist);
while (i) {
struct snapraid_content* content = i->data;
@ -3940,6 +4015,7 @@ static void state_rename_content(struct snapraid_state* state)
i = i->next;
}
#endif
}
void state_write(struct snapraid_state* state)

View file

@ -112,7 +112,8 @@ int sopen_multi_file(STREAM* s, unsigned i, const char* file)
pathcpy(s->handle[i].path, sizeof(s->handle[i].path), file);
f = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_SEQUENTIAL, 0600);
/* O_EXCL to be resilent ensure to always create a new file and not use a stale link to the original file */
f = open(file, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_SEQUENTIAL, 0600);
if (f == -1) {
/* LCOV_EXCL_START */
return -1;

View file

@ -299,6 +299,8 @@ void pathslash(char* dst, size_t size);
/**
* Cut everything after the latest slash.
*
* If the string doesn't contain any slash, it returns the empty string.
*/
void pathcut(char* dst);