1 /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 #include "mysys_priv.h"
17 #include <m_string.h>
18 #include "my_static.h"
19 #include "mysys_err.h"
20 #include <errno.h>
21 #ifdef HAVE_PATHS_H
22 #include <paths.h>
23 #endif
24 
25 #ifdef HAVE_MKOSTEMP
26 #define mkstemp(A) mkostemp(A, O_CLOEXEC)
27 #endif
28 
29 /*
30   @brief
31   Create a temporary file with unique name in a given directory
32 
33   @details
34   create_temp_file
35     to             pointer to buffer where temporary filename will be stored
36     dir            directory where to create the file
37     prefix         prefix the filename with this
38     mode           Flags to use for my_create/my_open
39     MyFlags        Magic flags
40 
41   @return
42     File descriptor of opened file if success
43     -1 and sets errno if fails.
44 
45   @note
46     The behaviour of this function differs a lot between
47     implementation, it's main use is to generate a file with
48     a name that does not already exist.
49 
50     When passing MY_TEMPORARY flag in MyFlags the file is automatically deleted
51 
52     "mode" bits that always must be used for newly created files with
53     unique file names (O_EXCL | O_TRUNC | O_CREAT | O_RDWR) are added
54     automatically, and shouldn't be specified by the caller.
55 
56     The implementation using mkstemp should be considered the
57     reference implementation when adding a new or modifying an
58     existing one
59 
60 */
61 
create_temp_file(char * to,const char * dir,const char * prefix,int mode,myf MyFlags)62 File create_temp_file(char *to, const char *dir, const char *prefix,
63 		      int mode, myf MyFlags)
64 {
65   File file= -1;
66 
67   DBUG_ENTER("create_temp_file");
68   DBUG_PRINT("enter", ("dir: %s, prefix: %s", dir ? dir : "(null)", prefix));
69   DBUG_ASSERT((mode & (O_EXCL | O_TRUNC | O_CREAT | O_RDWR)) == 0);
70 
71   mode|= O_TRUNC | O_CREAT | O_RDWR; /* not O_EXCL, see Windows code below */
72 
73 #ifdef _WIN32
74   {
75     TCHAR path_buf[MAX_PATH-14];
76     /*
77       Use GetTempPath to determine path for temporary files.
78       This is because the documentation for GetTempFileName
79       has the following to say about this parameter:
80       "If this parameter is NULL, the function fails."
81     */
82     if (!dir)
83     {
84       if(GetTempPath(sizeof(path_buf), path_buf) > 0)
85         dir = path_buf;
86     }
87     /*
88       Use GetTempFileName to generate a unique filename, create
89       the file and release it's handle
90        - uses up to the first three letters from prefix
91     */
92     if (GetTempFileName(dir, prefix, 0, to) == 0)
93       DBUG_RETURN(-1);
94 
95     DBUG_PRINT("info", ("name: %s", to));
96 
97     if (MyFlags & MY_TEMPORARY)
98       mode|= O_SHORT_LIVED | O_TEMPORARY;
99 
100     /*
101       Open the file without O_EXCL flag
102       since the file has already been created by GetTempFileName
103     */
104     if ((file= my_open(to, mode, MyFlags)) < 0)
105     {
106       /* Open failed, remove the file created by GetTempFileName */
107       int tmp= my_errno;
108       (void) my_delete(to, MYF(0));
109       my_errno= tmp;
110     }
111   }
112 #elif defined(HAVE_MKSTEMP)
113   if (!dir && ! (dir =getenv("TMPDIR")))
114     dir= DEFAULT_TMPDIR;
115 #ifdef O_TMPFILE
116   {
117     static int O_TMPFILE_works= 1;
118 
119     if ((MyFlags & MY_TEMPORARY) && O_TMPFILE_works)
120     {
121       /* explictly don't use O_EXCL here has it has a different
122          meaning with O_TMPFILE
123       */
124       if ((file= open(dir, (mode & ~O_CREAT) | O_TMPFILE | O_CLOEXEC,
125                       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) >= 0)
126       {
127         my_snprintf(to, FN_REFLEN, "%s/#sql/fd=%d", dir, file);
128         file=my_register_filename(file, to, FILE_BY_O_TMPFILE,
129                                   EE_CANTCREATEFILE, MyFlags);
130       }
131       else if (errno == EOPNOTSUPP || errno == EINVAL)
132       {
133         my_printf_error(EE_CANTCREATEFILE, "O_TMPFILE is not supported on %s "
134                         "(disabling future attempts)",
135                         MYF(ME_NOTE | ME_ERROR_LOG_ONLY), dir);
136         O_TMPFILE_works= 0;
137       }
138     }
139   }
140   if (file == -1)
141 #endif /* O_TMPFILE */
142   {
143     char prefix_buff[30];
144     uint pfx_len;
145     File org_file;
146 
147     pfx_len= (uint) (strmov(strnmov(prefix_buff,
148 				    prefix ? prefix : "tmp.",
149 				    sizeof(prefix_buff)-7),"XXXXXX") -
150 		     prefix_buff);
151     if (strlen(dir)+ pfx_len > FN_REFLEN-2)
152     {
153       errno=my_errno= ENAMETOOLONG;
154       DBUG_RETURN(file);
155     }
156     strmov(convert_dirname(to,dir,NullS),prefix_buff);
157     org_file=mkstemp(to);
158     if (org_file >= 0 && (MyFlags & MY_TEMPORARY))
159       (void) my_delete(to, MYF(MY_WME));
160     file=my_register_filename(org_file, to, FILE_BY_MKSTEMP,
161 			      EE_CANTCREATEFILE, MyFlags);
162     /* If we didn't manage to register the name, remove the temp file */
163     if (org_file >= 0 && file < 0)
164     {
165       int tmp=my_errno;
166       close(org_file);
167       (void) my_delete(to, MYF(MY_WME));
168       my_errno=tmp;
169     }
170   }
171 #else
172 #error No implementation found for create_temp_file
173 #endif
174   if (file >= 0)
175     statistic_increment(my_tmp_file_created,&THR_LOCK_open);
176   DBUG_RETURN(file);
177 }
178