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