1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \
28 !defined(CURL_DISABLE_HSTS)
29
30#ifdef HAVE_FCNTL_H
31#include <fcntl.h>
32#endif
33
34#include "urldata.h"
35#include "rand.h"
36#include "fopen.h"
37/* The last 3 #include files should be in this order */
38#include "curl_printf.h"
39#include "curl_memory.h"
40#include "memdebug.h"
41
42/*
43 * Curl_fopen() opens a file for writing with a temp name, to be renamed
44 * to the final name when completed. If there is an existing file using this
45 * name at the time of the open, this function will clone the mode from that
46 * file. if 'tempname' is non-NULL, it needs a rename after the file is
47 * written.
48 */
49CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
50 FILE **fh, char **tempname)
51{
52 CURLcode result = CURLE_WRITE_ERROR;
53 unsigned char randsuffix[9];
54 char *tempstore = NULL;
55 struct_stat sb;
56 int fd = -1;
57 *tempname = NULL;
58
59 *fh = fopen(filename: filename, FOPEN_WRITETEXT);
60 if(!*fh)
61 goto fail;
62 if(fstat(fd: fileno(stream: *fh), buf: &sb) == -1 || !S_ISREG(sb.st_mode))
63 return CURLE_OK;
64 fclose(stream: *fh);
65 *fh = NULL;
66
67 result = Curl_rand_alnum(data, rnd: randsuffix, num: sizeof(randsuffix));
68 if(result)
69 goto fail;
70
71 tempstore = aprintf(format: "%s.%s.tmp", filename, randsuffix);
72 if(!tempstore) {
73 result = CURLE_OUT_OF_MEMORY;
74 goto fail;
75 }
76
77 result = CURLE_WRITE_ERROR;
78 fd = open(file: tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600);
79 if(fd == -1)
80 goto fail;
81
82#ifdef HAVE_FCHMOD
83 {
84 struct_stat nsb;
85 if((fstat(fd: fd, buf: &nsb) != -1) &&
86 (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
87 /* if the user and group are the same, clone the original mode */
88 if(fchmod(fd: fd, mode: (mode_t)sb.st_mode) == -1)
89 goto fail;
90 }
91 }
92#endif
93
94 *fh = fdopen(fd: fd, FOPEN_WRITETEXT);
95 if(!*fh)
96 goto fail;
97
98 *tempname = tempstore;
99 return CURLE_OK;
100
101fail:
102 if(fd != -1) {
103 close(fd: fd);
104 unlink(name: tempstore);
105 }
106
107 free(tempstore);
108
109 return result;
110}
111
112#endif /* ! disabled */
113