chiark / gitweb /
"Don't fear the fsync()"
authorAlan Jenkins <alan.christopher.jenkins@gmail.com>
Thu, 17 Aug 2017 16:09:44 +0000 (17:09 +0100)
committerSven Eden <yamakuzure@gmx.net>
Tue, 29 Aug 2017 14:57:55 +0000 (16:57 +0200)
For files which are vital to boot

1. Avoid opening any window where power loss will zero them out or worse.
   I know app developers all coded to the ext3 implementation, but
   the only formal documentation we have says we're broken if we actually
   rely on it.  E.g.

   * `man mount`, search for `auto_da_alloc`.
   * http://www.linux-mtd.infradead.org/faq/ubifs.html#L_atomic_change
   * https://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/

2. If we tell the kernel we're interested in writing them to disk, it will
   tell us if that fails.  So at minimum, this means we play our part in
   notifying the user about errors.

I refactored error-handling in `udevadm-hwdb` a little.  It turns out I did
exactly the same as had already been done in the `elogind-hwdb` version,
i.e. commit d702dcd.

src/basic/fileio.c
src/basic/fileio.h

index 7edc31d0aff2bbbdc75413a4d529fe2830fec3d4..610a88f2e50fae118076e8203722200e9762a43d 100644 (file)
@@ -70,7 +70,7 @@ int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, stru
         return fflush_and_check(f);
 }
 
-static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
+static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline, bool sync) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *p = NULL;
         int r;
@@ -85,6 +85,9 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor
         (void) fchmod_umask(fileno(f), 0644);
 
         r = write_string_stream(f, line, enforce_newline);
+        if (r >= 0 && sync)
+                r = fflush_sync_and_check(f);
+
         if (r >= 0) {
                 if (rename(p, fn) < 0)
                         r = -errno;
@@ -103,10 +106,14 @@ int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags
         assert(fn);
         assert(line);
 
+        /* We don't know how to verify whether the file contents is on-disk. */
+        assert(!((flags & WRITE_STRING_FILE_SYNC) && (flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE)));
+
         if (flags & WRITE_STRING_FILE_ATOMIC) {
                 assert(flags & WRITE_STRING_FILE_CREATE);
 
-                r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+                r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE),
+                                                       flags & WRITE_STRING_FILE_SYNC);
                 if (r < 0)
                         goto fail;
 
@@ -143,6 +150,12 @@ int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags
         if (r < 0)
                 goto fail;
 
+        if (flags & WRITE_STRING_FILE_SYNC) {
+                r = fflush_sync_and_check(f);
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 
 fail:
@@ -1131,6 +1144,21 @@ int fflush_and_check(FILE *f) {
         return 0;
 }
 
+int fflush_sync_and_check(FILE *f) {
+        int r;
+
+        assert(f);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return r;
+
+        if (fsync(fileno(f)) < 0)
+                return -errno;
+
+        return 0;
+}
+
 /* This is much like mkostemp() but is subject to umask(). */
 int mkostemp_safe(char *pattern) {
         _cleanup_umask_ mode_t u = 0;
index fa223fdf52d021ace998f9be72c16e43ae45a244..f76c3243e39b246927bcb0ccdf7e85a9fc32d52f 100644 (file)
 #include "time-util.h"
 
 typedef enum {
-        WRITE_STRING_FILE_CREATE = 1,
-        WRITE_STRING_FILE_ATOMIC = 2,
-        WRITE_STRING_FILE_AVOID_NEWLINE = 4,
-        WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8,
+        WRITE_STRING_FILE_CREATE = 1<<0,
+        WRITE_STRING_FILE_ATOMIC = 1<<1,
+        WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2,
+        WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3,
+        WRITE_STRING_FILE_SYNC = 1<<4,
 } WriteStringFileFlags;
 
 int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts);
@@ -83,6 +84,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
                 } else
 
 int fflush_and_check(FILE *f);
+int fflush_sync_and_check(FILE *f);
 
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
 int mkostemp_safe(char *pattern);