mirror of git://git.sysmocom.de/ofono
storage: make write_file transaction-safe
write_file(), as written wasn't transaction-safe; a crash bewtween a file being open and the buffer being written before a safe close would leave the file with a set of undetermined contents. Modified to the file is written to a temporary file name; once completed, it is renamed to the final name. This way, a crash in the middle doesn't leave half-baked files.
This commit is contained in:
parent
c8219725f7
commit
ed8ef7a1e8
|
@ -98,11 +98,21 @@ ssize_t read_file(unsigned char *buffer, size_t len,
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a buffer to a file in a transactionally safe form
|
||||||
|
*
|
||||||
|
* Given a buffer, write it to a file named after
|
||||||
|
* @path_fmt+args. However, to make sure the file contents are
|
||||||
|
* consistent (ie: a crash right after opening or during write()
|
||||||
|
* doesn't leave a file half baked), the contents are written to a
|
||||||
|
* file with a temporary name and when closed, it is renamed to the
|
||||||
|
* specified name (@path_fmt+args).
|
||||||
|
*/
|
||||||
ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode,
|
ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode,
|
||||||
const char *path_fmt, ...)
|
const char *path_fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *path;
|
char *tmp_path, *path;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
@ -110,26 +120,41 @@ ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode,
|
||||||
path = g_strdup_vprintf(path_fmt, ap);
|
path = g_strdup_vprintf(path_fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
if (create_dirs(path, mode | S_IXUSR) != 0) {
|
tmp_path = g_strdup_printf("%s.XXXXXX.tmp", path);
|
||||||
g_free(path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = TFR(open(path, O_WRONLY | O_CREAT | O_TRUNC, mode));
|
r = -1;
|
||||||
if (fd == -1) {
|
if (create_dirs(path, mode | S_IXUSR) != 0)
|
||||||
g_free(path);
|
goto error_create_dirs;
|
||||||
return -1;
|
|
||||||
}
|
fd = TFR(g_mkstemp_full(tmp_path, O_WRONLY | O_CREAT | O_TRUNC, mode));
|
||||||
|
if (fd == -1)
|
||||||
|
goto error_mkstemp_full;
|
||||||
|
|
||||||
r = TFR(write(fd, buffer, len));
|
r = TFR(write(fd, buffer, len));
|
||||||
|
|
||||||
TFR(close(fd));
|
TFR(close(fd));
|
||||||
|
|
||||||
if (r != (ssize_t) len) {
|
if (r != (ssize_t) len) {
|
||||||
unlink(path);
|
|
||||||
r = -1;
|
r = -1;
|
||||||
|
goto error_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that the file contents are written, rename to the real
|
||||||
|
* file name; this way we are uniquely sure that the whole
|
||||||
|
* thing is there.
|
||||||
|
*/
|
||||||
|
unlink(path);
|
||||||
|
|
||||||
|
/* conserve @r's value from 'write' */
|
||||||
|
if (link(tmp_path, path) == -1)
|
||||||
|
r = -1;
|
||||||
|
|
||||||
|
error_write:
|
||||||
|
unlink(tmp_path);
|
||||||
|
error_mkstemp_full:
|
||||||
|
error_create_dirs:
|
||||||
|
g_free(tmp_path);
|
||||||
g_free(path);
|
g_free(path);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue