Blame SOURCES/redis-config.patch

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