mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
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:
parent
c696c56df1
commit
473819467f
5 changed files with 87 additions and 6 deletions
3
HISTORY
3
HISTORY
|
|
@ -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
|
||||
============
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue