/***************************************************************************1* _ _ ____ _2* Project ___| | | | _ \| |3* / __| | | | |_) | |4* | (__| |_| | _ <| |___5* \___|\___/|_| \_\_____|6*7* Copyright (C) Daniel Stenberg, <[email protected]>, et al.8*9* This software is licensed as described in the file COPYING, which10* you should have received as part of this distribution. The terms11* are also available at https://curl.se/docs/copyright.html.12*13* You may opt to use, copy, modify, merge, publish, distribute and/or sell14* copies of the Software, and permit persons to whom the Software is15* furnished to do so, under the terms of the COPYING file.16*17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY18* KIND, either express or implied.19*20* SPDX-License-Identifier: curl21*22***************************************************************************/23#include "curl_setup.h"2425#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \26!defined(CURL_DISABLE_HSTS)2728#include "urldata.h"29#include "rand.h"30#include "curl_fopen.h"3132/*33The dirslash() function breaks a null-terminated pathname string into34directory and filename components then returns the directory component up35to, *AND INCLUDING*, a final '/'. If there is no directory in the path,36this instead returns a "" string.3738This function returns a pointer to malloc'ed memory.3940The input path to this function is expected to have a filename part.41*/4243#ifdef _WIN3244#define PATHSEP "\\"45#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))46#elif defined(MSDOS) || defined(OS2)47#define PATHSEP "\\"48#define IS_SEP(x) ((x) == '\\')49#else50#define PATHSEP "/"51#define IS_SEP(x) ((x) == '/')52#endif5354static char *dirslash(const char *path)55{56size_t n;57struct dynbuf out;58DEBUGASSERT(path);59curlx_dyn_init(&out, CURL_MAX_INPUT_LENGTH);60n = strlen(path);61if(n) {62/* find the rightmost path separator, if any */63while(n && !IS_SEP(path[n - 1]))64--n;65/* skip over all the path separators, if any */66while(n && IS_SEP(path[n - 1]))67--n;68}69if(curlx_dyn_addn(&out, path, n))70return NULL;71/* if there was a directory, append a single trailing slash */72if(n && curlx_dyn_addn(&out, PATHSEP, 1))73return NULL;74return curlx_dyn_ptr(&out);75}7677/*78* Curl_fopen() opens a file for writing with a temp name, to be renamed79* to the final name when completed. If there is an existing file using this80* name at the time of the open, this function will clone the mode from that81* file. if 'tempname' is non-NULL, it needs a rename after the file is82* written.83*/84CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,85FILE **fh, char **tempname)86{87CURLcode result = CURLE_WRITE_ERROR;88unsigned char randbuf[41];89char *tempstore = NULL;90#ifndef _WIN3291struct_stat sb;92#endif93int fd = -1;94char *dir = NULL;95*tempname = NULL;9697#ifndef _WIN3298*fh = curlx_fopen(filename, FOPEN_WRITETEXT);99if(!*fh)100goto fail;101if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) {102return CURLE_OK;103}104curlx_fclose(*fh);105#endif106*fh = NULL;107108result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));109if(result)110goto fail;111112dir = dirslash(filename);113if(dir) {114/* The temp filename should not end up too long for the target file115system */116tempstore = curl_maprintf("%s%s.tmp", dir, randbuf);117curlx_free(dir);118}119120if(!tempstore) {121result = CURLE_OUT_OF_MEMORY;122goto fail;123}124125result = CURLE_WRITE_ERROR;126#ifdef _WIN32127fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,128S_IREAD | S_IWRITE);129#elif (defined(ANDROID) || defined(__ANDROID__)) && \130(defined(__i386__) || defined(__arm__))131fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,132(mode_t)(S_IRUSR | S_IWUSR | sb.st_mode));133#else134fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,135S_IRUSR | S_IWUSR | sb.st_mode);136#endif137if(fd == -1)138goto fail;139140*fh = curlx_fdopen(fd, FOPEN_WRITETEXT);141if(!*fh)142goto fail;143144*tempname = tempstore;145return CURLE_OK;146147fail:148if(fd != -1) {149close(fd);150unlink(tempstore);151}152153curlx_free(tempstore);154return result;155}156157#endif /* !disabled */158159160