Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

437 lines
12 KiB

  1. /*++
  2. Copyright (c) 1992-2000 Microsoft Corporation
  3. Module Name:
  4. subst.cxx
  5. Abstract:
  6. Utility to associate a path to a drive letter
  7. Author:
  8. THERESES 12-August-1992
  9. Revision History:
  10. --*/
  11. #define _NTAPI_ULIB_
  12. #include "ulib.hxx"
  13. #include "arg.hxx"
  14. #include "array.hxx"
  15. #include "smsg.hxx"
  16. #include "rtmsg.h"
  17. #include "wstring.hxx"
  18. #include "path.hxx"
  19. #include "substrng.hxx"
  20. #include "system.hxx"
  21. #include "ulibcl.hxx"
  22. #include "subst.hxx"
  23. #include "dir.hxx"
  24. #include "ntrtl.h"
  25. BOOLEAN
  26. QuerySubstedDrive(
  27. IN DWORD DriveNumber,
  28. OUT LPWSTR PhysicalDrive,
  29. IN DWORD PhysicalDriveLength,
  30. IN LPDWORD DosError
  31. );
  32. VOID
  33. DisplaySubstUsage(
  34. IN OUT PMESSAGE Message
  35. )
  36. /*++
  37. Routine Description:
  38. This routine displays the usage for the dos 5 label program.
  39. Arguments:
  40. Message - Supplies an outlet for the messages.
  41. Return Value:
  42. None.
  43. --*/
  44. {
  45. Message->Set(MSG_SUBST_INFO);
  46. Message->Display("");
  47. Message->Set(MSG_SUBST_USAGE);
  48. Message->Display("");
  49. }
  50. BOOLEAN
  51. DeleteSubst(
  52. IN LPWSTR Drive,
  53. IN OUT PMESSAGE Message
  54. )
  55. {
  56. BOOL Success;
  57. FSTRING AuxString;
  58. DWORD Status;
  59. WCHAR Buffer[ MAX_PATH + 8 ];
  60. Success = QuerySubstedDrive( *Drive - ( WCHAR )'@',
  61. Buffer,
  62. sizeof( Buffer ) / sizeof( WCHAR ),
  63. &Status );
  64. if( Success ) {
  65. Success = DefineDosDevice( DDD_REMOVE_DEFINITION,
  66. Drive,
  67. NULL );
  68. if( !Success ) {
  69. Status = GetLastError();
  70. }
  71. }
  72. if (!Success) {
  73. if( Status == ERROR_ACCESS_DENIED ) {
  74. AuxString.Initialize( Drive );
  75. Message->Set(MSG_SUBST_ACCESS_DENIED);
  76. Message->Display("%W",&AuxString);
  77. } else {
  78. AuxString.Initialize( Drive );
  79. Message->Set(MSG_SUBST_INVALID_PARAMETER);
  80. Message->Display("%W",&AuxString);
  81. }
  82. }
  83. return Success != FALSE;
  84. }
  85. BOOLEAN
  86. AddSubst(
  87. IN LPWSTR Drive,
  88. IN LPWSTR PhysicalDrive,
  89. IN DWORD PhysicalDriveLength,
  90. IN OUT PMESSAGE Message
  91. )
  92. {
  93. DWORD Status;
  94. FSTRING AuxString;
  95. WCHAR Buffer[ MAX_PATH + 8 ];
  96. if( !QuerySubstedDrive( Drive[0] - '@',
  97. Buffer,
  98. sizeof( Buffer ) / sizeof( WCHAR ),
  99. &Status ) ) {
  100. if( Status == ERROR_FILE_NOT_FOUND ) {
  101. if ( wcslen(PhysicalDrive) == 3 &&
  102. PhysicalDrive[1] == ':' &&
  103. PhysicalDrive[2] == '\\' &&
  104. PhysicalDrive[3] == 0 ) {
  105. UNICODE_STRING string;
  106. if ( !RtlDosPathNameToNtPathName_U(PhysicalDrive, &string, NULL, NULL) ) {
  107. Status = GetLastError();
  108. } else {
  109. string.Buffer[string.Length/sizeof(string.Buffer[0]) - 1] = 0;
  110. if ( !DefineDosDevice(DDD_RAW_TARGET_PATH, Drive, string.Buffer) ) {
  111. Status = GetLastError();
  112. } else {
  113. Status = ERROR_SUCCESS;
  114. }
  115. RtlFreeUnicodeString(&string);
  116. }
  117. } else if( !DefineDosDevice( 0, Drive, PhysicalDrive ) ) {
  118. Status = GetLastError();
  119. } else {
  120. Status = ERROR_SUCCESS;
  121. }
  122. }
  123. } else {
  124. Status = ERROR_IS_SUBSTED;
  125. }
  126. if( Status != ERROR_SUCCESS ) {
  127. if( Status == ERROR_IS_SUBSTED ) {
  128. Message->Set(MSG_SUBST_ALREADY_SUBSTED);
  129. Message->Display("");
  130. } else if (Status == ERROR_FILE_NOT_FOUND) {
  131. AuxString.Initialize( PhysicalDrive );
  132. Message->Set(MSG_SUBST_PATH_NOT_FOUND);
  133. Message->Display("%W", &AuxString);
  134. } else if (Status == ERROR_ACCESS_DENIED) {
  135. AuxString.Initialize( PhysicalDrive );
  136. Message->Set(MSG_SUBST_ACCESS_DENIED);
  137. Message->Display("%W", &AuxString);
  138. } else {
  139. AuxString.Initialize( Drive );
  140. Message->Set(MSG_SUBST_INVALID_PARAMETER);
  141. Message->Display("%W", &AuxString );
  142. }
  143. return( FALSE );
  144. } else {
  145. return( TRUE );
  146. }
  147. }
  148. BOOLEAN
  149. QuerySubstedDrive(
  150. IN DWORD DriveNumber,
  151. OUT LPWSTR PhysicalDrive,
  152. IN DWORD PhysicalDriveLength,
  153. IN LPDWORD DosError
  154. )
  155. {
  156. WCHAR DriveName[3];
  157. FSTRING DosDevicesPattern;
  158. FSTRING DeviceName;
  159. CHNUM Position;
  160. DriveName[0] = ( WCHAR )( DriveNumber + '@' );
  161. DriveName[1] = ( WCHAR )':';
  162. DriveName[2] = ( WCHAR )'\0';
  163. if( QueryDosDevice( DriveName,
  164. PhysicalDrive,
  165. PhysicalDriveLength ) != 0 ) {
  166. DosDevicesPattern.Initialize( (LPWSTR)L"\\??\\" );
  167. DeviceName.Initialize( PhysicalDrive );
  168. Position = DeviceName.Strstr( &DosDevicesPattern );
  169. if( Position == 0 ) {
  170. DeviceName.DeleteChAt( 0, DosDevicesPattern.QueryChCount() );
  171. *DosError = ERROR_SUCCESS;
  172. return( TRUE );
  173. } else {
  174. //
  175. // This is not a Dos device
  176. //
  177. *DosError = ERROR_INVALID_PARAMETER;
  178. return( FALSE );
  179. }
  180. } else {
  181. *DosError = GetLastError();
  182. return( FALSE );
  183. }
  184. }
  185. VOID
  186. DumpSubstedDrives (
  187. IN OUT PMESSAGE Message
  188. )
  189. {
  190. DSTRING Source;
  191. WCHAR LinkBuffer[MAX_PATH + 8];
  192. DWORD i;
  193. FSTRING AuxString;
  194. DWORD ErrorCode;
  195. Source.Initialize(L"D:\\");
  196. Message->Set(MSG_SUBST_SUBSTED_DRIVE);
  197. for (i=1;i<=MAXIMUM_DRIVES;i++) {
  198. if (QuerySubstedDrive(i,LinkBuffer,sizeof(LinkBuffer),&ErrorCode)) {
  199. Source.SetChAt((WCHAR)(i+'@'),0);
  200. if (wcslen(LinkBuffer) == 2 &&
  201. LinkBuffer[1] == ':' &&
  202. LinkBuffer[2] == 0) {
  203. LinkBuffer[2] = '\\';
  204. LinkBuffer[3] = 0;
  205. }
  206. AuxString.Initialize( LinkBuffer );
  207. Message->Display("%W%W", &Source, &AuxString);
  208. }
  209. }
  210. }
  211. INT
  212. __cdecl
  213. main(
  214. )
  215. /*++
  216. Routine Description:
  217. This routine emulates the dos 5 subst command for NT.
  218. Arguments:
  219. None.
  220. Return Value:
  221. 1 - An error occured.
  222. 0 - Success.
  223. --*/
  224. {
  225. STREAM_MESSAGE msg;
  226. ARGUMENT_LEXEMIZER arglex;
  227. ARRAY lex_array;
  228. ARRAY arg_array;
  229. STRING_ARGUMENT progname;
  230. PATH_ARGUMENT virtualdrive_arg;
  231. PATH_ARGUMENT physicaldrive_arg;
  232. FLAG_ARGUMENT help_arg;
  233. FLAG_ARGUMENT delete_arg;
  234. PWSTRING p;
  235. BOOL Success=TRUE;
  236. if (!msg.Initialize(Get_Standard_Output_Stream(),
  237. Get_Standard_Input_Stream(),
  238. Get_Standard_Error_Stream())) {
  239. return 1;
  240. }
  241. if (!lex_array.Initialize() || !arg_array.Initialize()) {
  242. return 1;
  243. }
  244. if (!arglex.Initialize(&lex_array)) {
  245. return 1;
  246. }
  247. arglex.PutSwitches( "/" );
  248. arglex.PutStartQuotes( "\"" );
  249. arglex.PutEndQuotes( "\"" );
  250. arglex.PutSeparators( " \t" );
  251. arglex.SetCaseSensitive(FALSE);
  252. if (!arglex.PrepareToParse()) {
  253. return 1;
  254. }
  255. if ( !arg_array.Initialize() ) {
  256. return 1;
  257. }
  258. if (!progname.Initialize("*") ||
  259. !help_arg.Initialize("/?") ||
  260. !virtualdrive_arg.Initialize("*", FALSE) ||
  261. !physicaldrive_arg.Initialize("*",FALSE) ||
  262. !delete_arg.Initialize("/D") ) {
  263. return 1;
  264. }
  265. if (!arg_array.Put(&progname) ||
  266. !arg_array.Put(&virtualdrive_arg) ||
  267. !arg_array.Put(&physicaldrive_arg) ||
  268. !arg_array.Put(&help_arg) ||
  269. !arg_array.Put(&delete_arg) ) {
  270. return 1;
  271. }
  272. if (!arglex.DoParsing(&arg_array)) {
  273. if (arglex.QueryLexemeCount() > MAXIMUM_SUBST_ARGS) {
  274. msg.Set(MSG_SUBST_TOO_MANY_PARAMETERS);
  275. msg.Display("%W", p = arglex.GetLexemeAt(MAXIMUM_SUBST_ARGS));
  276. } else {
  277. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  278. msg.Display("%W", p = arglex.QueryInvalidArgument());
  279. }
  280. DELETE(p);
  281. return 1;
  282. }
  283. if (help_arg.QueryFlag()) {
  284. DisplaySubstUsage(&msg);
  285. return 0;
  286. }
  287. if (delete_arg.IsValueSet() &&
  288. virtualdrive_arg.IsValueSet() &&
  289. physicaldrive_arg.IsValueSet()) {
  290. msg.Set(MSG_SUBST_TOO_MANY_PARAMETERS);
  291. msg.Display("%W", delete_arg.GetPattern() );
  292. return 1;
  293. }
  294. if (delete_arg.IsValueSet() &&
  295. !virtualdrive_arg.IsValueSet() &&
  296. !physicaldrive_arg.IsValueSet()) {
  297. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  298. msg.Display("%W", delete_arg.GetPattern());
  299. return 1;
  300. }
  301. //
  302. // Validate virtual drive
  303. // A virtual drive MUST have the format <drive letter>:
  304. // Anything that doesn't have this format is considered an invalid parameter
  305. //
  306. if( virtualdrive_arg.IsValueSet() &&
  307. ( ( virtualdrive_arg.GetPath()->GetPathString()->QueryChCount() != 2 ) ||
  308. ( virtualdrive_arg.GetPath()->GetPathString()->QueryChAt( 1 ) != ( WCHAR )':' ) )
  309. ) {
  310. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  311. msg.Display("%W", virtualdrive_arg.GetPath()->GetPathString() );
  312. return 1;
  313. }
  314. //
  315. // Validate physical drive
  316. // A physical drive CANNOT have the format <drive letter>:
  317. //
  318. if( physicaldrive_arg.IsValueSet() &&
  319. ( physicaldrive_arg.GetPath()->GetPathString()->QueryChCount() == 2 ) &&
  320. ( physicaldrive_arg.GetPath()->GetPathString()->QueryChAt( 1 ) == ( WCHAR )':' )
  321. ) {
  322. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  323. msg.Display("%W", physicaldrive_arg.GetPath()->GetPathString() );
  324. return 1;
  325. }
  326. //
  327. if (virtualdrive_arg.IsValueSet()) {
  328. DSTRING virtualdrivepath;
  329. DSTRING colon;
  330. PATH TmpPath;
  331. PFSN_DIRECTORY Directory;
  332. virtualdrivepath.Initialize(virtualdrive_arg.GetPath()->GetPathString());
  333. if (virtualdrivepath.Strupr() ) {
  334. if (delete_arg.IsValueSet()) {
  335. Success = DeleteSubst(virtualdrivepath.QueryWSTR(),&msg);
  336. } else if (physicaldrive_arg.IsValueSet()) {
  337. LPWSTR physicaldrivepath;
  338. //
  339. // verify that the physical drive is an accessible path
  340. //
  341. Directory = SYSTEM::QueryDirectory( physicaldrive_arg.GetPath() );
  342. if( !Directory ) {
  343. msg.Set(MSG_SUBST_PATH_NOT_FOUND);
  344. msg.Display("%W", physicaldrive_arg.GetPath()->GetPathString());
  345. return 1;
  346. }
  347. DELETE( Directory );
  348. TmpPath.Initialize( physicaldrive_arg.GetPath(), TRUE );
  349. physicaldrivepath = ( TmpPath.GetPathString() )->QueryWSTR();
  350. Success = AddSubst(virtualdrivepath.QueryWSTR(),
  351. physicaldrivepath,
  352. ( TmpPath.GetPathString() )->QueryChCount(),
  353. &msg
  354. );
  355. DELETE(physicaldrivepath);
  356. } else {
  357. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  358. msg.Display("%W", p = arglex.GetLexemeAt(1));
  359. DELETE(p);
  360. return 1;
  361. }
  362. }
  363. } else {
  364. if (arglex.QueryLexemeCount() > 1) {
  365. msg.Set(MSG_SUBST_INVALID_PARAMETER);
  366. msg.Display("%W", p = arglex.GetLexemeAt(1));
  367. DELETE(p);
  368. return 1;
  369. } else {
  370. DumpSubstedDrives(&msg);
  371. }
  372. }
  373. return !Success;
  374. }