ReactOS 0.4.16-dev-835-gd769f56
tmpfile.cpp
Go to the documentation of this file.
1//
2// tmpfile.cpp
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// Defines tmpfile() and tmpfile_s(), which create temporary files, and tmpnam()
7// and tmpnam_s(), which generate temporary file names (and are used by the
8// tmpfile() functions).
9//
11#include <sys/stat.h>
12
13
14
15// These are static buffers used by tmpnam() and tmpfile() to build file names.
16// The sizes are computed as follows: The tmpname string looks like so:
17//
18// PrefixFirstPart.SecondPart
19//
20// Prefix is "s" (1 character in length). FirstPart is generated by converting
21// the process identifier to a base 32 string. The maximum process identifier
22// so converted is 7 characters in length. The "." is one character in length.
23// This gives a total of 1 + 7 + 1 = 9 for the "PrefixFirstPart." part of the
24// file name.
25//
26// The SecondPart is generated by converting a number to a string. In tmpnam,
27// the maximum number is SHRT_MAX, the maximum length of which is 3 characters.
28// In tmpnam_s, the maximum number is INT_MAX, which is 7 characters long.
29//
30// The tmpnam paths are generated relative to "\\", so the total lengths for the
31// two functions are the length of that string plus 12 or plus 16 characters.
32//
33// The tmpfile path is generated in a manner similar to tmpnam, but relative to
34// an arbitrary path, so MAX_PATH is used in lieu of _countof("\\").
35namespace {
36
37 enum class buffer_id
38 {
39 tmpnam,
40 tmpfile,
42 count
43 };
44}
45
46static char* narrow_tmpfile_buffer_pointers[static_cast<size_t>(buffer_id::count)];
47static wchar_t* wide_tmpfile_buffer_pointers [static_cast<size_t>(buffer_id::count)];
48
50{
52 {
53 _free_crt(p);
54 p = nullptr;
55 }
56
57 for (wchar_t*& p : wide_tmpfile_buffer_pointers)
58 {
59 _free_crt(p);
60 p = nullptr;
61 }
62}
63
64static char*& __cdecl get_tmpfile_buffer_pointer_nolock(buffer_id const id, char) throw()
65{
66 return narrow_tmpfile_buffer_pointers[static_cast<size_t>(id)];
67}
68
69static wchar_t*& __cdecl get_tmpfile_buffer_pointer_nolock(buffer_id const id, wchar_t) throw()
70{
71 return wide_tmpfile_buffer_pointers[static_cast<size_t>(id)];
72}
73
74template <typename Character>
75_Success_(return != nullptr)
76static Character* __cdecl get_tmpfile_buffer_nolock(buffer_id const id) throw()
77{
78 Character*& buffer_pointer = get_tmpfile_buffer_pointer_nolock(id, Character());
79 if (!buffer_pointer)
80 {
81 buffer_pointer = _calloc_crt_t(Character, L_tmpnam).detach();
82 }
83
84 return buffer_pointer;
85}
86
87template <typename Character>
88_Success_(return == true)
89static bool __cdecl initialize_tmpfile_buffer_nolock(buffer_id const buffer_id) throw()
90{
91 typedef __acrt_stdio_char_traits<Character> stdio_traits;
92
93 Character* const buffer = get_tmpfile_buffer_nolock<Character>(buffer_id);
94 size_t const buffer_count = L_tmpnam;
95
96 if (!buffer)
97 {
98 return false;
99 }
100
101 // The temporary path must be short enough so that we can append a file name
102 // of the form [buffer id][process id].[unique id], which is at most 21
103 // characters in length (plus we must leave room for the null terminator).
104 // 1 Buffer Id ("s", "t", or "u")
105 // 7 Base-36 Process Id (maximum: "1z141z3")
106 // 13 Base-36 Unique File Id (maximum: "3w5e11264sgsf")
107 DWORD const max_supported_temp_path_length = buffer_count - 22;
108
109 // Generate the path prefix; make sure it ends with a slash or backslash:
110 // CRT_REFACTOR TODO We need to use the WinRT temp path logic here.
111 DWORD const temp_path_length = stdio_traits::get_temp_path(static_cast<DWORD>(buffer_count), buffer);
112 if (temp_path_length == 0 || temp_path_length > max_supported_temp_path_length)
113 {
114 buffer[0] = '\0';
115 return false;
116 }
117
118 Character* tail = buffer + temp_path_length;
119
120 auto tail_count = [&](){ return buffer_count - (tail - buffer); };
121
122 // Append the buffer identifier part of the file name:
123 switch (buffer_id)
124 {
125 case buffer_id::tmpnam: *tail++ = sizeof(Character) == 1 ? 's' : 'v'; break;
126 case buffer_id::tmpfile: *tail++ = sizeof(Character) == 1 ? 't' : 'w'; break;
127 case buffer_id::tmpnam_s: *tail++ = sizeof(Character) == 1 ? 'u' : 'x'; break;
128 }
129
130 // Append the process identifier part of the file name:
131 _ERRCHECK(stdio_traits::ulltot_s(GetCurrentProcessId(), tail, tail_count(), 36));
132 tail += stdio_traits::tcslen(tail);
133
134 // Append the dot part of the file name and the initial unique id:
135 *tail++ = '.';
136 *tail++ = '0';
137 *tail++ = '\0';
138
139 return true;
140}
141
142
143
144template <typename Character>
145_Success_(return == true)
146static bool __cdecl generate_tmpfile_file_name(
147 _Inout_updates_z_(file_name_count) Character* const file_name,
148 _In_ size_t const file_name_count
150{
151 typedef __acrt_stdio_char_traits<Character> stdio_traits;
152
153 Character* const dot = reinterpret_cast<Character*>(stdio_traits::tcsrchr(file_name, '.'));
154 _VALIDATE_RETURN_NOERRNO(dot != nullptr, false);
155 _VALIDATE_RETURN_NOERRNO(dot >= file_name, false);
156 _VALIDATE_RETURN_NOERRNO(file_name_count > static_cast<size_t>(dot - file_name), false);
157
158 Character* const unique_id = dot + 1;
159 size_t const unique_id_count = file_name_count - (unique_id - file_name);
160
161 uint64_t const next_identifier = stdio_traits::tcstoull(unique_id, nullptr, 36) + 1;
162 if (next_identifier == 0)
163 return false;
164
165#pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036 Prefast doesn't understand perfect forwarding.
166 _ERRCHECK(stdio_traits::ulltot_s(next_identifier, unique_id, unique_id_count, 36));
167 return true;
168}
169
170
171
172static char** __cdecl get_tmpnam_ptd_buffer(char) throw()
173{
175 if (ptd == nullptr)
176 return nullptr;
177
178 return &ptd->_tmpnam_narrow_buffer;
179}
180
181static wchar_t** __cdecl get_tmpnam_ptd_buffer(wchar_t) throw()
182{
184 if (ptd == nullptr)
185 return nullptr;
186
187 return &ptd->_tmpnam_wide_buffer;
188}
189
190
191
192template <typename Character>
193_Success_(return == 0)
194static errno_t __cdecl common_tmpnam_nolock(
198 ) throw()
199{
200 typedef __acrt_stdio_char_traits<Character> stdio_traits;
201
202 Character* const global_buffer = get_tmpfile_buffer_nolock<Character>(buffer_id);
203 size_t const global_buffer_count = L_tmpnam;
204
205 if (!global_buffer)
206 {
207 return ENOMEM;
208 }
209
210 // Initialize the tmpnam buffer if it has not yet been initialized.
211 // Otherwise, generate the next file name:
212 if (global_buffer[0] == 0)
213 {
214 initialize_tmpfile_buffer_nolock<Character>(buffer_id);
215 }
216 else if (!generate_tmpfile_file_name(global_buffer, global_buffer_count))
217 {
218 return ENOENT;
219 }
220
221 // Generate a file name that does not already exist:
222 while (stdio_traits::taccess_s(global_buffer, 0) == 0)
223 {
224 if (!generate_tmpfile_file_name(global_buffer, global_buffer_count))
225 {
226 return ENOENT;
227 }
228 }
229
230 // If the result buffer is non-null, copy the file name there, if it will fit:
231 if (result_buffer != nullptr)
232 {
234 stdio_traits::tcslen(global_buffer) >= result_buffer_count)
235 {
236 if (result_buffer_count != 0)
237 {
238 result_buffer[0] = 0;
239 }
240
241 return ERANGE;
242 }
243
244#pragma warning(suppress:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036 Prefast doesn't understand perfect forwarding.
245 _ERRCHECK(stdio_traits::tcscpy_s(result_buffer, result_buffer_count, global_buffer));
246
247 return 0;
248 }
249
250 // If the result buffer is null, use a buffer owned by the per-thread data:
252
253 Character** const ptd_buffer = get_tmpnam_ptd_buffer(Character());
254 if (ptd_buffer == nullptr)
255 {
256 return ENOMEM;
257 }
258
259 if (*ptd_buffer == nullptr)
260 {
261 *ptd_buffer = _calloc_crt_t(Character, global_buffer_count).detach();
262 if (*ptd_buffer == nullptr)
263 {
264 return ENOMEM;
265 }
266 }
267
268 _ERRCHECK(stdio_traits::tcscpy_s(*ptd_buffer, global_buffer_count, global_buffer));
269 return 0;
270}
271
272
273
274// Generates a temporary file name that is unique in the directory specified by
275// the related macros in <stdio.h>. Returns the file name in the result_buffer,
276// or in a per-thread buffer if the result_buffer is null. Returns zero on
277// success; returns an error code and sets errno on failure.
278template <typename Character>
279_Success_(return == 0)
280static errno_t common_tmpnam(
284 _Outptr_result_z_ Character** const result_pointer
285 ) throw()
286{
287 errno_t return_value = 0;
288
290 __try
291 {
292 errno_t const saved_errno = errno;
293
294 return_value = common_tmpnam_nolock(result_buffer, result_buffer_count, buffer_id);
295 if (return_value != 0)
296 {
297 *result_pointer = result_buffer;
298 errno = return_value;
299 __leave;
300 }
301
302 *result_pointer = result_buffer != nullptr
304 : *get_tmpnam_ptd_buffer(Character());
305
306 errno = saved_errno;
307 }
309 {
311 }
313
314 return return_value;
315}
316
317
318
319_Success_(return == 0)
320static errno_t __cdecl common_tmpfile_nolock(_Out_ FILE** const stream, int const sh_flag) throw()
321{
322 char* const global_buffer = get_tmpfile_buffer_nolock<char>(buffer_id::tmpfile);
323 size_t const global_buffer_count = L_tmpnam;
324
325 if (!global_buffer)
326 {
327 return ENOMEM;
328 }
329
330 // Initialize the tmpfile buffer if it has not yet been initialized.
331 // Otherwise, generate the next file name:
332 if (*global_buffer == 0)
333 {
334 if (!initialize_tmpfile_buffer_nolock<char>(buffer_id::tmpfile))
335 {
336 return EINVAL; // REVIEW Which error?
337 }
338 }
339 else if (!generate_tmpfile_file_name(global_buffer, global_buffer_count))
340 {
341 return EINVAL; // REVIEW Which error?
342 }
343
345 if (!local_stream.valid())
346 {
347 return EMFILE;
348 }
349
350 errno_t result = 0;
351
352 __try
353 {
354 errno_t const saved_errno = errno;
355 errno = 0;
356
357 // Create a temporary file. Note that the loop below will only create a
358 // new file. It will not open and truncate an existing behavior. This
359 // behavior is permitted under ISO C. The behavior implemented below is
360 // compatible with prior versions of the C Runtime and makes error
361 // checking easier.
362 int fh = 0;
363
364 int const open_flag = _O_CREAT | _O_EXCL | _O_RDWR | _O_BINARY | _O_TEMPORARY;
365 int const permission_mode = _S_IREAD | _S_IWRITE;
366
367 while ((result = _sopen_s(&fh, global_buffer, open_flag, sh_flag, permission_mode)) == EEXIST)
368 {
369 if (!generate_tmpfile_file_name(global_buffer, global_buffer_count))
370 {
371 break;
372 }
373 }
374
375 if (errno == 0)
376 {
377 errno = saved_errno;
378 }
379
380 // Ensure that the loop above did indeed create the file:
381 if (fh == -1)
382 {
383 __leave;
384 }
385
386 // Initialize the stream:
387 local_stream->_tmpfname = _strdup_crt(global_buffer);
388 if (local_stream->_tmpfname == nullptr)
389 {
390 _close(fh);
391 result = ENOMEM;
392 __leave;
393 }
394
395 local_stream->_cnt = 0;
396 local_stream->_base = nullptr;
397 local_stream->_ptr = nullptr;
398 local_stream.set_flags(_commode | _IOUPDATE);
399 local_stream->_file = fh;
400 *stream = local_stream.public_stream();
401 result = 0;
402 }
404 {
405 // If we didn't complete initialization of the stream successfully, we
406 // must free the allocated stream:
407 if (local_stream->_file == -1)
408 __acrt_stdio_free_stream(local_stream);
409
410 local_stream.unlock();
411 }
413
414 return result;
415}
416
417
418
419_Success_(return == 0)
420static errno_t __cdecl common_tmpfile(_Out_ FILE** const stream, int const sh_flag) throw()
421{
423 *stream = nullptr;
424
425 errno_t return_value = 0;
426
428 __try
429 {
430 return_value = common_tmpfile_nolock(stream, sh_flag);
431 if (return_value != 0)
432 {
433 errno = return_value;
434 }
435 }
437 {
439 }
441
442 return return_value;
443}
444
445
446
448 char* const result_buffer,
449 size_t const result_buffer_count
450 )
451{
452 char* result = nullptr;
455}
456
458 wchar_t* const result_buffer,
459 size_t const result_buffer_count
460 )
461{
462 wchar_t* result = nullptr;
465}
466
467extern "C" char* __cdecl tmpnam(char* const result_buffer)
468{
469 char* result = nullptr;
471 return result;
472}
473
474extern "C" wchar_t* __cdecl _wtmpnam(wchar_t* const result_buffer)
475{
476 wchar_t* result = nullptr;
478 return result;
479}
480
481// Creates a temporary file with the file mode "w+b". The file will be deleted
482// automatically when it is closed or when the program terminates normally.
483// On success, returns a stream; on failure, returns nullptr.
484extern "C" FILE* __cdecl tmpfile()
485{
486 FILE* stream = nullptr;
487 common_tmpfile(&stream, _SH_DENYNO);
488 return stream;
489}
490
491// Creates a temporary file with the file mode "w+b". The file will be deleted
492// automatically when it is closed or when the program terminates normally.
493// On success, returns zero and '*stream' refers to the newly opened stream.
494// On failure, returns an error code and '*stream' is null.
496{
497 return common_tmpfile(stream, _SH_DENYRW);
498}
499
500
501
502// Ensure that _rmtmp is called during static CRT termination:
503#ifndef CRTDLL
504
505 extern "C" unsigned __acrt_tmpfile_used;
506
508 {
510 }
511
512#endif
#define ENOENT
Definition: acclib.h:79
#define EEXIST
Definition: acclib.h:88
#define EINVAL
Definition: acclib.h:90
#define ENOMEM
Definition: acclib.h:84
#define ERANGE
Definition: acclib.h:92
#define __cdecl
Definition: accygwin.h:79
struct outqueuenode * tail
Definition: adnsresfilter.c:66
bool set_flags(long const flags) const
void __cdecl __acrt_unlock(_In_ __acrt_lock_id lock)
Definition: locks.cpp:57
@ __acrt_tempnam_lock
#define _ERRCHECK(e)
#define _strdup_crt
__acrt_ptd *__cdecl __acrt_getptd_noexit(void)
void __cdecl __acrt_stdio_free_stream(__crt_stdio_stream _Stream)
Definition: stream.cpp:97
__crt_stdio_stream __cdecl __acrt_stdio_allocate_stream()
Definition: stream.cpp:71
#define _ASSERTE(expr)
Definition: crtdbg.h:114
#define _SH_DENYRW
Definition: share.h:14
#define _SH_DENYNO
Definition: share.h:17
_In_ size_t const _In_ int _In_ bool const _In_ unsigned const _In_ __acrt_rounding_mode const _Inout_ __crt_cached_ptd_host & ptd
Definition: cvt.cpp:355
result_buffer_count char *const result_buffer
Definition: cvt.cpp:111
__acrt_lock(__acrt_heap_lock)
UINT64 uint64_t
Definition: types.h:77
#define _O_RDWR
Definition: cabinet.h:39
#define _O_BINARY
Definition: cabinet.h:51
#define _O_TEMPORARY
Definition: cabinet.h:44
#define _O_CREAT
Definition: cabinet.h:46
#define _O_EXCL
Definition: cabinet.h:48
#define _S_IWRITE
Definition: cabinet.h:33
#define _S_IREAD
Definition: cabinet.h:34
#define EMFILE
Definition: errno.h:30
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLuint buffer
Definition: glext.h:5915
GLuint64EXT * result
Definition: glext.h:11304
GLfloat GLfloat p
Definition: glext.h:8902
GLuint id
Definition: glext.h:5910
_CRTIMP int _commode
Definition: environ.c:31
#define L_tmpnam
Definition: stdio.h:49
#define _VALIDATE_RETURN_NOERRNO(expr, retexpr)
#define _free_crt
#define _VALIDATE_RETURN_ERRCODE(expr, errorcode)
static LPCWSTR file_name
Definition: protocol.c:147
#define _Outptr_result_z_
Definition: ms_sal.h:434
#define _Success_(c)
Definition: no_sal2.h:84
#define _Inout_updates_z_(s)
Definition: no_sal2.h:186
#define _Out_writes_opt_z_(s)
Definition: no_sal2.h:230
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define __try
Definition: pseh2_64.h:172
#define __leave
Definition: pseh2_64.h:176
#define __endtry
Definition: pseh2_64.h:175
#define __finally
Definition: pseh2_64.h:174
#define errno
Definition: errno.h:18
_Check_return_opt_ _CRTIMP int __cdecl _close(_In_ int _FileHandle)
_Check_return_wat_ _CRTIMP errno_t __cdecl _sopen_s(_Out_ int *_FileHandle, _In_z_ const char *_Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, _In_ int _PermissionMode)
Definition: parse.h:23
errno_t __cdecl tmpnam_s(char *const result_buffer, size_t const result_buffer_count)
Definition: tmpfile.cpp:447
errno_t __cdecl tmpfile_s(FILE **const stream)
Definition: tmpfile.cpp:495
static wchar_t * wide_tmpfile_buffer_pointers[static_cast< size_t >(buffer_id::count)]
Definition: tmpfile.cpp:47
static char * narrow_tmpfile_buffer_pointers[static_cast< size_t >(buffer_id::count)]
Definition: tmpfile.cpp:46
_In_ size_t const file_name_count throw()
Definition: tmpfile.cpp:149
FILE *__cdecl tmpfile()
Definition: tmpfile.cpp:484
size_t const result_buffer_count
Definition: tmpfile.cpp:196
static char **__cdecl get_tmpnam_ptd_buffer(char)
Definition: tmpfile.cpp:172
errno_t __cdecl _wtmpnam_s(wchar_t *const result_buffer, size_t const result_buffer_count)
Definition: tmpfile.cpp:457
unsigned __acrt_tmpfile_used
Definition: tmpfile.cpp:505
char *__cdecl tmpnam(char *const result_buffer)
Definition: tmpfile.cpp:467
void __acrt_force_use_of_tmpfile()
Definition: tmpfile.cpp:507
wchar_t *__cdecl _wtmpnam(wchar_t *const result_buffer)
Definition: tmpfile.cpp:474
void __cdecl __acrt_stdio_free_tmpfile_name_buffers_nolock()
Definition: tmpfile.cpp:49
static char *&__cdecl get_tmpfile_buffer_pointer_nolock(buffer_id const id, char)
Definition: tmpfile.cpp:64
int errno_t
Definition: corecrt.h:615
DWORD WINAPI GetCurrentProcessId(void)
Definition: proc.c:1158
size_t const buffer_count
Definition: xtoa.cpp:36
#define const
Definition: zconf.h:233