Leaked source code of windows server 2003
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.

711 lines
18 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. dir.c
  5. Abstract:
  6. This module implements the dir commands.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. //
  14. // global external variables
  15. //
  16. extern LARGE_INTEGER glBias;
  17. typedef struct _DIR_STATS {
  18. unsigned FileCount;
  19. LONGLONG TotalSize;
  20. RcFileSystemType fsType;
  21. } DIR_STATS, *PDIR_STATS;
  22. BOOLEAN
  23. pRcDirEnumProc(
  24. IN LPCWSTR Directory,
  25. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  26. OUT NTSTATUS *Status,
  27. IN PDIR_STATS DirStats
  28. );
  29. NTSTATUS
  30. SpSystemTimeToLocalTime (
  31. IN PLARGE_INTEGER SystemTime,
  32. OUT PLARGE_INTEGER LocalTime
  33. );
  34. NTSTATUS
  35. SpLocalTimeToSystemTime (
  36. IN PLARGE_INTEGER LocalTime,
  37. OUT PLARGE_INTEGER SystemTime
  38. );
  39. ULONG
  40. RcCmdDir(
  41. IN PTOKENIZED_LINE TokenizedLine
  42. )
  43. {
  44. LPCWSTR Dir;
  45. LPWSTR Path;
  46. LPWSTR DosPath;
  47. LPWSTR p;
  48. NTSTATUS Status;
  49. WCHAR Drive[4];
  50. IO_STATUS_BLOCK IoStatusBlock;
  51. UNICODE_STRING UnicodeString;
  52. HANDLE Handle;
  53. OBJECT_ATTRIBUTES Obja;
  54. DIR_STATS DirStats;
  55. ULONG u;
  56. ULONG rc;
  57. PFILE_FS_VOLUME_INFORMATION VolumeInfo;
  58. FILE_FS_SIZE_INFORMATION SizeInfo;
  59. BYTE bfFSInfo[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
  60. (MAX_PATH*2)];
  61. PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = 0;
  62. if (RcCmdParseHelp( TokenizedLine, MSG_DIR_HELP )) {
  63. return 1;
  64. }
  65. //
  66. // If there's no argument, then we want the current directory.
  67. //
  68. Dir = (TokenizedLine->TokenCount == 2)
  69. ? TokenizedLine->Tokens->Next->String
  70. : L".";
  71. //
  72. // Canonicalize the name once to get a full DOS-style path
  73. // we can print out, and another time to get the NT-style path
  74. // we'll use to actually do the work.
  75. //
  76. if (!RcFormFullPath(Dir,_CmdConsBlock->TemporaryBuffer,FALSE)) {
  77. RcMessageOut(MSG_INVALID_PATH);
  78. return 1;
  79. }
  80. DosPath = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  81. if (!RcFormFullPath(Dir,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  82. RcMessageOut(MSG_INVALID_PATH);
  83. return 1;
  84. }
  85. Path = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  86. //
  87. // Open up the root directory of the drive so we can query
  88. // the volume label, serial number, and free space.
  89. //
  90. Drive[0] = DosPath[0];
  91. Drive[1] = L':';
  92. Drive[2] = L'\\';
  93. Drive[3] = 0;
  94. if (!RcFormFullPath(Drive,_CmdConsBlock->TemporaryBuffer,TRUE)) {
  95. DEBUG_PRINTF(( "couldn't open root of drive!" ));
  96. RcNtError( STATUS_NO_MEDIA_IN_DEVICE, MSG_NO_MEDIA_IN_DEVICE );
  97. goto c2;
  98. }
  99. INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
  100. Status = ZwOpenFile(
  101. &Handle,
  102. FILE_READ_ATTRIBUTES,
  103. &Obja,
  104. &IoStatusBlock,
  105. FILE_SHARE_READ | FILE_SHARE_WRITE,
  106. FILE_DIRECTORY_FILE
  107. );
  108. pRcEnableMoreMode();
  109. if(NT_SUCCESS(Status)) {
  110. //
  111. // Get the volume label and serial number.
  112. //
  113. VolumeInfo = _CmdConsBlock->TemporaryBuffer;
  114. Status = ZwQueryVolumeInformationFile(
  115. Handle,
  116. &IoStatusBlock,
  117. VolumeInfo,
  118. _CmdConsBlock->TemporaryBufferSize,
  119. FileFsVolumeInformation
  120. );
  121. if(NT_SUCCESS(Status)) {
  122. //
  123. // We can tell the user the volume label and serial number.
  124. //
  125. VolumeInfo->VolumeLabel[VolumeInfo->VolumeLabelLength/sizeof(WCHAR)] = 0;
  126. p = SpDupStringW(VolumeInfo->VolumeLabel);
  127. u = VolumeInfo->VolumeSerialNumber;
  128. RcMessageOut(
  129. *p ? MSG_DIR_BANNER1a : MSG_DIR_BANNER1b,
  130. RcToUpper(DosPath[0]),
  131. p
  132. );
  133. SpMemFree(p);
  134. RcMessageOut(MSG_DIR_BANNER2,u >> 16,u & 0xffff);
  135. }
  136. //
  137. // Get free space value for drive.
  138. //
  139. Status = ZwQueryVolumeInformationFile(
  140. Handle,
  141. &IoStatusBlock,
  142. &SizeInfo,
  143. sizeof(FILE_FS_SIZE_INFORMATION),
  144. FileFsSizeInformation
  145. );
  146. if(!NT_SUCCESS(Status)) {
  147. SizeInfo.BytesPerSector = 0;
  148. }
  149. //
  150. // Get the type of the file system so that we can handle
  151. // the file times properly (NT stores the date in UTC).
  152. //
  153. RtlZeroMemory(bfFSInfo, sizeof(bfFSInfo));
  154. pFSInfo = (PFILE_FS_ATTRIBUTE_INFORMATION) bfFSInfo;
  155. Status = ZwQueryVolumeInformationFile(
  156. Handle,
  157. &IoStatusBlock,
  158. pFSInfo,
  159. sizeof(bfFSInfo),
  160. FileFsAttributeInformation);
  161. ZwClose(Handle);
  162. }
  163. //
  164. // Tell the user the full DOS path of the directory.
  165. //
  166. RcMessageOut(MSG_DIR_BANNER3,DosPath);
  167. //
  168. // Now go enumerate the directory.
  169. //
  170. RtlZeroMemory(&DirStats,sizeof(DIR_STATS));
  171. if (!NT_SUCCESS(Status)) {
  172. KdPrint(("SPCMDCON:Could not get volume information, Error Code:%lx\n", Status));
  173. DirStats.fsType = RcUnknown; // assume FAT file system (by default)
  174. } else {
  175. if (!wcscmp(pFSInfo->FileSystemName, L"NTFS"))
  176. DirStats.fsType = RcNTFS;
  177. else if (!wcscmp(pFSInfo->FileSystemName, L"FAT"))
  178. DirStats.fsType = RcFAT;
  179. else if (!wcscmp(pFSInfo->FileSystemName, L"FAT32"))
  180. DirStats.fsType = RcFAT32;
  181. else if (!wcscmp(pFSInfo->FileSystemName, L"CDFS"))
  182. DirStats.fsType = RcCDFS;
  183. else
  184. DirStats.fsType = RcUnknown;
  185. }
  186. KdPrint(("SPCMDCON: RcCmdDir detected file system type (%lx)-%ws\n",
  187. DirStats.fsType, pFSInfo ? pFSInfo->FileSystemName : L"None"));
  188. Status = RcEnumerateFiles(Dir,Path,pRcDirEnumProc,&DirStats);
  189. pRcDisableMoreMode();
  190. if(NT_SUCCESS(Status)) {
  191. RcFormat64BitIntForOutput(DirStats.TotalSize,_CmdConsBlock->TemporaryBuffer,FALSE);
  192. p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  193. RcMessageOut(MSG_DIR_BANNER4,DirStats.FileCount,p);
  194. SpMemFree(p);
  195. if(SizeInfo.BytesPerSector) {
  196. RcFormat64BitIntForOutput(
  197. SizeInfo.AvailableAllocationUnits.QuadPart * (LONGLONG)SizeInfo.SectorsPerAllocationUnit * (LONGLONG)SizeInfo.BytesPerSector,
  198. _CmdConsBlock->TemporaryBuffer,
  199. FALSE
  200. );
  201. p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
  202. RcMessageOut(MSG_DIR_BANNER5,p);
  203. SpMemFree(p);
  204. }
  205. } else {
  206. RcNtError(Status,MSG_FILE_ENUM_ERROR);
  207. }
  208. c2:
  209. SpMemFree(Path);
  210. SpMemFree(DosPath);
  211. return 1;
  212. }
  213. BOOLEAN
  214. pRcDirEnumProc(
  215. IN LPCWSTR Directory,
  216. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  217. OUT NTSTATUS *Status,
  218. IN PDIR_STATS DirStats
  219. )
  220. {
  221. WCHAR LineOut[50];
  222. WCHAR *p;
  223. NTSTATUS timeStatus;
  224. LARGE_INTEGER *pLastWriteTime = 0;
  225. LARGE_INTEGER lastWriteTime;
  226. LARGE_INTEGER timeBias;
  227. TIME_FIELDS timeFields;
  228. TIME_ZONE_INFORMATION timeZone;
  229. UNREFERENCED_PARAMETER(Directory);
  230. DirStats->FileCount++;
  231. DirStats->TotalSize += FileInfo->EndOfFile.QuadPart;
  232. lastWriteTime = FileInfo->LastWriteTime;
  233. //
  234. // Convert the time into local time from UTC if the file
  235. // system is NTFS
  236. //
  237. switch(DirStats->fsType) {
  238. case RcNTFS:
  239. case RcCDFS:
  240. // localtime = UTC - bias
  241. lastWriteTime.QuadPart -= glBias.QuadPart;
  242. break;
  243. case RcFAT:
  244. case RcFAT32:
  245. default:
  246. break;
  247. }
  248. //
  249. // Format the date and time, which go first.
  250. //
  251. RcFormatDateTime(&lastWriteTime,LineOut);
  252. RcTextOut(LineOut);
  253. //
  254. // 2 spaces for separation
  255. //
  256. RcTextOut(L" ");
  257. //
  258. // File attributes.
  259. //
  260. p = LineOut;
  261. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  262. *p++ = L'd';
  263. } else {
  264. *p++ = L'-';
  265. }
  266. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
  267. *p++ = L'a';
  268. } else {
  269. *p++ = L'-';
  270. }
  271. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_READONLY) {
  272. *p++ = L'r';
  273. } else {
  274. *p++ = L'-';
  275. }
  276. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_HIDDEN) {
  277. *p++ = L'h';
  278. } else {
  279. *p++ = L'-';
  280. }
  281. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
  282. *p++ = L's';
  283. } else {
  284. *p++ = L'-';
  285. }
  286. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
  287. *p++ = L'c';
  288. } else {
  289. *p++ = L'-';
  290. }
  291. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  292. *p++ = L'e';
  293. } else {
  294. *p++ = L'-';
  295. }
  296. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  297. *p++ = L'p';
  298. } else {
  299. *p++ = L'-';
  300. }
  301. *p = 0;
  302. RcTextOut(LineOut);
  303. //
  304. // 2 spaces for separation
  305. //
  306. RcTextOut(L" ");
  307. //
  308. // Now, put the size in there. Right justified and space padded
  309. // up to 8 chars. Oterwise unjustified or padded.
  310. //
  311. RcFormat64BitIntForOutput(FileInfo->EndOfFile.QuadPart,LineOut,TRUE);
  312. if(FileInfo->EndOfFile.QuadPart > 99999999i64) {
  313. RcTextOut(LineOut);
  314. } else {
  315. RcTextOut(LineOut+11); // outputs 8 chars
  316. }
  317. RcTextOut(L" ");
  318. //
  319. // Finally, put the filename on the line. Need to 0-terminate it first.
  320. //
  321. wcsncpy(_CmdConsBlock->TemporaryBuffer,FileInfo->FileName,FileInfo->FileNameLength);
  322. ((WCHAR *)_CmdConsBlock->TemporaryBuffer)[FileInfo->FileNameLength] = 0;
  323. *Status = STATUS_SUCCESS;
  324. return((BOOLEAN)(RcTextOut(_CmdConsBlock->TemporaryBuffer) && RcTextOut(L"\r\n")));
  325. }
  326. NTSTATUS
  327. RcEnumerateFiles(
  328. IN LPCWSTR OriginalPathSpec,
  329. IN LPCWSTR FullyQualifiedPathSpec,
  330. IN PENUMFILESCB Callback,
  331. IN PVOID CallerData
  332. )
  333. {
  334. OBJECT_ATTRIBUTES Obja;
  335. IO_STATUS_BLOCK IoStatusBlock;
  336. HANDLE Handle;
  337. UNICODE_STRING UnicodeString;
  338. NTSTATUS Status;
  339. BOOLEAN b;
  340. WCHAR *p;
  341. WCHAR *LastComponent = NULL;
  342. PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
  343. unsigned u;
  344. WCHAR *NameChar;
  345. BOOLEAN EndsInDot;
  346. WCHAR *DirectoryPart;
  347. //
  348. // Determine whether the original path spec ends with a .
  349. // This is used below to get around a problem with specifying
  350. // *. as a search specifier.
  351. //
  352. u = wcslen(OriginalPathSpec);
  353. if(u && (OriginalPathSpec[u-1] == L'.')) {
  354. EndsInDot = TRUE;
  355. } else {
  356. EndsInDot = FALSE;
  357. }
  358. //
  359. // Determine whether the given path points at a directory.
  360. // If so, we'll concatenate \* on the end and fall through
  361. // to the common case.
  362. //
  363. b = FALSE;
  364. INIT_OBJA(&Obja,&UnicodeString,FullyQualifiedPathSpec);
  365. Status = ZwOpenFile(
  366. &Handle,
  367. FILE_READ_ATTRIBUTES,
  368. &Obja,
  369. &IoStatusBlock,
  370. FILE_SHARE_READ | FILE_SHARE_WRITE,
  371. FILE_DIRECTORY_FILE
  372. );
  373. if(NT_SUCCESS(Status)) {
  374. ZwClose(Handle);
  375. b = TRUE;
  376. }
  377. if(b) {
  378. //
  379. // Directory, append \*.
  380. //
  381. p = SpMemAlloc((wcslen(FullyQualifiedPathSpec)+3)*sizeof(WCHAR));
  382. if (p) {
  383. wcscpy(p,FullyQualifiedPathSpec);
  384. SpConcatenatePaths(p,L"*");
  385. EndsInDot = FALSE;
  386. }
  387. } else {
  388. //
  389. // Not directory, pass as-is. Note that this could be an actual
  390. // file, or a wild-card spec.
  391. //
  392. p = SpDupStringW((PVOID)FullyQualifiedPathSpec);
  393. }
  394. //
  395. // Now trim back the path/file specification so we can open the containing
  396. // directory for enumeration.
  397. //
  398. if (p) {
  399. LastComponent = wcsrchr(p,L'\\');
  400. } else {
  401. return STATUS_NO_MEMORY;
  402. }
  403. if (LastComponent) {
  404. *LastComponent++ = 0;
  405. }
  406. DirectoryPart = SpMemAlloc((wcslen(p)+2)*sizeof(WCHAR));
  407. wcscpy(DirectoryPart,p);
  408. wcscat(DirectoryPart,L"\\");
  409. INIT_OBJA(&Obja,&UnicodeString,p);
  410. if (LastComponent) {
  411. LastComponent[-1] = L'\\';
  412. }
  413. UnicodeString.Length += sizeof(WCHAR);
  414. Status = ZwOpenFile(
  415. &Handle,
  416. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  417. &Obja,
  418. &IoStatusBlock,
  419. FILE_SHARE_READ | FILE_SHARE_WRITE,
  420. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  421. );
  422. if(!NT_SUCCESS(Status)) {
  423. SpMemFree(p);
  424. SpMemFree(DirectoryPart);
  425. return(Status);
  426. }
  427. RtlInitUnicodeString(&UnicodeString,LastComponent);
  428. //
  429. // The following code is adapted from the implementation for
  430. // the FindFirstFile Win32 API, and provides additional DOS-like
  431. // wildcard matching semantics.
  432. //
  433. // Special case *.* to * since it is so common. Otherwise transmogrify
  434. // the input name according to the following rules:
  435. //
  436. // - Change all ? to DOS_QM
  437. // - Change all . followed by ? or * to DOS_DOT
  438. // - Change all * followed by a . into DOS_STAR
  439. //
  440. // These transmogrifications are all done in place.
  441. //
  442. if(!wcscmp(LastComponent,L"*.*")) {
  443. UnicodeString.Length = sizeof(WCHAR); // trim down to just *
  444. } else {
  445. for(u=0, NameChar=UnicodeString.Buffer;
  446. u < (UnicodeString.Length/sizeof(WCHAR));
  447. u++, NameChar++) {
  448. if(u && (*NameChar == L'.') && (*(NameChar - 1) == L'*')) {
  449. *(NameChar-1) = DOS_STAR;
  450. }
  451. if((*NameChar == L'?') || (*NameChar == L'*')) {
  452. if(*NameChar == L'?') {
  453. *NameChar = DOS_QM;
  454. }
  455. if(u && (*(NameChar-1) == L'.')) {
  456. *(NameChar-1) = DOS_DOT;
  457. }
  458. }
  459. }
  460. if(EndsInDot && (*(NameChar - 1) == L'*')) {
  461. *(NameChar-1) = DOS_STAR;
  462. }
  463. }
  464. //
  465. // Finally, iterate the directory.
  466. //
  467. #define DIRINFO_BUFFER_SIZE ((2*MAX_PATH) + sizeof(FILE_BOTH_DIR_INFORMATION))
  468. DirectoryInfo = SpMemAlloc(DIRINFO_BUFFER_SIZE);
  469. b = TRUE;
  470. while(TRUE) {
  471. Status = ZwQueryDirectoryFile(
  472. Handle,
  473. NULL,
  474. NULL,
  475. NULL,
  476. &IoStatusBlock,
  477. DirectoryInfo,
  478. DIRINFO_BUFFER_SIZE,
  479. FileBothDirectoryInformation,
  480. TRUE,
  481. &UnicodeString,
  482. b
  483. );
  484. b = FALSE;
  485. //
  486. // Check termination condition
  487. //
  488. if(Status == STATUS_NO_MORE_FILES) {
  489. Status = STATUS_SUCCESS;
  490. break;
  491. }
  492. if(!NT_SUCCESS(Status)) {
  493. break;
  494. }
  495. //
  496. // OK, nul-terminate filename and pass info to callback.
  497. //
  498. DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(WCHAR)] = 0;
  499. if(!Callback(DirectoryPart,DirectoryInfo,&Status,CallerData)) {
  500. break;
  501. }
  502. }
  503. ZwClose(Handle);
  504. SpMemFree(DirectoryPart);
  505. SpMemFree(DirectoryInfo);
  506. SpMemFree(p);
  507. return(Status);
  508. }
  509. VOID
  510. RcFormat64BitIntForOutput(
  511. IN LONGLONG n,
  512. OUT LPWSTR Output,
  513. IN BOOLEAN RightJustify
  514. )
  515. {
  516. WCHAR *p;
  517. LONGLONG d;
  518. BOOLEAN b;
  519. WCHAR c;
  520. //
  521. // Max signed 64-bit integer is 9223372036854775807 (19 digits).
  522. // The result will be space padded to the left so it's right-justified
  523. // if that flag is set. Otherwise it's just a plain 0-terminated string.
  524. //
  525. p = Output;
  526. d = 1000000000000000000i64;
  527. b = FALSE;
  528. do {
  529. c = (WCHAR)((n / d) % 10) + L'0';
  530. if(c == L'0') {
  531. if(!b && (d != 1)) {
  532. c = RightJustify ? L' ' : 0;
  533. }
  534. } else {
  535. b = TRUE;
  536. }
  537. if(c) {
  538. *p++ = c;
  539. }
  540. } while(d /= 10);
  541. *p = 0;
  542. }
  543. //
  544. // This time conversion APIs should be moved to setupdd.sys
  545. // if more modules need this
  546. //
  547. NTSTATUS
  548. SpSystemTimeToLocalTime (
  549. IN PLARGE_INTEGER SystemTime,
  550. OUT PLARGE_INTEGER LocalTime
  551. )
  552. {
  553. NTSTATUS Status;
  554. SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
  555. Status = ZwQuerySystemInformation(
  556. SystemTimeOfDayInformation,
  557. &TimeOfDay,
  558. sizeof(TimeOfDay),
  559. NULL
  560. );
  561. if ( !NT_SUCCESS(Status) ) {
  562. return Status;
  563. }
  564. //
  565. // LocalTime = SystemTime - TimeZoneBias
  566. //
  567. LocalTime->QuadPart = SystemTime->QuadPart -
  568. TimeOfDay.TimeZoneBias.QuadPart;
  569. return STATUS_SUCCESS;
  570. }
  571. //
  572. // This time conversion APIs should be moved to setupdd.sys
  573. // if more modules need this
  574. //
  575. NTSTATUS
  576. SpLocalTimeToSystemTime (
  577. IN PLARGE_INTEGER LocalTime,
  578. OUT PLARGE_INTEGER SystemTime
  579. )
  580. {
  581. NTSTATUS Status;
  582. SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
  583. Status = ZwQuerySystemInformation(
  584. SystemTimeOfDayInformation,
  585. &TimeOfDay,
  586. sizeof(TimeOfDay),
  587. NULL
  588. );
  589. if ( !NT_SUCCESS(Status) ) {
  590. return Status;
  591. }
  592. //
  593. // SystemTime = LocalTime + TimeZoneBias
  594. //
  595. SystemTime->QuadPart = LocalTime->QuadPart +
  596. TimeOfDay.TimeZoneBias.QuadPart;
  597. return STATUS_SUCCESS;
  598. }