xref: /reactos/base/system/subst/subst.c (revision c2c66aff)
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