Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513//// tmpfile.cpp//// Copyright (c) Microsoft Corporation. All rights reserved.//// Defines tmpfile() and tmpfile_s(), which create temporary files, and tmpnam()// and tmpnam_s(), which generate temporary file names (and are used by the// tmpfile() functions).//#include <corecrt_internal_stdio.h>#include <sys/stat.h>
// These are static buffers used by tmpnam() and tmpfile() to build file names.// The sizes are computed as follows: The tmpname string looks like so://// PrefixFirstPart.SecondPart//// Prefix is "s" (1 character in length). FirstPart is generated by converting// the process identifier to a base 32 string. The maximum process identifier// so converted is 7 characters in length. The "." is one character in length.// This gives a total of 1 + 7 + 1 = 9 for the "PrefixFirstPart." part of the// file name.//// The SecondPart is generated by converting a number to a string. In tmpnam,// the maximum number is SHRT_MAX, the maximum length of which is 3 characters.// In tmpnam_s, the maximum number is INT_MAX, which is 7 characters long.//// The tmpnam paths are generated relative to "\\", so the total lengths for the// two functions are the length of that string plus 12 or plus 16 characters.//// The tmpfile path is generated in a manner similar to tmpnam, but relative to// an arbitrary path, so MAX_PATH is used in lieu of _countof("\\").namespace {
enum class buffer_id { tmpnam, tmpfile, tmpnam_s, count };}
static char* narrow_tmpfile_buffer_pointers[static_cast<size_t>(buffer_id::count)];static wchar_t* wide_tmpfile_buffer_pointers [static_cast<size_t>(buffer_id::count)];
extern "C" void __cdecl __acrt_stdio_free_tmpfile_name_buffers_nolock(){ for (char*& p : narrow_tmpfile_buffer_pointers) { _free_crt(p); p = nullptr; }
for (wchar_t*& p : wide_tmpfile_buffer_pointers) { _free_crt(p); p = nullptr; }}
static char*& __cdecl get_tmpfile_buffer_pointer_nolock(buffer_id const id, char) throw(){ return narrow_tmpfile_buffer_pointers[static_cast<size_t>(id)];}
static wchar_t*& __cdecl get_tmpfile_buffer_pointer_nolock(buffer_id const id, wchar_t) throw(){ return wide_tmpfile_buffer_pointers[static_cast<size_t>(id)];}
template <typename Character>_Success_(return != nullptr)static Character* __cdecl get_tmpfile_buffer_nolock(buffer_id const id) throw(){ Character*& buffer_pointer = get_tmpfile_buffer_pointer_nolock(id, Character()); if (!buffer_pointer) { buffer_pointer = _calloc_crt_t(Character, L_tmpnam).detach(); }
return buffer_pointer;}
template <typename Character>_Success_(return == true)static bool __cdecl initialize_tmpfile_buffer_nolock(buffer_id const buffer_id) throw(){ typedef __acrt_stdio_char_traits<Character> stdio_traits;
Character* const buffer = get_tmpfile_buffer_nolock<Character>(buffer_id); size_t const buffer_count = L_tmpnam;
if (!buffer) { return false; }
// The temporary path must be short enough so that we can append a file name // of the form [buffer id][process id].[unique id], which is at most 21 // characters in length (plus we must leave room for the null terminator). // 1 Buffer Id ("s", "t", or "u") // 7 Base-36 Process Id (maximum: "1z141z3") // 13 Base-36 Unique File Id (maximum: "3w5e11264sgsf") DWORD const max_supported_temp_path_length = buffer_count - 22; // Generate the path prefix; make sure it ends with a slash or backslash: // CRT_REFACTOR TODO We need to use the WinRT temp path logic here. DWORD const temp_path_length = stdio_traits::get_temp_path(static_cast<DWORD>(buffer_count), buffer); if (temp_path_length == 0 || temp_path_length > max_supported_temp_path_length) { buffer[0] = '\0'; return false; }
Character* tail = buffer + temp_path_length;
auto tail_count = [&](){ return buffer_count - (tail - buffer); };
// Append the buffer identifier part of the file name: switch (buffer_id) { case buffer_id::tmpnam: *tail++ = sizeof(Character) == 1 ? 's' : 'v'; break; case buffer_id::tmpfile: *tail++ = sizeof(Character) == 1 ? 't' : 'w'; break; case buffer_id::tmpnam_s: *tail++ = sizeof(Character) == 1 ? 'u' : 'x'; break; }
// Append the process identifier part of the file name: _ERRCHECK(stdio_traits::ulltot_s(GetCurrentProcessId(), tail, tail_count(), 36)); tail += stdio_traits::tcslen(tail);
// Append the dot part of the file name and the initial unique id: *tail++ = '.'; *tail++ = '0'; *tail++ = '\0';
return true;}
template <typename Character>_Success_(return == true)static bool __cdecl generate_tmpfile_file_name( _Inout_updates_z_(file_name_count) Character* const file_name, _In_ size_t const file_name_count ) throw(){ typedef __acrt_stdio_char_traits<Character> stdio_traits;
Character* const dot = reinterpret_cast<Character*>(stdio_traits::tcsrchr(file_name, '.')); _VALIDATE_RETURN_NOERRNO(dot != nullptr, false); _VALIDATE_RETURN_NOERRNO(dot >= file_name, false); _VALIDATE_RETURN_NOERRNO(file_name_count > static_cast<size_t>(dot - file_name), false);
Character* const unique_id = dot + 1; size_t const unique_id_count = file_name_count - (unique_id - file_name);
uint64_t const next_identifier = stdio_traits::tcstoull(unique_id, nullptr, 36) + 1; if (next_identifier == 0) return false;
#pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036 Prefast doesn't understand perfect forwarding. _ERRCHECK(stdio_traits::ulltot_s(next_identifier, unique_id, unique_id_count, 36)); return true;}
static char** __cdecl get_tmpnam_ptd_buffer(char) throw(){ __acrt_ptd* const ptd = __acrt_getptd_noexit(); if (ptd == nullptr) return nullptr;
return &ptd->_tmpnam_narrow_buffer;}
static wchar_t** __cdecl get_tmpnam_ptd_buffer(wchar_t) throw(){ __acrt_ptd* const ptd = __acrt_getptd_noexit(); if (ptd == nullptr) return nullptr;
return &ptd->_tmpnam_wide_buffer;}
template <typename Character>_Success_(return == 0)static errno_t __cdecl common_tmpnam_nolock( _Out_writes_opt_z_(result_buffer_count) Character* const result_buffer, size_t const result_buffer_count, buffer_id const buffer_id ) throw(){ typedef __acrt_stdio_char_traits<Character> stdio_traits;
Character* const global_buffer = get_tmpfile_buffer_nolock<Character>(buffer_id); size_t const global_buffer_count = L_tmpnam;
if (!global_buffer) { return ENOMEM; }
// Initialize the tmpnam buffer if it has not yet been initialized. // Otherwise, generate the next file name: if (global_buffer[0] == 0) { initialize_tmpfile_buffer_nolock<Character>(buffer_id); } else if (!generate_tmpfile_file_name(global_buffer, global_buffer_count)) { return ENOENT; }
// Generate a file name that does not already exist: while (stdio_traits::taccess_s(global_buffer, 0) == 0) { if (!generate_tmpfile_file_name(global_buffer, global_buffer_count)) { return ENOENT; } }
// If the result buffer is non-null, copy the file name there, if it will fit: if (result_buffer != nullptr) { if (buffer_id != buffer_id::tmpnam && stdio_traits::tcslen(global_buffer) >= result_buffer_count) { if (result_buffer_count != 0) { result_buffer[0] = 0; }
return ERANGE; }
#pragma warning(suppress:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036 Prefast doesn't understand perfect forwarding. _ERRCHECK(stdio_traits::tcscpy_s(result_buffer, result_buffer_count, global_buffer));
return 0; }
// If the result buffer is null, use a buffer owned by the per-thread data: _ASSERTE(buffer_id == buffer_id::tmpnam);
Character** const ptd_buffer = get_tmpnam_ptd_buffer(Character()); if (ptd_buffer == nullptr) { return ENOMEM; }
if (*ptd_buffer == nullptr) { *ptd_buffer = _calloc_crt_t(Character, global_buffer_count).detach(); if (*ptd_buffer == nullptr) { return ENOMEM; } }
_ERRCHECK(stdio_traits::tcscpy_s(*ptd_buffer, global_buffer_count, global_buffer)); return 0;}
// Generates a temporary file name that is unique in the directory specified by// the related macros in <stdio.h>. Returns the file name in the result_buffer,// or in a per-thread buffer if the result_buffer is null. Returns zero on// success; returns an error code and sets errno on failure.template <typename Character>_Success_(return == 0)static errno_t common_tmpnam( _Out_writes_opt_z_(result_buffer_count) Character* const result_buffer, _In_ size_t const result_buffer_count, _In_ buffer_id const buffer_id, _Outptr_result_z_ Character** const result_pointer ) throw(){ errno_t return_value = 0;
__acrt_lock(__acrt_tempnam_lock); __try { errno_t const saved_errno = errno;
return_value = common_tmpnam_nolock(result_buffer, result_buffer_count, buffer_id); if (return_value != 0) { *result_pointer = result_buffer; errno = return_value; __leave; }
*result_pointer = result_buffer != nullptr ? result_buffer : *get_tmpnam_ptd_buffer(Character());
errno = saved_errno; } __finally { __acrt_unlock(__acrt_tempnam_lock); } __endtry
return return_value;}
_Success_(return == 0)static errno_t __cdecl common_tmpfile_nolock(_Out_ FILE** const stream, int const sh_flag) throw(){ char* const global_buffer = get_tmpfile_buffer_nolock<char>(buffer_id::tmpfile); size_t const global_buffer_count = L_tmpnam;
if (!global_buffer) { return ENOMEM; }
// Initialize the tmpfile buffer if it has not yet been initialized. // Otherwise, generate the next file name: if (*global_buffer == 0) { if (!initialize_tmpfile_buffer_nolock<char>(buffer_id::tmpfile)) { return EINVAL; // REVIEW Which error? } } else if (!generate_tmpfile_file_name(global_buffer, global_buffer_count)) { return EINVAL; // REVIEW Which error? }
__crt_stdio_stream const local_stream = __acrt_stdio_allocate_stream(); if (!local_stream.valid()) { return EMFILE; }
errno_t result = 0;
__try { errno_t const saved_errno = errno; errno = 0;
// Create a temporary file. Note that the loop below will only create a // new file. It will not open and truncate an existing behavior. This // behavior is permitted under ISO C. The behavior implemented below is // compatible with prior versions of the C Runtime and makes error // checking easier. int fh = 0;
int const open_flag = _O_CREAT | _O_EXCL | _O_RDWR | _O_BINARY | _O_TEMPORARY; int const permission_mode = _S_IREAD | _S_IWRITE;
while ((result = _sopen_s(&fh, global_buffer, open_flag, sh_flag, permission_mode)) == EEXIST) { if (!generate_tmpfile_file_name(global_buffer, global_buffer_count)) { break; } }
if (errno == 0) { errno = saved_errno; }
// Ensure that the loop above did indeed create the file: if (fh == -1) { __leave; }
// Initialize the stream: local_stream->_tmpfname = _strdup_crt(global_buffer); if (local_stream->_tmpfname == nullptr) { _close(fh); result = ENOMEM; __leave; }
local_stream->_cnt = 0; local_stream->_base = nullptr; local_stream->_ptr = nullptr; local_stream.set_flags(_commode | _IOUPDATE); local_stream->_file = fh; *stream = local_stream.public_stream(); result = 0; } __finally { // If we didn't complete initialization of the stream successfully, we // must free the allocated stream: if (local_stream->_file == -1) __acrt_stdio_free_stream(local_stream);
local_stream.unlock(); } __endtry
return result;}
_Success_(return == 0)static errno_t __cdecl common_tmpfile(_Out_ FILE** const stream, int const sh_flag) throw(){ _VALIDATE_RETURN_ERRCODE(stream != nullptr, EINVAL); *stream = nullptr;
errno_t return_value = 0;
__acrt_lock(__acrt_tempnam_lock); __try { return_value = common_tmpfile_nolock(stream, sh_flag); if (return_value != 0) { errno = return_value; } } __finally { __acrt_unlock(__acrt_tempnam_lock); } __endtry
return return_value;}
extern "C" errno_t __cdecl tmpnam_s( char* const result_buffer, size_t const result_buffer_count ){ char* result = nullptr; _VALIDATE_RETURN_ERRCODE(result_buffer != nullptr, EINVAL); return common_tmpnam(result_buffer, result_buffer_count, buffer_id::tmpnam_s, &result);}
extern "C" errno_t __cdecl _wtmpnam_s( wchar_t* const result_buffer, size_t const result_buffer_count ){ wchar_t* result = nullptr; _VALIDATE_RETURN_ERRCODE(result_buffer != nullptr, EINVAL); return common_tmpnam(result_buffer, result_buffer_count, buffer_id::tmpnam_s, &result);}
extern "C" char* __cdecl tmpnam(char* const result_buffer){ char* result = nullptr; common_tmpnam(result_buffer, L_tmpnam, buffer_id::tmpnam, &result); return result;}
extern "C" wchar_t* __cdecl _wtmpnam(wchar_t* const result_buffer){ wchar_t* result = nullptr; common_tmpnam(result_buffer, L_tmpnam, buffer_id::tmpnam, &result); return result;}
// Creates a temporary file with the file mode "w+b". The file will be deleted// automatically when it is closed or when the program terminates normally.// On success, returns a stream; on failure, returns nullptr.extern "C" FILE* __cdecl tmpfile(){ FILE* stream = nullptr; common_tmpfile(&stream, _SH_DENYNO); return stream;}
// Creates a temporary file with the file mode "w+b". The file will be deleted// automatically when it is closed or when the program terminates normally.// On success, returns zero and '*stream' refers to the newly opened stream.// On failure, returns an error code and '*stream' is null.extern "C" errno_t __cdecl tmpfile_s(FILE** const stream){ return common_tmpfile(stream, _SH_DENYRW);}
// Ensure that _rmtmp is called during static CRT termination:#ifndef CRTDLL
extern "C" unsigned __acrt_tmpfile_used;
extern "C" void __acrt_force_use_of_tmpfile() { ++__acrt_tmpfile_used; }
#endif