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.

502 lines
10 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. curdir.c
  5. Abstract:
  6. This module implements the directory commands.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. //
  14. // Each entry in _CurDirs always starts and ends with a \.
  15. //
  16. LPWSTR _CurDirs[26];
  17. WCHAR _CurDrive;
  18. LPWSTR _NtDrivePrefixes[26];
  19. BOOLEAN AllowAllPaths;
  20. VOID
  21. RcAddDrive(
  22. WCHAR DriveLetter
  23. )
  24. {
  25. OBJECT_ATTRIBUTES Obja;
  26. UNICODE_STRING UnicodeString;
  27. WCHAR name[20];
  28. HANDLE Handle;
  29. NTSTATUS Status;
  30. ASSERT(_NtDrivePrefixes[(int)(DriveLetter - L'A')] == NULL);
  31. swprintf(name,L"\\DosDevices\\%c:", DriveLetter);
  32. INIT_OBJA(&Obja, &UnicodeString, name);
  33. Status = ZwOpenSymbolicLinkObject(&Handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &Obja);
  34. if (NT_SUCCESS(Status)) {
  35. ZwClose(Handle);
  36. _NtDrivePrefixes[(int)(DriveLetter - L'A')] = SpDupStringW(name);
  37. }
  38. }
  39. VOID
  40. RcRemoveDrive(
  41. WCHAR DriveLetter
  42. )
  43. {
  44. ASSERT(_NtDrivePrefixes[(int)(DriveLetter - L'A')] != NULL);
  45. SpMemFree(_NtDrivePrefixes[(int)(DriveLetter - L'A')]);
  46. _NtDrivePrefixes[(int)(DriveLetter - L'A')] = NULL;
  47. }
  48. VOID
  49. RcInitializeCurrentDirectories(
  50. VOID
  51. )
  52. {
  53. unsigned i;
  54. RtlZeroMemory( _CurDirs, sizeof(_CurDirs) );
  55. RtlZeroMemory( _NtDrivePrefixes, sizeof(_NtDrivePrefixes) );
  56. //
  57. // Initially, the current directory on all drives
  58. // is the root.
  59. //
  60. for( i=0; i<26; i++ ) {
  61. _CurDirs[i] = SpDupStringW(L"\\");
  62. }
  63. //
  64. // Now go set up the NT drive prefixes for each drive in the system.
  65. // For each drive letter, we see whether it exists in the \DosDevices
  66. // directory as a symbolic link.
  67. //
  68. for( i=0; i<26; i++ ) {
  69. RcAddDrive((WCHAR)(i+L'A'));
  70. }
  71. //
  72. // NOTE: need to determine this by tracking the lowest
  73. // valid drive letter from the loop above, taking into account
  74. // floppy drives.
  75. //
  76. //
  77. _CurDrive = L'C';
  78. // fixed by using the drive letter for the selected install of NT
  79. // this is done in in logon.c .
  80. return;
  81. }
  82. VOID
  83. RcTerminateCurrentDirectories(
  84. VOID
  85. )
  86. {
  87. unsigned i;
  88. for( i=0; i<26; i++ ) {
  89. if( _CurDirs[i] ) {
  90. SpMemFree(_CurDirs[i]);
  91. _CurDirs[i] = NULL;
  92. }
  93. if( _NtDrivePrefixes[i] ) {
  94. SpMemFree(_NtDrivePrefixes[i]);
  95. _NtDrivePrefixes[i] = NULL;
  96. }
  97. }
  98. }
  99. BOOLEAN
  100. RcFormFullPath(
  101. IN LPCWSTR PartialPath,
  102. OUT LPWSTR FullPath,
  103. IN BOOLEAN NtPath
  104. )
  105. /*++
  106. Routine Description:
  107. This routine is similar to the Win32 GetFullPathName() API.
  108. It takes an arbitrary pathspec and converts it to a full one,
  109. by merging in the current drive and directory if necessary.
  110. The output is a fully-qualified NT pathname equivalent to
  111. the partial spec given.
  112. Processing includes all your favorite Win32isms, including
  113. collapsing adjacent dots and slashes, stripping trailing spaces,
  114. handling . and .., etc.
  115. Arguments:
  116. PartialPath - supplies a (dos-style) path spec of arbitrary qualification.
  117. FullPath - receives the equivalent fully-qualified NT path. The caller
  118. must ensure that this buffer is large enough.
  119. NtPath - if TRUE, we want a fully canonicalized NT path. Otherwise we want
  120. a DOS path.
  121. Return Value:
  122. FALSE if failure, indicating an invalid drive spec or syntactically
  123. invalid path. TRUE otherwise.
  124. --*/
  125. {
  126. unsigned len;
  127. unsigned len2;
  128. LPCWSTR Prefix;
  129. PDISK_REGION Region;
  130. WCHAR Buffer[MAX_PATH*2];
  131. //
  132. // The first thing we do is to form the fully qualified path
  133. // by merging in the current drive and directory, if necessary.
  134. //
  135. // Check for leading drive in the form X:.
  136. //
  137. if((wcslen(PartialPath) >= 2) && (PartialPath[1] == L':') && RcIsAlpha(PartialPath[0])) {
  138. //
  139. // Got leading drive, transfer it into the target.
  140. //
  141. FullPath[0] = PartialPath[0];
  142. PartialPath += 2;
  143. } else {
  144. //
  145. // No leading drive, use current drive.
  146. //
  147. FullPath[0] = _CurDrive;
  148. }
  149. //
  150. // Make sure we've got a drive we think is valid.
  151. //
  152. Prefix = _NtDrivePrefixes[RcToUpper(FullPath[0])-L'A'];
  153. if(!Prefix) {
  154. return(FALSE);
  155. }
  156. FullPath[1] = L':';
  157. FullPath[2] = 0;
  158. //
  159. // Now deal with the path part. If the next character in the input
  160. // is \ then we have a rooted path, otherwise we need to merge in
  161. // the current directory for the drive.
  162. //
  163. if(PartialPath[0] != L'\\') {
  164. wcscat(FullPath,_CurDirs[RcToUpper(FullPath[0])-L'A']);
  165. }
  166. wcscat(FullPath,PartialPath);
  167. //
  168. // Disallow ending with \ except for the root.
  169. //
  170. len = wcslen(FullPath);
  171. if((len > 3) && (FullPath[len-1] == L'\\')) {
  172. FullPath[len-1] = 0;
  173. }
  174. //
  175. // Now that we've done this, we need to call RtlGetFullPathName_U
  176. // to get full win32 naming semantics, for example, stripping
  177. // trailing spaces, coalescing adjacent dots, processing . and .., etc.
  178. // We get at that API via setupdd.sys.
  179. //
  180. if(!NT_SUCCESS(SpGetFullPathName(FullPath))) {
  181. return(FALSE);
  182. }
  183. len = wcslen(FullPath) * sizeof(WCHAR);
  184. //
  185. // check if the path is too long to be
  186. // handled by our routines [MAX_PATH*2] limit
  187. //
  188. // Note : RcGetNTFileName is called irrespective of whether caller
  189. // requested it or not to do proper error handling at the caller.
  190. //
  191. if ((len < sizeof(Buffer)) && RcGetNTFileName(FullPath, Buffer)){
  192. if (NtPath)
  193. wcscpy(FullPath, Buffer);
  194. }
  195. else
  196. return FALSE;
  197. return TRUE;
  198. }
  199. VOID
  200. RcGetCurrentDriveAndDir(
  201. OUT LPWSTR Output
  202. )
  203. {
  204. ULONG len;
  205. Output[0] = _CurDrive;
  206. Output[1] = L':';
  207. wcscpy(Output+2,_CurDirs[_CurDrive-L'A']);
  208. //
  209. // Strip off trailing \ except in root case.
  210. //
  211. len = wcslen(Output);
  212. if( (len > 3) && (Output[len-1] == L'\\') ) {
  213. Output[len-1] = 0;
  214. }
  215. }
  216. WCHAR
  217. RcGetCurrentDriveLetter(
  218. VOID
  219. )
  220. {
  221. return(_CurDrive);
  222. }
  223. BOOLEAN
  224. RcIsDriveApparentlyValid(
  225. IN WCHAR DriveLetter
  226. )
  227. {
  228. return((BOOLEAN)(_NtDrivePrefixes[RcToUpper(DriveLetter)-L'A'] != NULL));
  229. }
  230. ULONG
  231. RcCmdSwitchDrives(
  232. IN WCHAR DriveLetter
  233. )
  234. {
  235. //
  236. // If there's no NT equivalent for this drive, then we can't
  237. // switch to it.
  238. //
  239. if( !RcIsDriveApparentlyValid(DriveLetter) ) {
  240. RcMessageOut(MSG_INVALID_DRIVE);
  241. return 1;
  242. }
  243. //
  244. // NOTE should we attempt to open the root of the drive,
  245. // so we can mimic cmd.exe's behavior of refusing to set
  246. // the current drive when say there's no floppy in the drive?
  247. // There's really no great reason to do this except that it might
  248. // be a little less confusing for the user.
  249. //
  250. // No.
  251. //
  252. _CurDrive = RcToUpper(DriveLetter);
  253. return 1;
  254. }
  255. ULONG
  256. RcCmdChdir(
  257. IN PTOKENIZED_LINE TokenizedLine
  258. )
  259. {
  260. unsigned u;
  261. WCHAR *p,*Arg;
  262. HANDLE Handle;
  263. IO_STATUS_BLOCK IoStatusBlock;
  264. UNICODE_STRING UnicodeString;
  265. OBJECT_ATTRIBUTES Obja;
  266. NTSTATUS Status;
  267. if (RcCmdParseHelp( TokenizedLine, MSG_CHDIR_HELP )) {
  268. return 1;
  269. }
  270. if (TokenizedLine->TokenCount == 1) {
  271. RcGetCurrentDriveAndDir(_CmdConsBlock->TemporaryBuffer);
  272. RcRawTextOut(_CmdConsBlock->TemporaryBuffer,-1);
  273. return 1;
  274. }
  275. p = _CmdConsBlock->TemporaryBuffer;
  276. //
  277. // Get the argument. Special case x:, to print out the
  278. // current directory on that drive.
  279. //
  280. Arg = TokenizedLine->Tokens->Next->String;
  281. if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && (Arg[2] == 0)) {
  282. Arg[0] = RcToUpper(Arg[0]);
  283. u = Arg[0] - L'A';
  284. if(_NtDrivePrefixes[u] && _CurDirs[u]) {
  285. RcTextOut(Arg);
  286. //
  287. // Strip off the terminating \ except in root case.
  288. //
  289. wcscpy(p,_CurDirs[u]);
  290. u = wcslen(p);
  291. if((u > 1) && (p[u-1] == L'\\')) {
  292. p[u-1] = 0;
  293. }
  294. RcTextOut(p);
  295. RcTextOut(L"\r\n");
  296. } else {
  297. RcMessageOut(MSG_INVALID_DRIVE);
  298. }
  299. return 1;
  300. }
  301. //
  302. // Got a new directory spec. Canonicalize it to a fully qualified
  303. // DOS-style path. Check the drive to make sure it's legal.
  304. //
  305. if(!RcFormFullPath(Arg,p,FALSE)) {
  306. RcMessageOut(MSG_INVALID_PATH);
  307. return 1;
  308. }
  309. if(!_NtDrivePrefixes[RcToUpper(p[0])-L'A']) {
  310. RcMessageOut(MSG_INVALID_DRIVE);
  311. return 1;
  312. }
  313. //
  314. // Check the directory to make sure it exists.
  315. //
  316. if(!RcFormFullPath(Arg,p,TRUE)) {
  317. RcMessageOut(MSG_INVALID_PATH);
  318. return 1;
  319. }
  320. INIT_OBJA(&Obja,&UnicodeString,p);
  321. Status = ZwOpenFile(
  322. &Handle,
  323. FILE_READ_ATTRIBUTES,
  324. &Obja,
  325. &IoStatusBlock,
  326. FILE_SHARE_READ | FILE_SHARE_WRITE,
  327. FILE_DIRECTORY_FILE
  328. );
  329. if(!NT_SUCCESS(Status)) {
  330. RcNtError(Status,MSG_INVALID_PATH);
  331. return 1;
  332. }
  333. ZwClose(Handle);
  334. //
  335. // OK, it's a valid directory on a valid drive.
  336. // Form a path that starts and ends with \.
  337. //
  338. if(!RcFormFullPath(Arg,p,FALSE)) {
  339. RcMessageOut(MSG_INVALID_PATH);
  340. return 1;
  341. }
  342. if (!RcIsPathNameAllowed(p,TRUE,FALSE)) {
  343. RcMessageOut(MSG_ACCESS_DENIED);
  344. return 1;
  345. }
  346. p += 2; // skip x:
  347. u = wcslen(p);
  348. if(!u || (p[u-1] != L'\\')) {
  349. p[u] = L'\\';
  350. p[u+1] = 0;
  351. }
  352. u = RcToUpper(p[-2]) - L'A';
  353. if(_CurDirs[u]) {
  354. SpMemFree(_CurDirs[u]);
  355. }
  356. _CurDirs[u] = SpDupStringW(p);
  357. return 1;
  358. }
  359. ULONG
  360. RcCmdSystemRoot(
  361. IN PTOKENIZED_LINE TokenizedLine
  362. )
  363. {
  364. ULONG u;
  365. WCHAR buf[MAX_PATH];
  366. if (RcCmdParseHelp( TokenizedLine, MSG_SYSTEMROOT_HELP )) {
  367. return 1;
  368. }
  369. //
  370. // set the current drive to the correct one.
  371. //
  372. if (SelectedInstall == NULL) {
  373. return 1;
  374. }
  375. _CurDrive = SelectedInstall->DriveLetter;
  376. //
  377. // set the current dir to the correct one.
  378. //
  379. RtlZeroMemory( buf, sizeof(buf) );
  380. wcscat( buf, L"\\" );
  381. wcscat( buf, SelectedInstall->Path );
  382. wcscat( buf, L"\\" );
  383. u = RcToUpper(SelectedInstall->DriveLetter) - L'A';
  384. if( _CurDirs[u] ) {
  385. SpMemFree(_CurDirs[u]);
  386. }
  387. _CurDirs[u] = SpDupStringW( buf );
  388. return 1;
  389. }