Reactos
at master 212 lines 7.3 kB view raw
1// 2// spawnvp.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines the -vp and -vpe flavors of the _exec() and _spawn() functions. See 7// the comments in spawnv.cpp for details of the various flavors of these 8// functions. 9// 10#include <corecrt_internal.h> 11#include <corecrt_internal_traits.h> 12#include <errno.h> 13#include <stdlib.h> 14#include <string.h> 15#include <malloc.h> 16#include <process.h> 17#include <mbstring.h> 18 19 20 21template <typename Character> 22static intptr_t __cdecl common_spawnvp( 23 int const mode, 24 Character const* const file_name, 25 Character const* const* const arguments, 26 Character const* const* const environment 27 ) throw() 28{ 29 typedef __crt_char_traits<Character> traits; 30 31 _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1); 32 _VALIDATE_RETURN(file_name[0] != '\0', EINVAL, -1); 33 _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1); 34 _VALIDATE_RETURN(arguments[0] != nullptr, EINVAL, -1); 35 _VALIDATE_RETURN(arguments[0][0] != '\0', EINVAL, -1); 36 37 __crt_errno_guard const guard_errno; 38 39 // Attempt to perform the spawn with the given file name. If this succeeds 40 // and is a spawn operation (mode != _P_OVERLAY), then it returns a status 41 // code other than -1 and we can just return it. If this succeds and is an 42 // exec operation (mode == _P_OVERLAY), then this call does not return. 43 intptr_t const initial_result = traits::tspawnve(mode, file_name, arguments, environment); 44 if (initial_result != -1) 45 return initial_result; 46 47 // If the spawn attempt failed, try to determine why. ENOENT indicates that 48 // the spawn operation itself failed, and there's nothing we can do: 49 if (errno != ENOENT) 50 return -1; 51 52 // If the file name is an absolute or relative path, then we do not search 53 // the %PATH%, so there is nothing left to do: 54 if (traits::tcschr(file_name, '\\') != nullptr) 55 return -1; 56 57 if (traits::tcschr(file_name, '/') != nullptr) 58 return -1; 59 60 if (file_name[1] == ':') 61 return -1; 62 63 // Otherwise, the file name string just names an executable. Let's try 64 // appending it to each path in the %PATH% and see if we can find the 65 // file to execute: 66 Character const path_name[] = { 'P', 'A', 'T', 'H', '\0' }; 67 __crt_unique_heap_ptr<Character> path_value; 68 if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(path_value.get_address_of(), nullptr, path_name)) != 0) 69 return -1; 70 71 if (!path_value) 72 return -1; 73 74 // This will be used to store alternative path names to the executable: 75 __crt_unique_heap_ptr<Character> const owned_file_buffer(_calloc_crt_t(Character, _MAX_PATH)); 76 if (!owned_file_buffer) 77 return -1; 78 79 Character* file_buffer = owned_file_buffer.get(); 80 Character* path_state = path_value.get(); 81 while ((path_state = traits::tgetpath(path_state, file_buffer, _MAX_PATH - 1)) != 0) 82 { 83 if (!*file_buffer) 84 break; 85 86 // Append a '\' if necessary: 87 Character* const last_character_it = file_buffer + traits::tcslen(file_buffer) - 1; 88 if (last_character_it != traits::tcsrchr(file_buffer, '\\') && 89 last_character_it != traits::tcsrchr(file_buffer, '/')) 90 { 91 Character const backslash_string[] = { '\\', '\0' }; 92 _ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, backslash_string)); 93 } 94 95 // Make sure that the buffer is sufficiently large to hold the file name 96 // concatenated onto the end of this %PATH% component. If it is not, we 97 // return immediately (errno will still be set from the last call to 98 // _spawnve(). 99 if (traits::tcslen(file_buffer) + traits::tcslen(file_name) >= _MAX_PATH) 100 break; 101 102 _ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, file_name)); 103 104 // Try the spawn again with the newly constructed path. Like before, if 105 // this succeeds and is a spawn operation, then a status is returned and 106 // we can return immediately. If this succeeds and is an exec operation, 107 // the call does not return. 108 errno = 0; 109 intptr_t const result = traits::tspawnve(mode, file_buffer, arguments, environment); 110 if (result != -1) 111 return result; 112 113 // Two classes of failures are acceptable here: either the operation 114 // failed because the spawn operation itself failed (e.g., if the file 115 // could not be found)... 116 if (errno == ENOENT || _doserrno == ERROR_NOT_READY) 117 continue; 118 119 // ...or the path is a UNC path... 120 bool const is_unc_path_with_slashes = 121 traits::tcschr(file_buffer, '/') == file_buffer && 122 traits::tcschr(file_buffer + 1, '/') == file_buffer + 1; 123 124 bool const is_unc_path_with_backslashes = 125 traits::tcschr(file_buffer, '\\') == file_buffer && 126 traits::tcschr(file_buffer + 1, '\\') == file_buffer + 1; 127 128 if (is_unc_path_with_slashes || is_unc_path_with_backslashes) 129 continue; 130 131 // ...otherwise, we report the error back to the caller: 132 break; 133 } 134 135 return -1; 136} 137 138 139 140extern "C" intptr_t __cdecl _execvp( 141 char const* const file_name, 142 char const* const* const arguments 143 ) 144{ 145 return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast<char const* const* const>(nullptr)); 146} 147 148extern "C" intptr_t __cdecl _execvpe( 149 char const* const file_name, 150 char const* const* const arguments, 151 char const* const* const environment 152 ) 153{ 154 return common_spawnvp(_P_OVERLAY, file_name, arguments, environment); 155} 156 157extern "C" intptr_t __cdecl _spawnvp( 158 int const mode, 159 char const* const file_name, 160 char const* const* const arguments 161 ) 162{ 163 return common_spawnvp(mode, file_name, arguments, static_cast<char const* const* const>(nullptr)); 164} 165 166extern "C" intptr_t __cdecl _spawnvpe( 167 int const mode, 168 char const* const file_name, 169 char const* const* const arguments, 170 char const* const* const environment 171 ) 172{ 173 return common_spawnvp(mode, file_name, arguments, environment); 174} 175 176 177 178extern "C" intptr_t __cdecl _wexecvp( 179 wchar_t const* const file_name, 180 wchar_t const* const* const arguments 181 ) 182{ 183 return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast<wchar_t const* const* const>(nullptr)); 184} 185 186extern "C" intptr_t __cdecl _wexecvpe( 187 wchar_t const* const file_name, 188 wchar_t const* const* const arguments, 189 wchar_t const* const* const environment 190 ) 191{ 192 return common_spawnvp(_P_OVERLAY, file_name, arguments, environment); 193} 194 195extern "C" intptr_t __cdecl _wspawnvp( 196 int const mode, 197 wchar_t const* const file_name, 198 wchar_t const* const* const arguments 199 ) 200{ 201 return common_spawnvp(mode, file_name, arguments, static_cast<wchar_t const* const* const>(nullptr)); 202} 203 204extern "C" intptr_t __cdecl _wspawnvpe( 205 int const mode, 206 wchar_t const* const file_name, 207 wchar_t const* const* const arguments, 208 wchar_t const* const* const environment 209 ) 210{ 211 return common_spawnvp(mode, file_name, arguments, environment); 212}