fopen.c (4404B)
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 filename part. 51 */ 52 53 #ifdef _WIN32 54 #define PATHSEP "\\" 55 #define IS_SEP(x) (((x) == '/') || ((x) == '\\')) 56 #elif defined(MSDOS) || 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 64 static char *dirslash(const char *path) 65 { 66 size_t n; 67 struct dynbuf out; 68 DEBUGASSERT(path); 69 curlx_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(curlx_dyn_addn(&out, path, n)) 80 return NULL; 81 /* if there was a directory, append a single trailing slash */ 82 if(n && curlx_dyn_addn(&out, PATHSEP, 1)) 83 return NULL; 84 return curlx_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 */ 94 CURLcode 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( 109 #ifdef UNDER_CE 110 stat(filename, &sb) == -1 111 #else 112 fstat(fileno(*fh), &sb) == -1 113 #endif 114 || !S_ISREG(sb.st_mode)) { 115 return CURLE_OK; 116 } 117 fclose(*fh); 118 *fh = NULL; 119 120 result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); 121 if(result) 122 goto fail; 123 124 dir = dirslash(filename); 125 if(dir) { 126 /* The temp filename should not end up too long for the target file 127 system */ 128 tempstore = aprintf("%s%s.tmp", dir, randbuf); 129 free(dir); 130 } 131 132 if(!tempstore) { 133 result = CURLE_OUT_OF_MEMORY; 134 goto fail; 135 } 136 137 result = CURLE_WRITE_ERROR; 138 #if (defined(ANDROID) || defined(__ANDROID__)) && \ 139 (defined(__i386__) || defined(__arm__)) 140 fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode)); 141 #else 142 fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); 143 #endif 144 if(fd == -1) 145 goto fail; 146 147 *fh = fdopen(fd, FOPEN_WRITETEXT); 148 if(!*fh) 149 goto fail; 150 151 *tempname = tempstore; 152 return CURLE_OK; 153 154 fail: 155 if(fd != -1) { 156 close(fd); 157 unlink(tempstore); 158 } 159 160 free(tempstore); 161 return result; 162 } 163 164 #endif /* ! disabled */