|
|
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 |
|