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 The dirslash() function breaks a null-terminated pathname string into 44 directory and filename components then returns the directory component up 45 to, *AND INCLUDING*, a final '/'. If there is no directory in the path, 46 this instead returns a "" string. 47 48 This function returns a pointer to malloc'ed memory. 49 50 The input path to this function is expected to have a file name part. 51*/ 52 53#ifdef _WIN32 54#define PATHSEP "\\" 55#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) 56#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) 57#define PATHSEP "\\" 58#define IS_SEP(x) ((x) == '\\') 59#else 60#define PATHSEP "/" 61#define IS_SEP(x) ((x) == '/') 62#endif 63 64static char *dirslash(const char *path) 65{ 66 size_t n; 67 struct dynbuf out; 68 DEBUGASSERT(path); 69 Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); 70 n = strlen(path); 71 if(n) { 72 /* find the rightmost path separator, if any */ 73 while(n && !IS_SEP(path[n-1])) 74 --n; 75 /* skip over all the path separators, if any */ 76 while(n && IS_SEP(path[n-1])) 77 --n; 78 } 79 if(Curl_dyn_addn(&out, path, n)) 80 return NULL; 81 /* if there was a directory, append a single trailing slash */ 82 if(n && Curl_dyn_addn(&out, PATHSEP, 1)) 83 return NULL; 84 return Curl_dyn_ptr(&out); 85} 86 87/* 88 * Curl_fopen() opens a file for writing with a temp name, to be renamed 89 * to the final name when completed. If there is an existing file using this 90 * name at the time of the open, this function will clone the mode from that 91 * file. if 'tempname' is non-NULL, it needs a rename after the file is 92 * written. 93 */ 94CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, 95 FILE **fh, char **tempname) 96{ 97 CURLcode result = CURLE_WRITE_ERROR; 98 unsigned char randbuf[41]; 99 char *tempstore = NULL; 100 struct_stat sb; 101 int fd = -1; 102 char *dir = NULL; 103 *tempname = NULL; 104 105 *fh = fopen(filename, FOPEN_WRITETEXT); 106 if(!*fh) 107 goto fail; 108 if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { 109 return CURLE_OK; 110 } 111 fclose(*fh); 112 *fh = NULL; 113 114 result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); 115 if(result) 116 goto fail; 117 118 dir = dirslash(filename); 119 if(dir) { 120 /* The temp file name should not end up too long for the target file 121 system */ 122 tempstore = aprintf("%s%s.tmp", dir, randbuf); 123 free(dir); 124 } 125 126 if(!tempstore) { 127 result = CURLE_OUT_OF_MEMORY; 128 goto fail; 129 } 130 131 result = CURLE_WRITE_ERROR; 132 fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); 133 if(fd == -1) 134 goto fail; 135 136 *fh = fdopen(fd, FOPEN_WRITETEXT); 137 if(!*fh) 138 goto fail; 139 140 *tempname = tempstore; 141 return CURLE_OK; 142 143fail: 144 if(fd != -1) { 145 close(fd); 146 unlink(tempstore); 147 } 148 149 free(tempstore); 150 return result; 151} 152 153#endif /* ! disabled */ 154