Blame SOURCES/redis-config.patch

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