From 14916a82e23c22e1f3d3ebbd90421eb747480e99 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 Feb 2020 23:01:48 +0100 Subject: [PATCH] altsvc: make saving the cache an atomic operation ... by writing the file to temp name then rename to the final when done. Assisted-by: Jay Satiro Fixes #4936 Closes #4942 --- lib/altsvc.c | 45 +++++++++++++++++++++++++++++++------------ lib/altsvc.h | 9 +++++---- lib/url.c | 2 +- tests/unit/unit1654.c | 4 ++-- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index c05562985..8066302ae 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -34,6 +34,8 @@ #include "parsedate.h" #include "sendf.h" #include "warnless.h" +#include "rand.h" +#include "rename.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -317,12 +319,15 @@ void Curl_altsvc_cleanup(struct altsvcinfo *altsvc) /* * Curl_altsvc_save() writes the altsvc cache to a file. */ -CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file) +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *altsvc, const char *file) { struct curl_llist_element *e; struct curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; + char *tempstore; + unsigned char randsuffix[9]; if(!altsvc) /* no cache activated */ @@ -335,20 +340,36 @@ CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file) if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) /* marked as read-only, no file or zero length file name */ return CURLE_OK; - out = fopen(file, FOPEN_WRITETEXT); + + 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) - return CURLE_WRITE_ERROR; - fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" - "# This file was generated by libcurl! Edit at your own risk.\n", - out); - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - result = altsvc_out(as, out); + result = CURLE_WRITE_ERROR; + else { + fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n", + out); + for(e = altsvc->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + result = altsvc_out(as, out); + if(result) + break; + } + fclose(out); + if(!result && Curl_rename(tempstore, file)) + result = CURLE_WRITE_ERROR; + if(result) - break; + unlink(tempstore); } - fclose(out); + free(tempstore); return result; } diff --git a/lib/altsvc.h b/lib/altsvc.h index 99d0499af..248e71eef 100644 --- a/lib/altsvc.h +++ b/lib/altsvc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,7 +59,8 @@ 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 altsvcinfo *asi, const char *file); +CURLcode Curl_altsvc_save(struct Curl_easy *data, + 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, @@ -70,9 +71,9 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, struct altsvc **dstentry, - int versions); /* one or more CURLALTSVC_H* bits */ + const int versions); /* CURLALTSVC_H* bits */ #else /* disabled */ -#define Curl_altsvc_save(a,b) +#define Curl_altsvc_save(a,b,c) #endif /* CURL_DISABLE_HTTP || USE_ALTSVC */ #endif /* HEADER_CURL_ALTSVC_H */ diff --git a/lib/url.c b/lib/url.c index f3b26dafa..d6adf7177 100644 --- a/lib/url.c +++ b/lib/url.c @@ -380,7 +380,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->state.ulbuf); Curl_flush_cookies(data, TRUE); #ifdef USE_ALTSVC - Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]); + Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(data->asi); data->asi = NULL; #endif diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index a800d9c3a..d05d0b214 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -124,7 +124,7 @@ UNITTEST_START } fail_unless(asi->num == 10, "wrong number of entries"); - Curl_altsvc_save(asi, outname); + Curl_altsvc_save(curl, asi, outname); curl_easy_cleanup(curl); fail: