summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2021-05-06 16:42:04 +0200
committerDaniel Stenberg <daniel@haxx.se>2021-05-06 16:42:09 +0200
commit1f831123757f092ef1aef6317a1f7d7e5604cc09 (patch)
treea04828297d9c2a4745d550aaa0740cb75a662f8d /lib
parent577f19397c3545da7b0162677e2783d188e74ae1 (diff)
downloadcurl-bagder/lock-output-files.tar.gz
lib: when saving files, use advisory locksbagder/lock-output-files
... instead of saving into a temp name and rename. This makes them work properly when the output is a character device. Applies to cookies, alt-svc and hsts files. Makes Curl_rename superfluous and therefore removed. Reported-by: rofl0r on github Assisted-by: Paul Vixie Fixes #6882 Closes #6884
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.inc4
-rw-r--r--lib/altsvc.c38
-rw-r--r--lib/altsvc.h7
-rw-r--r--lib/cookie.c39
-rw-r--r--lib/curl_config.h.cmake3
-rw-r--r--lib/hsts.c34
-rw-r--r--lib/openlock.c108
-rw-r--r--lib/openlock.h (renamed from lib/rename.h)17
-rw-r--r--lib/rename.c71
-rw-r--r--lib/url.c2
10 files changed, 159 insertions, 164 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 3e9ddec12..fbd1426f6 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -173,13 +173,13 @@ LIB_CFILES = \
non-ascii.c \
nonblock.c \
openldap.c \
+ openlock.c \
parsedate.c \
pingpong.c \
pop3.c \
progress.c \
psl.c \
rand.c \
- rename.c \
rtsp.c \
select.c \
sendf.c \
@@ -293,6 +293,7 @@ LIB_HFILES = \
netrc.h \
non-ascii.h \
nonblock.h \
+ openlock.h \
parsedate.h \
pingpong.h \
pop3.h \
@@ -300,7 +301,6 @@ LIB_HFILES = \
psl.h \
quic.h \
rand.h \
- rename.h \
rtsp.h \
select.h \
sendf.h \
diff --git a/lib/altsvc.c b/lib/altsvc.c
index 4ab77fdfc..610969a97 100644
--- a/lib/altsvc.c
+++ b/lib/altsvc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -34,8 +34,7 @@
#include "parsedate.h"
#include "sendf.h"
#include "warnless.h"
-#include "rand.h"
-#include "rename.h"
+#include "openlock.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -322,15 +321,12 @@ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
/*
* Curl_altsvc_save() writes the altsvc cache to a file.
*/
-CURLcode Curl_altsvc_save(struct Curl_easy *data,
- struct altsvcinfo *altsvc, const char *file)
+CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file)
{
struct Curl_llist_element *e;
struct Curl_llist_element *n;
+ struct openlock o;
CURLcode result = CURLE_OK;
- FILE *out;
- char *tempstore;
- unsigned char randsuffix[9];
if(!altsvc)
/* no cache activated */
@@ -344,35 +340,23 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
/* marked as read-only, no file or zero length file name */
return CURLE_OK;
- if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
- return CURLE_FAILED_INIT;
-
- tempstore = aprintf("%s.%s.tmp", file, randsuffix);
- if(!tempstore)
- return CURLE_OUT_OF_MEMORY;
-
- out = fopen(tempstore, FOPEN_WRITETEXT);
- if(!out)
- result = CURLE_WRITE_ERROR;
+ result = Curl_openlock(file, &o);
+ if(result)
+ goto error;
else {
fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n",
- out);
+ o.out);
for(e = altsvc->list.head; e; e = n) {
struct altsvc *as = e->ptr;
n = e->next;
- result = altsvc_out(as, out);
+ result = altsvc_out(as, o.out);
if(result)
break;
}
- fclose(out);
- if(!result && Curl_rename(tempstore, file))
- result = CURLE_WRITE_ERROR;
-
- if(result)
- unlink(tempstore);
}
- free(tempstore);
+ error:
+ Curl_openunlock(&o);
return result;
}
diff --git a/lib/altsvc.h b/lib/altsvc.h
index 2ab89e705..c17610167 100644
--- a/lib/altsvc.h
+++ b/lib/altsvc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -58,8 +58,7 @@ struct altsvcinfo {
const char *Curl_alpnid2str(enum alpnid id);
struct altsvcinfo *Curl_altsvc_init(void);
CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file);
-CURLcode Curl_altsvc_save(struct Curl_easy *data,
- struct altsvcinfo *asi, const char *file);
+CURLcode Curl_altsvc_save(struct altsvcinfo *asi, const char *file);
CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl);
void Curl_altsvc_cleanup(struct altsvcinfo **altsvc);
CURLcode Curl_altsvc_parse(struct Curl_easy *data,
@@ -73,7 +72,7 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi,
const int versions); /* CURLALTSVC_H* bits */
#else
/* disabled */
-#define Curl_altsvc_save(a,b,c)
+#define Curl_altsvc_save(a,b)
#define Curl_altsvc_cleanup(x)
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
#endif /* HEADER_CURL_ALTSVC_H */
diff --git a/lib/cookie.c b/lib/cookie.c
index 8831b2a0e..2183d5a19 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -83,7 +83,6 @@ Example set of cookies:
#include "curl_setup.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-
#include "urldata.h"
#include "cookie.h"
#include "psl.h"
@@ -97,8 +96,7 @@ Example set of cookies:
#include "curl_memrchr.h"
#include "inet_pton.h"
#include "parsedate.h"
-#include "rand.h"
-#include "rename.h"
+#include "openlock.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -1515,14 +1513,13 @@ static char *get_netscape_format(const struct Cookie *co)
*
* The function returns non-zero on write failure.
*/
-static int cookie_output(struct Curl_easy *data,
- struct CookieInfo *c, const char *filename)
+static int cookie_output(struct CookieInfo *c, const char *filename)
{
struct Cookie *co;
FILE *out = NULL;
bool use_stdout = FALSE;
- char *tempstore = NULL;
bool error = false;
+ struct openlock o;
if(!c)
/* no cookie engine alive */
@@ -1537,18 +1534,10 @@ static int cookie_output(struct Curl_easy *data,
use_stdout = TRUE;
}
else {
- unsigned char randsuffix[9];
-
- if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
- return 2;
-
- tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
- if(!tempstore)
- return 1;
-
- out = fopen(tempstore, FOPEN_WRITETEXT);
- if(!out)
+ CURLcode result = Curl_openlock(filename, &o);
+ if(result)
goto error;
+ out = o.out;
}
fputs("# Netscape HTTP Cookie File\n"
@@ -1591,22 +1580,12 @@ static int cookie_output(struct Curl_easy *data,
free(array);
}
- if(!use_stdout) {
- fclose(out);
- out = NULL;
- if(Curl_rename(tempstore, filename)) {
- unlink(tempstore);
- goto error;
- }
- }
-
goto cleanup;
error:
error = true;
cleanup:
- if(out && !use_stdout)
- fclose(out);
- free(tempstore);
+ if(!use_stdout)
+ Curl_openunlock(&o);
return error ? 1 : 0;
}
@@ -1665,7 +1644,7 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
/* if we have a destination file for all the cookies to get dumped to */
- if(cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]))
+ if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
infof(data, "WARNING: failed to save cookies in %s\n",
data->set.str[STRING_COOKIEJAR]);
}
diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake
index 1f298871e..c32d2f22d 100644
--- a/lib/curl_config.h.cmake
+++ b/lib/curl_config.h.cmake
@@ -268,6 +268,9 @@
/* Define to 1 if you have the `if_nametoindex' function. */
#cmakedefine HAVE_IF_NAMETOINDEX 1
+/* Define to 1 if you have the `lockf' function. */
+#cmakedefine HAVE_LOCKF 1
+
/* Define to 1 if you have the `getpwuid' function. */
#cmakedefine HAVE_GETPWUID 1
diff --git a/lib/hsts.c b/lib/hsts.c
index ef166f196..5923671e9 100644
--- a/lib/hsts.c
+++ b/lib/hsts.c
@@ -35,9 +35,7 @@
#include "sendf.h"
#include "strtoofft.h"
#include "parsedate.h"
-#include "rand.h"
-#include "rename.h"
-#include "strtoofft.h"
+#include "openlock.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -319,9 +317,8 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
struct Curl_llist_element *e;
struct Curl_llist_element *n;
CURLcode result = CURLE_OK;
- FILE *out;
- char *tempstore;
- unsigned char randsuffix[9];
+ FILE *out = NULL;
+ struct openlock o;
if(!h)
/* no cache activated */
@@ -335,17 +332,9 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
/* marked as read-only, no file or zero length file name */
goto skipsave;
- if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
- return CURLE_FAILED_INIT;
-
- tempstore = aprintf("%s.%s.tmp", file, randsuffix);
- if(!tempstore)
- return CURLE_OUT_OF_MEMORY;
-
- out = fopen(tempstore, FOPEN_WRITETEXT);
- if(!out)
- result = CURLE_WRITE_ERROR;
- else {
+ result = Curl_openlock(file, &o);
+ if(!result) {
+ out = o.out;
fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n",
out);
@@ -356,14 +345,8 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
if(result)
break;
}
- fclose(out);
- if(!result && Curl_rename(tempstore, file))
- result = CURLE_WRITE_ERROR;
-
- if(result)
- unlink(tempstore);
}
- free(tempstore);
+
skipsave:
if(data->set.hsts_write) {
/* if there's a write callback */
@@ -380,6 +363,9 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
i.index++;
}
}
+ if(out)
+ Curl_openunlock(&o);
+
return result;
}
diff --git a/lib/openlock.c b/lib/openlock.c
new file mode 100644
index 000000000..f913ef82f
--- /dev/null
+++ b/lib/openlock.c
@@ -0,0 +1,108 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <curl/curl.h>
+#include "openlock.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_openlock() opens a file for writing text and waits for the adivsory
+ * lock on it.
+ */
+
+#ifndef O_CLOEXEC
+/* this doesn't exist everywhere */
+#define O_CLOEXEC 0
+#endif
+
+CURLcode Curl_openlock(const char *file, struct openlock *o)
+{
+ int fd = -1;
+ FILE *out = NULL;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(o);
+ o->fd = -1;
+ o->out = NULL;
+
+ fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666);
+ if(fd == -1) {
+ result = CURLE_WRITE_ERROR;
+ goto error;
+ }
+
+#ifdef HAVE_LOCKF
+ /* wait for advisory lock on the whole file */
+ if(lockf(fd, F_LOCK, 0)) {
+ result = CURLE_WRITE_ERROR;
+ goto error;
+ }
+#endif
+
+ out = fdopen(fd, FOPEN_WRITETEXT);
+ if(!out)
+ result = CURLE_WRITE_ERROR;
+ else {
+ o->fd = fd;
+ o->out = out;
+ return CURLE_OK;
+ }
+ error:
+ if(out)
+ /* fclose() will close the fd as well after fdopen */
+ fclose(out);
+ else if(fd != -1)
+ close(fd);
+ return result;
+}
+
+/*
+ * Truncate the file at the current position, then unlock and close it.
+ */
+void Curl_openunlock(struct openlock *o)
+{
+#ifdef HAVE_FTRUNCATE
+ long pos = ftell(o->out);
+ fflush(o->out);
+ if(ftruncate(o->fd, (off_t)pos))
+ /* ignoring the return code causes warnings ... */
+ pos = 0;
+#endif
+#ifdef HAVE_LOCKF
+ if(o->fd != -1) {
+ if(lockf(o->fd, F_ULOCK, 0)) {
+ o->fd = -1;
+ }
+ }
+#endif
+ if(o->out)
+ fclose(o->out);
+}
diff --git a/lib/rename.h b/lib/openlock.h
index 534f7471c..b287bead2 100644
--- a/lib/rename.h
+++ b/lib/openlock.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_RENAME_H
-#define HEADER_CURL_RENAME_H
+#ifndef HEADER_CURL_OPENLOCK_H
+#define HEADER_CURL_OPENLOCK_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,7 +21,14 @@
* KIND, either express or implied.
*
***************************************************************************/
+#include "curl_setup.h"
-int Curl_rename(const char *oldpath, const char *newpath);
+struct openlock {
+ int fd;
+ FILE *out;
+};
-#endif /* HEADER_CURL_RENAME_H */
+CURLcode Curl_openlock(const char *file, struct openlock *o);
+void Curl_openunlock(struct openlock *o);
+
+#endif /* HEADER_CURL_OPENLOCK_H */
diff --git a/lib/rename.c b/lib/rename.c
deleted file mode 100644
index f858d4369..000000000
--- a/lib/rename.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "rename.h"
-
-#include "curl_setup.h"
-
-#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \
- !defined(CURL_DISABLE_ALTSVC)
-
-#include "curl_multibyte.h"
-#include "timeval.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* return 0 on success, 1 on error */
-int Curl_rename(const char *oldpath, const char *newpath)
-{
-#ifdef WIN32
- /* rename() on Windows doesn't overwrite, so we can't use it here.
- MoveFileEx() will overwrite and is usually atomic, however it fails
- when there are open handles to the file. */
- const int max_wait_ms = 1000;
- struct curltime start = Curl_now();
- TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath);
- TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath);
- for(;;) {
- timediff_t diff;
- if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) {
- curlx_unicodefree(tchar_oldpath);
- curlx_unicodefree(tchar_newpath);
- break;
- }
- diff = Curl_timediff(Curl_now(), start);
- if(diff < 0 || diff > max_wait_ms) {
- curlx_unicodefree(tchar_oldpath);
- curlx_unicodefree(tchar_newpath);
- return 1;
- }
- Sleep(1);
- }
-#else
- if(rename(oldpath, newpath))
- return 1;
-#endif
- return 0;
-}
-
-#endif
diff --git a/lib/url.c b/lib/url.c
index 74ebb6f91..fd0869e81 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -416,7 +416,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_dyn_free(&data->state.headerb);
Curl_safefree(data->state.ulbuf);
Curl_flush_cookies(data, TRUE);
- Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
+ Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);
Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
Curl_hsts_cleanup(&data->hsts);