1 /* 2 * PROJECT: ReactOS Subst Command 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/system/subst/subst.c 5 * PURPOSE: Maps a path with a drive letter 6 * PROGRAMMERS: Sam Arun Raj 7 * Peter Hater 8 * Hermes Belusca-Maito 9 */ 10 11 /* INCLUDES *****************************************************************/ 12 13 #include <stdio.h> 14 15 #define WIN32_NO_STATUS 16 #include <windef.h> 17 #include <winbase.h> 18 19 #include <conutils.h> 20 21 #include "resource.h" 22 23 /* FUNCTIONS ****************************************************************/ 24 25 VOID PrintError(IN DWORD ErrCode) 26 { 27 // DWORD dwLength = 0; 28 PWSTR pMsgBuf = NULL; 29 30 #if 0 31 if (ErrCode == ERROR_SUCCESS) 32 return; 33 #endif 34 35 /* Retrieve the message string without appending extra newlines */ 36 // dwLength = 37 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 38 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, 39 NULL, 40 ErrCode, 41 LANG_USER_DEFAULT, 42 (PWSTR)&pMsgBuf, 43 0, NULL); 44 if (pMsgBuf /* && dwLength */) 45 { 46 ConResPrintf(StdErr, IDS_FAILED_WITH_ERRORCODE, 47 ErrCode, pMsgBuf); 48 LocalFree(pMsgBuf); 49 } 50 } 51 52 ULONG QuerySubstedDrive(IN WCHAR DriveLetter, 53 IN OUT PWSTR* TargetPath OPTIONAL, 54 IN OUT PULONG Size) 55 { 56 ULONG Result = ERROR_INVALID_DRIVE; 57 WCHAR Drive[] = L"A:"; 58 DWORD dwSize, CharCount = 0; 59 PWSTR lpTargetPath = NULL, tmp; 60 61 Drive[0] = DriveLetter; 62 63 /* Check whether the user has given a pointer to a target path buffer */ 64 if (!TargetPath) 65 { 66 /* No, therefore use a local buffer */ 67 dwSize = MAX_PATH; 68 lpTargetPath = (PWSTR)HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(WCHAR)); 69 if (!lpTargetPath) 70 return ERROR_NOT_ENOUGH_MEMORY; 71 } 72 else 73 { 74 /* Just use the user-given pointer to a buffer; Size should point to a valid ULONG */ 75 if (!Size) 76 return ERROR_INVALID_PARAMETER; 77 78 lpTargetPath = *TargetPath; 79 dwSize = *Size; 80 } 81 82 Retry: 83 /* Try querying DOS device information */ 84 CharCount = QueryDosDeviceW(Drive, lpTargetPath, dwSize); 85 if (!CharCount) 86 Result = GetLastError(); 87 88 if (!CharCount && (Result == ERROR_INSUFFICIENT_BUFFER)) 89 { 90 /* Reallocate the buffer with double size */ 91 dwSize *= 2; 92 tmp = (PWSTR)HeapReAlloc(GetProcessHeap(), 0, lpTargetPath, dwSize * sizeof(WCHAR)); 93 if (!tmp) 94 { 95 /* Memory problem, bail out */ 96 CharCount = 0; 97 Result = ERROR_NOT_ENOUGH_MEMORY; 98 } 99 else 100 { 101 /* Retry again */ 102 lpTargetPath = tmp; 103 goto Retry; 104 } 105 } 106 107 if (CharCount) 108 { 109 if ( wcsncmp(lpTargetPath, L"\\??\\", 4) == 0 && 110 ( (lpTargetPath[4] >= L'A' && lpTargetPath[4] <= L'Z') || 111 (lpTargetPath[4] >= L'a' && lpTargetPath[4] <= L'z') ) ) 112 { 113 /* The drive exists and is SUBSTed */ 114 Result = ERROR_IS_SUBSTED; 115 } 116 #if 0 117 else 118 { 119 /* The drive exists but is not SUBSTed */ 120 Result = ERROR_INVALID_DRIVE; 121 } 122 #endif 123 } 124 125 if (!TargetPath) 126 { 127 /* Free the local buffer */ 128 HeapFree(GetProcessHeap(), 0, lpTargetPath); 129 } 130 else 131 { 132 /* Update the user-given pointers */ 133 *TargetPath = lpTargetPath; 134 *Size = dwSize; 135 } 136 137 return Result; 138 } 139 140 VOID DumpSubstedDrives(VOID) 141 { 142 WCHAR DriveLetter; 143 PWSTR lpTargetPath = NULL; 144 DWORD dwSize; 145 UCHAR i = 0; 146 147 dwSize = MAX_PATH; 148 lpTargetPath = (PWSTR)HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(WCHAR)); 149 if (!lpTargetPath) 150 return; 151 152 while (i < 26) 153 { 154 DriveLetter = L'A' + i; 155 if (QuerySubstedDrive(DriveLetter, &lpTargetPath, &dwSize) == ERROR_IS_SUBSTED) 156 { 157 ConPrintf(StdOut, L"%c:\\: => %s\n", DriveLetter, lpTargetPath + 4); 158 } 159 160 i++; 161 } 162 163 HeapFree(GetProcessHeap(), 0, lpTargetPath); 164 } 165 166 INT DeleteSubst(IN PWSTR Drive) 167 { 168 DWORD dwResult; 169 170 if ((wcslen(Drive) != 2) || (Drive[1] != L':')) 171 { 172 dwResult = ERROR_INVALID_PARAMETER; 173 goto Quit; 174 } 175 176 if (QuerySubstedDrive(Drive[0], NULL, NULL) != ERROR_IS_SUBSTED) 177 { 178 dwResult = ERROR_INVALID_PARAMETER; 179 goto Quit; 180 } 181 182 if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION, Drive, NULL)) 183 dwResult = GetLastError(); 184 else 185 dwResult = ERROR_SUCCESS; 186 187 Quit: 188 switch (dwResult) 189 { 190 case ERROR_SUCCESS: 191 break; 192 193 // case ERROR_INVALID_DRIVE: 194 case ERROR_INVALID_PARAMETER: 195 { 196 ConResPrintf(StdErr, IDS_INVALID_PARAMETER2, Drive); 197 return 1; 198 } 199 200 case ERROR_ACCESS_DENIED: 201 { 202 ConResPrintf(StdErr, IDS_ACCESS_DENIED, Drive); 203 return 1; 204 } 205 206 default: 207 { 208 PrintError(GetLastError()); 209 return 1; 210 } 211 } 212 213 return 0; 214 } 215 216 INT AddSubst(IN PWSTR Drive, IN PWSTR Path) 217 { 218 DWORD dwResult, dwPathAttr; 219 220 if ((wcslen(Drive) != 2) || (Drive[1] != L':')) 221 { 222 dwResult = ERROR_INVALID_PARAMETER; 223 goto Quit; 224 } 225 226 /* 227 * Even if DefineDosDevice allows to map files to drive letters (yes yes!!) 228 * it is not the purpose of SUBST to allow that. Therefore check whether 229 * the given path exists and really is a path to a directory, and if not, 230 * just fail with an error. 231 */ 232 dwPathAttr = GetFileAttributesW(Path); 233 if ( (dwPathAttr == INVALID_FILE_ATTRIBUTES) || 234 !(dwPathAttr & FILE_ATTRIBUTE_DIRECTORY) ) 235 { 236 dwResult = ERROR_PATH_NOT_FOUND; 237 goto Quit; 238 } 239 240 /* 241 * QuerySubstedDrive (via QueryDosDevice) returns ERROR_FILE_NOT_FOUND only 242 * if there is no already existing drive mapping. For all other results 243 * (existing drive, be it already subst'ed or not, or other errors...) 244 * no attempt at defining a drive mapping should be done. 245 */ 246 dwResult = QuerySubstedDrive(Drive[0], NULL, NULL); 247 if (dwResult != ERROR_FILE_NOT_FOUND) 248 goto Quit; 249 250 if (!DefineDosDeviceW(0, Drive, Path)) 251 dwResult = GetLastError(); 252 else 253 dwResult = ERROR_SUCCESS; 254 255 Quit: 256 switch (dwResult) 257 { 258 case ERROR_SUCCESS: 259 break; 260 261 case ERROR_INVALID_DRIVE: 262 case ERROR_INVALID_PARAMETER: 263 { 264 ConResPrintf(StdErr, IDS_INVALID_PARAMETER2, Drive); 265 return 1; 266 } 267 268 case ERROR_IS_SUBSTED: 269 { 270 ConResPuts(StdErr, IDS_DRIVE_ALREADY_SUBSTED); 271 return 1; 272 } 273 274 case ERROR_FILE_NOT_FOUND: 275 case ERROR_PATH_NOT_FOUND: 276 { 277 ConResPrintf(StdErr, IDS_PATH_NOT_FOUND, Path); 278 return 1; 279 } 280 281 case ERROR_ACCESS_DENIED: 282 { 283 ConResPrintf(StdErr, IDS_ACCESS_DENIED, Path); 284 return 1; 285 } 286 287 default: 288 { 289 PrintError(GetLastError()); 290 return 1; 291 } 292 } 293 294 return 0; 295 } 296 297 int wmain(int argc, WCHAR* argv[]) 298 { 299 INT i; 300 301 /* Initialize the Console Standard Streams */ 302 ConInitStdStreams(); 303 304 for (i = 0; i < argc; i++) 305 { 306 if (!_wcsicmp(argv[i], L"/?")) 307 { 308 ConResPuts(StdOut, IDS_USAGE); 309 return 0; 310 } 311 } 312 313 if (argc < 3) 314 { 315 if (argc >= 2) 316 { 317 ConResPrintf(StdErr, IDS_INVALID_PARAMETER, argv[1]); 318 return 1; 319 } 320 DumpSubstedDrives(); 321 return 0; 322 } 323 324 if (argc > 3) 325 { 326 ConResPrintf(StdErr, IDS_INCORRECT_PARAMETER_COUNT, argv[3]); 327 return 1; 328 } 329 330 if (!_wcsicmp(argv[1], L"/D")) 331 return DeleteSubst(argv[2]); 332 if (!_wcsicmp(argv[2], L"/D")) 333 return DeleteSubst(argv[1]); 334 return AddSubst(argv[1], argv[2]); 335 } 336 337 /* EOF */ 338