Reactos
at master 295 lines 9.7 kB view raw
1// 2// tempnam.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines _tempnam(), which generates temporary file names. 7// 8#include <corecrt_internal_stdio.h> 9 10 11 12// Strips quotes from a string. If the source string contains no quotes, it is 13// returned. If the source string contains quotes, a new string is allocated 14// and the string is copied into it, character by character, skipping "'s. If 15// a non-null pointer is returned, the caller is responsible for freeing it. 16template <typename Character> 17static Character const* __cdecl strip_quotes(Character const* const source) throw() 18{ 19 // Count the number of quotation marks in the string, and compute the length 20 // of the string, in case we need to allocate a new string: 21 size_t quote_count = 0; 22 size_t source_length = 0; 23 for (Character const* it = source; *it; ++it) 24 { 25 if (*it == '\"') 26 ++quote_count; 27 28 ++source_length; 29 } 30 31 // No quotes? No problem! 32 if (quote_count == 0) 33 return nullptr; 34 35 size_t const destination_length = source_length - quote_count + 1; 36 __crt_unique_heap_ptr<Character> destination(_calloc_crt_t(Character, destination_length)); 37 if (destination.get() == nullptr) 38 return nullptr; 39 40 // Copy the string, stripping quotation marks: 41 Character* destination_it = destination.get(); 42 for (Character const* source_it = source; *source_it; ++source_it) 43 { 44 if (*source_it == '\"') 45 continue; 46 47 *destination_it++ = *source_it; 48 } 49 50 *destination_it = '\0'; 51 return destination.detach(); 52} 53 54// Gets the path to the %TMP% directory 55template <typename Character> 56static Character const* __cdecl get_tmp_directory() throw() 57{ 58 typedef __acrt_stdio_char_traits<Character> stdio_traits; 59 60 static Character const tmp_name[] = { 'T', 'M', 'P', '\0' }; 61 62 Character* tmp_value = nullptr; 63 if (_ERRCHECK_EINVAL(stdio_traits::tdupenv_s_crt(&tmp_value, nullptr, tmp_name)) != 0) 64 return nullptr; 65 66 return tmp_value; 67} 68 69 70 71// Gets the directory relative to which the temporary name should be formed. 72// The directory to be used is returned via the 'result' out parameter. The 73// '*result' pointer is never null on return. If '*result' needs to be freed 74// by the caller, the function also returns the pointer; if null is returned, 75// no caller cleanup is required. 76template <typename Character> 77static Character const* __cdecl get_directory( 78 Character const* const alternative, 79 Character const** const result 80 ) throw() 81{ 82 typedef __acrt_stdio_char_traits<Character> stdio_traits; 83 84 __crt_unique_heap_ptr<Character const> tmp(get_tmp_directory<Character>()); 85 if (tmp.get() != nullptr) 86 { 87 if (stdio_traits::taccess_s(tmp.get(), 0) == 0) 88 return *result = tmp.detach(); 89 90 // Otherwise, try stripping quotes out of the TMP path and check again: 91 __crt_unique_heap_ptr<Character const> unquoted_tmp(strip_quotes(tmp.get())); 92 if (unquoted_tmp.get() != nullptr && stdio_traits::taccess_s(unquoted_tmp.get(), 0) == 0) 93 return *result = unquoted_tmp.detach(); 94 } 95 96 // Otherwise, the TMP path is not usable; use the alternative path if one 97 // was provided and is accessible: 98 if (alternative != nullptr && stdio_traits::taccess_s(alternative, 0) == 0) 99 return (*result = alternative), nullptr; 100 101 // Otherwise, fall back to \ or .: 102 static Character const root_fallback[] = { '\\', '\0' }; 103 static Character const cwd_fallback [] = { '.', '\0' }; 104 105 if (stdio_traits::taccess_s(root_fallback, 0) == 0) 106 return (*result = root_fallback), nullptr; 107 108 return (*result = cwd_fallback), nullptr; 109} 110 111 112 113// The path_buffer is a pointer to the beginning of the buffer into which we 114// are formatting the temporary path. The suffix_pointer is a pointer into 115// that same buffer, to the position where the unique identifier is to be 116// written. The suffix_count is the number of characters that can be written 117// to the suffix_pointer. The prefix_length is the length of the prefix that 118// appears before the suffix in the path_buffer already. 119template <typename Character> 120static bool __cdecl compute_name( 121 Character const* const path_buffer, 122 Character* const suffix_pointer, 123 size_t const suffix_count, 124 size_t const prefix_length 125 ) throw() 126{ 127 typedef __acrt_stdio_char_traits<Character> stdio_traits; 128 129 // Re-initialize _tempoff if necessary. If we don't re-init _tempoff, we 130 // can get into an infinate loop (e.g., (a) _tempoff is a big number on 131 // entry, (b) prefix is a long string (e.g., 8 chars) and all tempfiles 132 // with that prefix exist, (c) _tempoff will never equal first and we'll 133 // loop forever). 134 135 // [NOTE: To avoid a conflict that causes the same bug as that discussed 136 // above, _tempnam() uses _tempoff; tmpnam() uses _tmpoff] 137 138 bool return_value = false; 139 140 __acrt_lock(__acrt_tempnam_lock); 141 __try 142 { 143 if (_old_pfxlen < prefix_length) 144 _tempoff = 1; 145 146 _old_pfxlen = static_cast<unsigned int>(prefix_length); 147 148 unsigned const first = _tempoff; 149 150 errno_t const saved_errno = errno; 151 do 152 { 153 ++_tempoff; 154 if (_tempoff - first > _TMP_MAX_S) 155 { 156 errno = saved_errno; 157 __leave; 158 } 159 160 // The maximum length string returned by the conversion is ten 161 // characters, assuming a 32-bit unsigned integer, so there is 162 // sufficient room in the result buffer for it. 163 _ERRCHECK(stdio_traits::ultot_s(_tempoff, suffix_pointer, suffix_count, 10)); 164 errno = 0; 165 } 166 while (stdio_traits::taccess_s(path_buffer, 0) == 0 || errno == EACCES); 167 168 errno = saved_errno; 169 return_value = true; 170 } 171 __finally 172 { 173 __acrt_unlock(__acrt_tempnam_lock); 174 } 175 __endtry 176 177 return return_value; 178} 179 180 181 182// Generates a unique file name within the temporary directory. If the TMP 183// environment variable is defined and is accessible, that directory is used. 184// Otherwise, the given 'alternative' directory is used, if that is non-null and 185// the path is accessible. Otherwise, the root of the drive is used if it is 186// accessible; otherwise, the local directory is used. 187// 188// Returns a pointer to the resulting file name on success; returns nullptr on 189// failure. If a non-null pointer is returned, the caller is responsible for 190// freeing it by calling 'free()'. 191template <typename Character> 192_Success_(return != 0) 193static Character* __cdecl common_tempnam( 194 Character const* const alternative, 195 Character const* const prefix, 196 int const block_use, 197 char const* const file_name, 198 int const line_number 199 ) throw() 200{ 201 // These are referenced only in the Debug CRT build 202 UNREFERENCED_PARAMETER(block_use); 203 UNREFERENCED_PARAMETER(file_name); 204 UNREFERENCED_PARAMETER(line_number); 205 206 typedef __acrt_stdio_char_traits<Character> stdio_traits; 207 208 Character const* directory = nullptr; 209 __crt_unique_heap_ptr<Character const> const directory_cleanup(get_directory(alternative, &directory)); 210 211 unsigned const prefix_length = prefix != nullptr 212 ? static_cast<unsigned>(stdio_traits::tcslen(prefix)) 213 : 0; 214 215 // The 12 allows for a backslash, a ten character temporary string, and a 216 // null terminator. 217 unsigned const buffer_size = static_cast<unsigned>(stdio_traits::tcslen(directory)) + prefix_length + 12; 218 219 __crt_unique_heap_ptr<Character, __crt_public_free_policy> result( 220 static_cast<Character*>(_calloc_dbg( 221 buffer_size, 222 sizeof(Character), 223 block_use, 224 file_name, 225 line_number))); 226 227 if (!result) 228 return nullptr; 229 230 *result.get() = 0; 231 _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, directory)); 232 233 if (__crt_stdio_path_requires_backslash(directory)) 234 { 235 static Character const backslash[] = { '\\', '\0' }; 236 _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, backslash)); 237 } 238 239 if (prefix != nullptr) 240 { 241 _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, prefix)); 242 } 243 244 Character* const ptr = result.get() + stdio_traits::tcslen(result.get()); 245 size_t const ptr_size = buffer_size - (ptr - result.get()); 246 247 if (!compute_name(result.get(), ptr, ptr_size, prefix_length)) 248 return nullptr; 249 250 return result.detach(); 251} 252 253 254 255extern "C" char* __cdecl _tempnam( 256 char const* const alternative, 257 char const* const prefix 258 ) 259{ 260 return common_tempnam(alternative, prefix, _NORMAL_BLOCK, nullptr, 0); 261} 262 263extern "C" wchar_t* __cdecl _wtempnam( 264 wchar_t const* const alternative, 265 wchar_t const* const prefix 266 ) 267{ 268 return common_tempnam(alternative, prefix, _NORMAL_BLOCK, nullptr, 0); 269} 270 271#ifdef _DEBUG 272 273extern "C" char* __cdecl _tempnam_dbg( 274 char const* const alternative, 275 char const* const prefix, 276 int const block_use, 277 char const* const file_name, 278 int const line_number 279 ) 280{ 281 return common_tempnam(alternative, prefix, block_use, file_name, line_number); 282} 283 284extern "C" wchar_t* __cdecl _wtempnam_dbg( 285 wchar_t const* const alternative, 286 wchar_t const* const prefix, 287 int const block_use, 288 char const* const file_name, 289 int const line_number 290 ) 291{ 292 return common_tempnam(alternative, prefix, block_use, file_name, line_number); 293} 294 295#endif