1 /* Create a temporary file.
2 Copyright (C) 2007, 2009-2021 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Written by Ben Pfaff. */
18
19 #include <config.h>
20
21 /* Specification. */
22 #include <stdio.h>
23
24 #include <errno.h>
25 #include <stdbool.h>
26
27 #if defined _WIN32 && ! defined __CYGWIN__
28 /* A native Windows platform. */
29
30 # include <fcntl.h>
31 # include <string.h>
32 # include <sys/stat.h>
33
34 # include <io.h>
35
36 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
37 # include <windows.h>
38
39 #else
40
41 # include <unistd.h>
42
43 #endif
44
45 #include "pathmax.h"
46 #include "tempname.h"
47 #include "tmpdir.h"
48
49 /* PATH_MAX is guaranteed to be defined, because this replacement is only
50 used on native Windows and Android. */
51
52 #if defined _WIN32 && ! defined __CYGWIN__
53 /* A native Windows platform. */
54
55 /* Don't assume that UNICODE is not defined. */
56 # undef OSVERSIONINFO
57 # define OSVERSIONINFO OSVERSIONINFOA
58 # undef GetVersionEx
59 # define GetVersionEx GetVersionExA
60 # undef GetTempPath
61 # define GetTempPath GetTempPathA
62
63 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
64 the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
65 of deleting the file when it is closed - even when the program crashes.
66 But (according to the Cygwin sources) it works only on Windows NT or newer.
67 So we cache the info whether we are running on Windows NT or newer. */
68
69 static bool
70 supports_delete_on_close ()
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
71 {
72 static int known; /* 1 = yes, -1 = no, 0 = unknown */
73 if (!known)
74 {
75 OSVERSIONINFO v;
76
77 /* According to
78 <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getversionexa>
79 this structure must be initialized as follows: */
80 v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
81
82 if (GetVersionEx (&v))
83 known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
84 else
85 known = -1;
86 }
87 return (known > 0);
88 }
89
90 FILE *
91 tmpfile (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
92 {
93 char dir[PATH_MAX];
94 DWORD retval;
95
96 /* Find Windows temporary file directory.
97 We provide this as the directory argument to path_search because Windows
98 defines P_tmpdir to "\\" and will therefore try to put all temporary files
99 in the root directory (unless $TMPDIR is set). */
100 retval = GetTempPath (PATH_MAX, dir);
101 if (retval > 0 && retval < PATH_MAX)
102 {
103 char xtemplate[PATH_MAX];
104
105 if (path_search (xtemplate, PATH_MAX, dir, NULL, true) >= 0)
106 {
107 size_t len = strlen (xtemplate);
108 int o_temporary = (supports_delete_on_close () ? _O_TEMPORARY : 0);
109 int fd;
110
111 do
112 {
113 memcpy (&xtemplate[len - 6], "XXXXXX", 6);
114 if (gen_tempname (xtemplate, 0, 0, GT_NOCREATE) < 0)
115 {
116 fd = -1;
117 break;
118 }
119
120 fd = _open (xtemplate,
121 _O_CREAT | _O_EXCL | o_temporary
122 | _O_RDWR | _O_BINARY,
123 _S_IREAD | _S_IWRITE);
124 }
125 while (fd < 0 && errno == EEXIST);
126
127 if (fd >= 0)
128 {
129 FILE *fp = _fdopen (fd, "w+b");
130
131 if (fp != NULL)
132 return fp;
133 else
134 {
135 int saved_errno = errno;
136 _close (fd);
137 errno = saved_errno;
138 }
139 }
140 }
141 }
142 else
143 {
144 if (retval > 0)
145 errno = ENAMETOOLONG;
146 else
147 /* Ideally this should translate GetLastError () to an errno value. */
148 errno = ENOENT;
149 }
150
151 return NULL;
152 }
153
154 #else
155
156 FILE *
157 tmpfile (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
158 {
159 char buf[PATH_MAX];
160 int fd;
161 FILE *fp;
162
163 /* Try $TMPDIR first, not /tmp nor P_tmpdir, because we need this replacement
164 on Android, and /tmp does not exist on Android. */
165
166 if (path_search (buf, sizeof buf, NULL, "tmpf", true))
167 return NULL;
168
169 fd = gen_tempname (buf, 0, 0, GT_FILE);
170 if (fd < 0)
171 return NULL;
172
173 /* Note that this relies on the Unix semantics that
174 a file is not really removed until it is closed. */
175 (void) unlink (buf);
176
177 if ((fp = fdopen (fd, "w+b")) == NULL)
178 {
179 int saved_errno = errno;
180 close (fd);
181 errno = saved_errno;
182 }
183
184 return fp;
185 }
186
187 #endif