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
PrintError(IN DWORD ErrCode)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
QuerySubstedDrive(IN WCHAR DriveLetter,IN OUT PWSTR * TargetPath OPTIONAL,IN OUT PULONG Size)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
DumpSubstedDrives(VOID)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
DeleteSubst(IN PWSTR Drive)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
AddSubst(IN PWSTR Drive,IN PWSTR Path)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
wmain(int argc,WCHAR * argv[])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