Blame SOURCES/redis-config.patch

e5c076
Revert to 6.0.8 behavior to save configuration file
e5c076
to fix "CONFIG REWRITE" when using /etc/redis.conf
e5c076
as new behavior expect a writable directory
e5c076
e5c076
Revert: 90555566ed5cbd3e1c3df1293ba3bbf6098e34c3
e5c076
e5c076
See discussion about this breaking change in
e5c076
https://github.com/redis/redis/issues/8051
e5c076
e5c076
--- redis-6.0.9/src/config.c	2020-10-27 08:12:01.000000000 +0100
e5c076
+++ redis-6.0.8/src/config.c	2020-09-10 13:09:00.000000000 +0200
e5c076
@@ -1568,62 +1568,60 @@
e5c076
     dictReleaseIterator(di);
e5c076
 }
e5c076
 
e5c076
-/* This function replaces the old configuration file with the new content
e5c076
- * in an atomic manner.
e5c076
+/* This function overwrites the old configuration file with the new content.
e5c076
+ *
e5c076
+ * 1) The old file length is obtained.
e5c076
+ * 2) If the new content is smaller, padding is added.
e5c076
+ * 3) A single write(2) call is used to replace the content of the file.
e5c076
+ * 4) Later the file is truncated to the length of the new content.
e5c076
+ *
e5c076
+ * This way we are sure the file is left in a consistent state even if the
e5c076
+ * process is stopped between any of the four operations.
e5c076
  *
e5c076
  * The function returns 0 on success, otherwise -1 is returned and errno
e5c076
- * is set accordingly. */
e5c076
+ * set accordingly. */
e5c076
 int rewriteConfigOverwriteFile(char *configfile, sds content) {
e5c076
-    int fd = -1;
e5c076
-    int retval = -1;
e5c076
-    char tmp_conffile[PATH_MAX];
e5c076
-    const char *tmp_suffix = ".XXXXXX";
e5c076
-    size_t offset = 0;
e5c076
-    ssize_t written_bytes = 0;
e5c076
-
e5c076
-    int tmp_path_len = snprintf(tmp_conffile, sizeof(tmp_conffile), "%s%s", configfile, tmp_suffix);
e5c076
-    if (tmp_path_len <= 0 || (unsigned int)tmp_path_len >= sizeof(tmp_conffile)) {
e5c076
-        serverLog(LL_WARNING, "Config file full path is too long");
e5c076
-        errno = ENAMETOOLONG;
e5c076
-        return retval;
e5c076
-    }
e5c076
-
e5c076
-#ifdef _GNU_SOURCE
e5c076
-    fd = mkostemp(tmp_conffile, O_CLOEXEC);
e5c076
-#else
e5c076
-    /* There's a theoretical chance here to leak the FD if a module thread forks & execv in the middle */
e5c076
-    fd = mkstemp(tmp_conffile);
e5c076
-#endif
e5c076
-
e5c076
-    if (fd == -1) {
e5c076
-        serverLog(LL_WARNING, "Could not create tmp config file (%s)", strerror(errno));
e5c076
-        return retval;
e5c076
-    }
e5c076
-
e5c076
-    while (offset < sdslen(content)) {
e5c076
-         written_bytes = write(fd, content + offset, sdslen(content) - offset);
e5c076
-         if (written_bytes <= 0) {
e5c076
-             if (errno == EINTR) continue; /* FD is blocking, no other retryable errors */
e5c076
-             serverLog(LL_WARNING, "Failed after writing (%zd) bytes to tmp config file (%s)", offset, strerror(errno));
e5c076
-             goto cleanup;
e5c076
-         }
e5c076
-         offset+=written_bytes;
e5c076
-    }
e5c076
-
e5c076
-    if (fsync(fd))
e5c076
-        serverLog(LL_WARNING, "Could not sync tmp config file to disk (%s)", strerror(errno));
e5c076
-    else if (fchmod(fd, 0644) == -1)
e5c076
-        serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno));
e5c076
-    else if (rename(tmp_conffile, configfile) == -1)
e5c076
-        serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno));
e5c076
-    else {
e5c076
-        retval = 0;
e5c076
-        serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile);
e5c076
+    int retval = 0;
e5c076
+    int fd = open(configfile,O_RDWR|O_CREAT,0644);
e5c076
+    int content_size = sdslen(content), padding = 0;
e5c076
+    struct stat sb;
e5c076
+    sds content_padded;
e5c076
+
e5c076
+    /* 1) Open the old file (or create a new one if it does not
e5c076
+     *    exist), get the size. */
e5c076
+    if (fd == -1) return -1; /* errno set by open(). */
e5c076
+    if (fstat(fd,&sb) == -1) {
e5c076
+        close(fd);
e5c076
+        return -1; /* errno set by fstat(). */
e5c076
+    }
e5c076
+
e5c076
+    /* 2) Pad the content at least match the old file size. */
e5c076
+    content_padded = sdsdup(content);
e5c076
+    if (content_size < sb.st_size) {
e5c076
+        /* If the old file was bigger, pad the content with
e5c076
+         * a newline plus as many "#" chars as required. */
e5c076
+        padding = sb.st_size - content_size;
e5c076
+        content_padded = sdsgrowzero(content_padded,sb.st_size);
e5c076
+        content_padded[content_size] = '\n';
e5c076
+        memset(content_padded+content_size+1,'#',padding-1);
e5c076
+    }
e5c076
+
e5c076
+    /* 3) Write the new content using a single write(2). */
e5c076
+    if (write(fd,content_padded,strlen(content_padded)) == -1) {
e5c076
+        retval = -1;
e5c076
+        goto cleanup;
e5c076
+    }
e5c076
+
e5c076
+    /* 4) Truncate the file to the right length if we used padding. */
e5c076
+    if (padding) {
e5c076
+        if (ftruncate(fd,content_size) == -1) {
e5c076
+            /* Non critical error... */
e5c076
+        }
e5c076
     }
e5c076
 
e5c076
 cleanup:
e5c076
+    sdsfree(content_padded);
e5c076
     close(fd);
e5c076
-    if (retval) unlink(tmp_conffile);
e5c076
     return retval;
e5c076
 }
e5c076
 
e5c076