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.

1496 lines
32 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. chksis.cpp
  5. Abstract:
  6. This module implements a utility that examines all SIS files on a volume
  7. looking for errors and optionally displaying file information.
  8. Author:
  9. Scott Cutshall Fall, 1997
  10. --*/
  11. #include <iostream>
  12. #include <string>
  13. #include <vector>
  14. #include <algorithm>
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <ntioapi.h>
  19. #include <windows.h>
  20. using namespace std;
  21. bool verbose = false;
  22. typedef LONGLONG INDEX;
  23. //
  24. // Convert a 32bit value to a base 36 representation in
  25. // the caller provided string.
  26. //
  27. void IntegerToBase36String(ULONG val, string& s) {
  28. //
  29. // Maximum number of "digits" in a base 36 representation of a 32 bit
  30. // value is 7.
  31. //
  32. char rs[8];
  33. ULONG v = val;
  34. rs[7] = 0;
  35. for (int i = 7; i == 7 || v != 0;) {
  36. ULONG d = v % 36;
  37. v = v / 36;
  38. --i;
  39. if (d < 10)
  40. rs[i] = '0' + d;
  41. else
  42. rs[i] = 'a' + d - 10;
  43. }
  44. s.assign(&rs[i]);
  45. }
  46. //
  47. // A put operator for INDEX types. Implemented as IndexToSISFileName().
  48. //
  49. #ifndef _WIN64
  50. ostream& operator<<(ostream& out, INDEX& index)
  51. {
  52. unsigned long lo = static_cast<unsigned long> (index);
  53. long hi = static_cast<long> (index >> 32);
  54. string s("1234567");
  55. IntegerToBase36String(lo, s);
  56. out << s << '.';
  57. IntegerToBase36String(hi, s);
  58. out << s;
  59. return out;
  60. }
  61. #endif
  62. //
  63. // A common store file object. Holds the file's index, name, internal refcount,
  64. // external refcount, and identity operations.
  65. //
  66. class CsFile {
  67. public:
  68. CsFile(INDEX i = 0, int r = 0, string n = "") :
  69. index(i), internalRefCount(r), name(n), externalRefCount(0) {}
  70. void Validate() {
  71. if (internalRefCount != externalRefCount) {
  72. cout << name << " Reference Count: " << internalRefCount;
  73. cout << ". " << externalRefCount << " external references identified." << endl;
  74. }
  75. }
  76. friend bool operator<(const CsFile& a, const CsFile& b) {
  77. return a.index < b.index;
  78. }
  79. friend bool operator>(const CsFile& a, const CsFile& b) {
  80. return a.index > b.index;
  81. }
  82. friend bool operator==(const CsFile& a, const CsFile& b) {
  83. return a.index == b.index;
  84. }
  85. void IncRefCount() {
  86. ++externalRefCount;
  87. }
  88. void display() {
  89. cout << "CS Index: " << (INDEX) index << " Ref Count: " << internalRefCount << endl;
  90. }
  91. private:
  92. //
  93. // Index of this entry's file.
  94. //
  95. INDEX index;
  96. //
  97. // The file name. This is somewhat redundant with the index (ie. the
  98. // name is derived from the index), so it isn't absolutely necessary.
  99. //
  100. string name;
  101. //
  102. // Reference count read from the file's refcount stream.
  103. //
  104. int internalRefCount;
  105. //
  106. // Number of valid references to this file detected during scan.
  107. //
  108. int externalRefCount;
  109. };
  110. //
  111. // The SIS Common Store object. Holds all common store file objects, and
  112. // validation and query operations.
  113. //
  114. class CommonStore {
  115. public:
  116. CommonStore(int vsize = 0) : maxIndex(0) {
  117. if (vsize > 0) csFiles.resize(vsize);
  118. }
  119. //
  120. // Method to create a common store on a volume.
  121. //
  122. bool Create(string& Volume);
  123. //
  124. // Validate the common store directory and initialize this class.
  125. //
  126. void Validate(string& Volume);
  127. //
  128. // Validate the reference counts. Assumes all external references
  129. // have been identified.
  130. //
  131. void ValidateRefCounts();
  132. //
  133. // All indices must be less than maxIndex;
  134. //
  135. bool ValidateIndex(INDEX i) {
  136. return i <= maxIndex;
  137. }
  138. //
  139. // Lookup a common store index and add a ref if found.
  140. //
  141. CsFile *Query(INDEX index);
  142. private:
  143. bool FileNameToIndex(string& fileName, INDEX& csIndex);
  144. //
  145. // Index from the MaxIndex file.
  146. //
  147. INDEX maxIndex;
  148. //
  149. // Database of content files. All CS files are examined and added to the database,
  150. // sorted, and subsequently used during the SIS link scan.
  151. //
  152. vector<CsFile> csFiles;
  153. };
  154. //
  155. // Various SIS file and directory names.
  156. //
  157. const string maxIndexFileName("MaxIndex");
  158. const string logFileName("LogFile");
  159. const string csDir("\\SIS Common Store\\");
  160. //
  161. // Create a common store directory on a volume.
  162. //
  163. // todo:
  164. // - Verify that the volume is ntfs.
  165. // - Verify that the SIS driver is loaded.
  166. //
  167. bool
  168. CommonStore::Create(string& Volume)
  169. {
  170. const string CommonStoreDir = Volume + "\\SIS Common Store";
  171. USHORT comp = COMPRESSION_FORMAT_DEFAULT;
  172. DWORD transferCount;
  173. bool rc;
  174. if (! CreateDirectory(CommonStoreDir.c_str(), NULL) ) {
  175. cout << "Cannot create Common Store directory, " << GetLastError() << endl;
  176. return false;
  177. }
  178. if (verbose)
  179. cout << CommonStoreDir << " created" << endl;
  180. //
  181. // Open the Common Store directory and enable compression.
  182. //
  183. HANDLE CSDirHandle = CreateFile(
  184. CommonStoreDir.c_str(),
  185. GENERIC_READ,
  186. FILE_SHARE_READ | FILE_SHARE_WRITE,
  187. NULL,
  188. OPEN_EXISTING,
  189. FILE_FLAG_BACKUP_SEMANTICS,
  190. NULL);
  191. if (CSDirHandle == INVALID_HANDLE_VALUE) {
  192. cout << "Can't open Common Store directory." << endl;
  193. rc = false;
  194. } else {
  195. rc = 0 != DeviceIoControl(
  196. CSDirHandle,
  197. FSCTL_SET_COMPRESSION,
  198. &comp,
  199. sizeof(comp),
  200. NULL,
  201. 0,
  202. &transferCount,
  203. NULL);
  204. CloseHandle(CSDirHandle);
  205. }
  206. if (!rc)
  207. cout << "Cannot enable compression on Common Store directory, " << GetLastError() << endl;
  208. //
  209. // Chdir into the common store directory.
  210. //
  211. if (SetCurrentDirectory(CommonStoreDir.c_str()) == 0) {
  212. //
  213. // Unable to chdir into the common store.
  214. //
  215. cout << "\"\\SIS Common Store\" directory not found" << endl;
  216. return false;
  217. }
  218. rc = true;
  219. //
  220. // Create the MaxIndex file.
  221. //
  222. HANDLE hMaxIndex = CreateFile(
  223. maxIndexFileName.c_str(),
  224. GENERIC_READ | GENERIC_WRITE,
  225. 0,
  226. NULL,
  227. CREATE_NEW,
  228. FILE_ATTRIBUTE_NORMAL,
  229. NULL);
  230. if (hMaxIndex == INVALID_HANDLE_VALUE) {
  231. cout << "Can't create \"\\SIS Common Store\\MaxIndex\"" << endl;
  232. rc = false;
  233. } else {
  234. DWORD bytesWritten;
  235. maxIndex = 1;
  236. if (! WriteFile(
  237. hMaxIndex,
  238. &maxIndex,
  239. sizeof maxIndex,
  240. &bytesWritten,
  241. NULL) ||
  242. (bytesWritten < sizeof maxIndex)) {
  243. cout << "Can't write MaxIndex, " << GetLastError() << endl;
  244. rc = false;
  245. } else {
  246. CloseHandle(hMaxIndex);
  247. if (verbose)
  248. cout << "MaxIndex: " << (INDEX) maxIndex << endl;
  249. rc = true;
  250. }
  251. }
  252. return rc;
  253. }
  254. //
  255. // Validate the common store directory.
  256. //
  257. void
  258. CommonStore::Validate(string& Volume)
  259. {
  260. WIN32_FIND_DATA findData;
  261. HANDLE findHandle;
  262. const string fileNameMatchAny = "*";
  263. const string CommonStoreDir = Volume + "\\SIS Common Store";
  264. cout << "Checking Common Store" << endl;
  265. //
  266. // Chdir into the common store directory.
  267. //
  268. if (SetCurrentDirectory(CommonStoreDir.c_str()) == 0) {
  269. //
  270. // Unable to chdir into the common store.
  271. //
  272. cout << "\"\\SIS Common Store\" directory not found" << endl;
  273. return;
  274. }
  275. //
  276. // Validate and read the contents of the MaxIndex file.
  277. //
  278. HANDLE hMaxIndex = CreateFile(
  279. maxIndexFileName.c_str(),
  280. GENERIC_READ,
  281. FILE_SHARE_READ | FILE_SHARE_WRITE,
  282. NULL,
  283. OPEN_EXISTING,
  284. FILE_ATTRIBUTE_NORMAL,
  285. NULL);
  286. if (hMaxIndex == INVALID_HANDLE_VALUE) {
  287. cout << "Can't open \"\\SIS Common Store\\MaxIndex\"" << endl;
  288. } else {
  289. DWORD bytesRead;
  290. if (! ReadFile(
  291. hMaxIndex,
  292. &maxIndex,
  293. sizeof maxIndex,
  294. &bytesRead,
  295. NULL)) {
  296. cout << "Can't read MaxIndex, " << GetLastError() << endl;
  297. }
  298. if (bytesRead < sizeof maxIndex) {
  299. cout << "Invalid MaxIndex" << endl;
  300. }
  301. CloseHandle(hMaxIndex);
  302. if (verbose)
  303. cout << "MaxIndex: " << (INDEX) maxIndex << endl;
  304. }
  305. //
  306. // Enumerate and validate all files in the common store directory.
  307. // Save the file name and reference count for later lookup when validating
  308. // the SIS link files.
  309. //
  310. findHandle = FindFirstFile( fileNameMatchAny.c_str(), &findData );
  311. if (INVALID_HANDLE_VALUE == findHandle) {
  312. cout << CommonStoreDir << " is empty." << endl;
  313. return;
  314. }
  315. do {
  316. ULONG refCount;
  317. string fileName;
  318. fileName = findData.cFileName;
  319. if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  320. //
  321. // Ignore . and ..
  322. //
  323. if ( findData.cFileName[0] == '.' ) {
  324. if (( findData.cFileName[1] == 0 ) ||
  325. (( findData.cFileName[1] == '.' ) && ( findData.cFileName[2] == 0 )))
  326. continue;
  327. }
  328. cout << "Common Store directory skipped: " << fileName << endl;
  329. continue;
  330. }
  331. if ((_stricmp(maxIndexFileName.c_str(),fileName.c_str()) == 0) ||
  332. (_stricmp(logFileName.c_str(),fileName.c_str()) == 0)) {
  333. //
  334. // Skip the MaxIndex and LogFile files.
  335. //
  336. continue;
  337. }
  338. //
  339. // Verify that:
  340. // - the file name is a valid index.
  341. // - this is a normal file (ie. not a reparse point).
  342. // - there is a refcount stream of proper format.
  343. //
  344. INDEX csIndex;
  345. refCount = 0;
  346. if (! FileNameToIndex(fileName, csIndex)) {
  347. cout << "Unknown file in Common Store: " << fileName << endl;
  348. continue;
  349. }
  350. if (! ValidateIndex(csIndex)) {
  351. cout << "Invalid CSIndex: " << fileName << endl;
  352. }
  353. if ( IO_REPARSE_TAG_SIS == findData.dwReserved0 ) {
  354. cout << "SIS link found in Common Store: " << fileName << endl;
  355. } else {
  356. //
  357. // Read in the refcount;
  358. //
  359. string refName(fileName + ":sisrefs$");
  360. HANDLE hRefCount = CreateFile(
  361. refName.c_str(),
  362. GENERIC_READ,
  363. FILE_SHARE_READ | FILE_SHARE_WRITE,
  364. NULL,
  365. OPEN_EXISTING,
  366. FILE_ATTRIBUTE_NORMAL,
  367. NULL);
  368. if (hRefCount == INVALID_HANDLE_VALUE) {
  369. cout << "Can't open ref count stream, " << refName << ", " << GetLastError() << endl;
  370. } else {
  371. DWORD bytesRead;
  372. if (! ReadFile(
  373. hRefCount,
  374. &refCount,
  375. sizeof refCount,
  376. &bytesRead,
  377. NULL)) {
  378. cout << "Can't read " << refName << ", " << GetLastError() << endl;
  379. }
  380. if (bytesRead < sizeof refCount) {
  381. cout << "Invalid ref count in " << refName << endl;
  382. }
  383. CloseHandle(hRefCount);
  384. }
  385. CsFile csFile(csIndex, refCount, fileName);
  386. //
  387. // Add this file to our database. Expand the database if necessary.
  388. //
  389. if (0 == csFiles.capacity())
  390. csFiles.reserve(csFiles.size() + 200);
  391. csFiles.push_back(csFile);
  392. if (verbose)
  393. csFile.display();
  394. }
  395. } while ( FindNextFile( findHandle, &findData ) );
  396. FindClose( findHandle );
  397. //
  398. // Sort the database for subsequent lookups.
  399. //
  400. sort(csFiles.begin(), csFiles.end());
  401. }
  402. //
  403. // Validate the reference counts. Assumes all external references
  404. // have been identified.
  405. //
  406. void
  407. CommonStore::ValidateRefCounts() {
  408. vector<CsFile>::iterator p;
  409. for (p = csFiles.begin(); p != csFiles.end(); ++p) {
  410. p->Validate();
  411. }
  412. }
  413. //
  414. // Lookup the specified index in the common store.
  415. //
  416. CsFile *
  417. CommonStore::Query(INDEX index)
  418. {
  419. CsFile key(index);
  420. //
  421. // Use a binary search to lookup the index.
  422. //
  423. vector<CsFile>::iterator p = lower_bound(csFiles.begin(), csFiles.end(), key);
  424. if (p == csFiles.end() || *p > key)
  425. return NULL; // not found
  426. return p;
  427. }
  428. //
  429. // Extract the index from a common store file name.
  430. //
  431. bool
  432. CommonStore::FileNameToIndex(string& fileName, INDEX& csIndex)
  433. {
  434. char c;
  435. const size_t len = fileName.length();
  436. ULONG hi = 0, lo = 0;
  437. //
  438. // Format: "_low.high", where low.high is the base 36 representation of
  439. // the index value.
  440. //
  441. size_t i = 0;
  442. if (len < 2 || fileName.at(i) != '_') {
  443. cout << "Invalid Common Store file name: " << fileName << endl;
  444. return false;
  445. }
  446. while (++i < len && (c = fileName.at(i)) != '.') {
  447. INDEX d;
  448. if (c >= '0' && c <= '9') {
  449. d = c - '0';
  450. } else if (c >= 'a' && c <= 'z') {
  451. d = c - 'a' + 10;
  452. } else {
  453. cout << "Invalid Common Store file name: " << fileName << endl;
  454. return false;
  455. }
  456. lo = lo * 36 + d;
  457. }
  458. if (c != '.') {
  459. cout << "Invalid Common Store file name: " << fileName << endl;
  460. return false;
  461. }
  462. while (++i < len) {
  463. INDEX d;
  464. c = fileName.at(i);
  465. if (c >= '0' && c <= '9') {
  466. d = c - '0';
  467. } else if (c >= 'a' && c <= 'z') {
  468. d = c - 'a' + 10;
  469. } else {
  470. cout << "Invalid Common Store file name: " << fileName << endl;
  471. return false;
  472. }
  473. hi = hi * 36 + d;
  474. }
  475. csIndex = (INDEX) hi << 32 | lo;
  476. return true;
  477. }
  478. class LinkFile {
  479. public:
  480. LinkFile(INDEX i = 0, LONGLONG id = 0, INDEX cs = 0, int v = 0, string n = 0) :
  481. index(i), NtfsId(id), csIndex(cs), version(v), name(n) {}
  482. friend bool operator<(const LinkFile& a, const LinkFile& b) {
  483. return a.index < b.index;
  484. }
  485. friend bool operator>(const LinkFile& a, const LinkFile& b) {
  486. return a.index > b.index;
  487. }
  488. friend bool operator==(const LinkFile& a, const LinkFile& b) {
  489. return a.index == b.index;
  490. }
  491. INDEX& LinkIndex() {
  492. return index;
  493. }
  494. string& FileName() {
  495. return name;
  496. }
  497. void display() {
  498. cout << "Link: " << name <<
  499. " CS Index: " << csIndex <<
  500. " Link Index:" << index <<
  501. " Id:" << NtfsId <<
  502. " Version: " << version << endl;
  503. }
  504. private:
  505. //
  506. // This file's Ntfs Id.
  507. //
  508. LONGLONG NtfsId;
  509. //
  510. // Link index associated with this file.
  511. //
  512. INDEX index;
  513. //
  514. // The common store file (index) associated with this link.
  515. //
  516. INDEX csIndex;
  517. //
  518. // The revision number of this link file.
  519. //
  520. ULONG version;
  521. //
  522. // The fully qualified file name.
  523. //
  524. string name;
  525. };
  526. //
  527. // The SIS Volume object.
  528. //
  529. class SISVolume {
  530. public:
  531. //
  532. // Validate all SIS files on the volume.
  533. //
  534. void Validate(string& Volume);
  535. //
  536. // Set up a volume for use with SIS.
  537. //
  538. bool Create(string& Volume);
  539. private:
  540. //
  541. // The bits that are actually in a SIS reparse point.
  542. //
  543. //
  544. // Version 1
  545. //
  546. typedef struct _SI_REPARSE_BUFFER_V1 {
  547. //
  548. // A version number so that we can change the reparse point format
  549. // and still properly handle old ones. This structure describes
  550. // version 1.
  551. //
  552. ULONG ReparsePointFormatVersion;
  553. //
  554. // The index of the common store file.
  555. //
  556. INDEX CSIndex;
  557. //
  558. // The index of this link file.
  559. //
  560. INDEX LinkIndex;
  561. } SI_REPARSE_BUFFER_V1, *PSI_REPARSE_BUFFER_V1;
  562. //
  563. // Version 2
  564. //
  565. typedef struct _SI_REPARSE_BUFFER_V2 {
  566. //
  567. // A version number so that we can change the reparse point format
  568. // and still properly handle old ones. This structure describes
  569. // version 2.
  570. //
  571. ULONG ReparsePointFormatVersion;
  572. //
  573. // The index of the common store file.
  574. //
  575. INDEX CSIndex;
  576. //
  577. // The index of this link file.
  578. //
  579. INDEX LinkIndex;
  580. //
  581. // The file ID of the link file.
  582. //
  583. LONGLONG LinkFileNtfsId;
  584. //
  585. // A "131 hash" checksum of this structure.
  586. // N.B. Must be last.
  587. //
  588. LARGE_INTEGER Checksum;
  589. } SI_REPARSE_BUFFER_V2, *PSI_REPARSE_BUFFER_V2;
  590. //
  591. // The bits that are actually in a SIS reparse point. Version 3.
  592. //
  593. typedef struct _SI_REPARSE_BUFFER {
  594. //
  595. // A version number so that we can change the reparse point format
  596. // and still properly handle old ones. This structure describes
  597. // version 1.
  598. //
  599. ULONG ReparsePointFormatVersion;
  600. //
  601. // The index of the common store file.
  602. //
  603. INDEX CSIndex;
  604. //
  605. // The index of this link file.
  606. //
  607. INDEX LinkIndex;
  608. //
  609. // The file ID of the link file.
  610. //
  611. LONGLONG LinkFileNtfsId;
  612. //
  613. // The file ID of the common store file.
  614. //
  615. LONGLONG CSFileNtfsId;
  616. //
  617. // A "131 hash" checksum of this structure.
  618. // N.B. Must be last.
  619. //
  620. LARGE_INTEGER Checksum;
  621. } SI_REPARSE_BUFFER, *PSI_REPARSE_BUFFER;
  622. #define SIS_REPARSE_BUFFER_FORMAT_VERSION_1 1
  623. #define SIS_REPARSE_BUFFER_FORMAT_VERSION_2 2
  624. #define SIS_REPARSE_BUFFER_FORMAT_VERSION 3
  625. #define SIS_MAX_REPARSE_DATA_VALUE_LENGTH (sizeof(SI_REPARSE_BUFFER))
  626. #define SIS_REPARSE_DATA_SIZE (sizeof(REPARSE_DATA_BUFFER)+SIS_MAX_REPARSE_DATA_VALUE_LENGTH)
  627. void Walk(string& dirName);
  628. bool GetLinkInfo(string& fileName, SI_REPARSE_BUFFER& linkInfo);
  629. void ComputeChecksum(PVOID buffer, ULONG size, PLARGE_INTEGER checksum);
  630. void ValidateLink();
  631. //
  632. // The common store object associated with this volume.
  633. //
  634. CommonStore cs;
  635. //
  636. // Database of link files. The link files are recorded to verify that
  637. // duplicate link indices do not occur, and also to be able to identify
  638. // all link files associated with a particular common store file.
  639. //
  640. vector<LinkFile> linkFiles;
  641. };
  642. void
  643. SISVolume::Validate(string& Volume)
  644. {
  645. string ntVolume("\\\\.\\" + Volume);
  646. //
  647. // See if we can open the volume.
  648. //
  649. HANDLE hVolume = CreateFile(
  650. ntVolume.c_str(),
  651. GENERIC_READ,
  652. FILE_SHARE_READ | FILE_SHARE_WRITE,
  653. NULL,
  654. OPEN_EXISTING,
  655. FILE_ATTRIBUTE_NORMAL,
  656. NULL);
  657. if (hVolume == INVALID_HANDLE_VALUE) {
  658. cout << "Can't open " << Volume << endl;
  659. return;
  660. } else {
  661. CloseHandle(hVolume);
  662. }
  663. //
  664. // Check the common store directory and it's files. This will also build
  665. // a database of common store files that will be used to validate the link
  666. // files.
  667. //
  668. cs.Validate(Volume);
  669. cout << "Checking Link Files" << endl;
  670. //
  671. // Enumerate all of the files on the volume looking for SIS links.
  672. //
  673. // if the file is a SIS reparse point then validate it:
  674. // - link index (against MaxIndex and other link indices)
  675. // - CS index (lookup in CommonStore)
  676. //
  677. Walk( Volume + "\\" );
  678. //
  679. // Now we can check the reference counts in the common store files.
  680. //
  681. cout << "Checking Reference Counts" << endl;
  682. cs.ValidateRefCounts();
  683. //
  684. // Check for duplicate link indices.
  685. //
  686. cout << "Checking Link Indices" << endl;
  687. sort(linkFiles.begin(), linkFiles.end());
  688. vector<LinkFile>::iterator p = linkFiles.begin();
  689. if (p != linkFiles.end()) {
  690. for (++p; p != linkFiles.end(); ++p) {
  691. if (p == (p-1)) {
  692. cout << "Duplicate link index (" << (INDEX) p->LinkIndex() << "): ";
  693. cout << p->FileName() << ", " << (p-1)->FileName() << endl;
  694. }
  695. }
  696. }
  697. }
  698. void
  699. SISVolume::Walk(string& dirName)
  700. {
  701. WIN32_FIND_DATA findData;
  702. HANDLE findHandle;
  703. const string fileNameMatchAny = dirName + "*";
  704. //
  705. // Enumerate all files in the specified directory, looking for SIS links.
  706. //
  707. findHandle = FindFirstFile( fileNameMatchAny.c_str(), &findData );
  708. if (INVALID_HANDLE_VALUE == findHandle) {
  709. //
  710. // Empty directory.
  711. //
  712. return;
  713. }
  714. do {
  715. //
  716. // Check for a SIS link.
  717. //
  718. if (( findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) &&
  719. ( findData.dwReserved0 == IO_REPARSE_TAG_SIS )) {
  720. if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  721. //
  722. // File is both a directory and a SIS link -- illegal.
  723. //
  724. cout << dirName << findData.cFileName << " SIS link directory." << endl;
  725. }
  726. SI_REPARSE_BUFFER linkInfo;
  727. //
  728. // Read the reparse point data to get the link index and
  729. // common store index.
  730. //
  731. if (! GetLinkInfo(dirName + findData.cFileName, linkInfo)) {
  732. cout << dirName << findData.cFileName << " : invalid link information." << endl;
  733. continue;
  734. }
  735. //
  736. // Create a LinkFile object.
  737. //
  738. LinkFile lf(linkInfo.LinkIndex,
  739. linkInfo.LinkFileNtfsId,
  740. linkInfo.CSIndex,
  741. linkInfo.ReparsePointFormatVersion,
  742. dirName + findData.cFileName);
  743. //
  744. // And add it to our database. Expand the database first if necessary.
  745. //
  746. if (0 == linkFiles.capacity())
  747. linkFiles.reserve(linkFiles.size() + 200);
  748. linkFiles.push_back(lf);
  749. if (! cs.ValidateIndex(linkInfo.LinkIndex)) {
  750. cout << "Invalid Link index: " << lf.FileName() << "(" << (INDEX) linkInfo.LinkIndex << ")" << endl;
  751. }
  752. //
  753. // Find the common store file.
  754. //
  755. CsFile *pcsFile = cs.Query(linkInfo.CSIndex);
  756. if (pcsFile == 0) {
  757. //
  758. // cs file was not found.
  759. //
  760. cout << "Common Store file " << (INDEX) linkInfo.CSIndex << " not found." << endl;
  761. } else {
  762. //
  763. // Update the external reference count on the common store file.
  764. //
  765. pcsFile->IncRefCount();
  766. }
  767. //
  768. // Make sure the link index isn't in use as a common store index.
  769. //
  770. pcsFile = cs.Query(linkInfo.LinkIndex);
  771. if (pcsFile != 0) {
  772. cout << "Link index collision with common store file. Link: ";
  773. cout << lf.FileName() << ", index: " << (INDEX) linkInfo.LinkIndex << endl;
  774. }
  775. if (verbose)
  776. lf.display();
  777. } else if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  778. //
  779. // Ignore \. and \..
  780. //
  781. if ( findData.cFileName[0] == '.' ) {
  782. if (( findData.cFileName[1] == 0 ) ||
  783. (( findData.cFileName[1] == '.' ) && ( findData.cFileName[2] == 0 )))
  784. continue;
  785. }
  786. //
  787. // Walk down this directory.
  788. //
  789. Walk( dirName + findData.cFileName + "\\" );
  790. }
  791. } while ( FindNextFile( findHandle, &findData ) );
  792. FindClose( findHandle );
  793. }
  794. #define SHARE_ALL (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
  795. bool
  796. SISVolume::GetLinkInfo(string& fileName, SI_REPARSE_BUFFER& linkInfo)
  797. {
  798. NTSTATUS Status = STATUS_SUCCESS;
  799. HANDLE fileHandle;
  800. UNICODE_STRING ufileName,
  801. uNTName;
  802. IO_STATUS_BLOCK IoStatusBlock;
  803. OBJECT_ATTRIBUTES ObjectAttributes;
  804. PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
  805. UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  806. LARGE_INTEGER Checksum;
  807. //
  808. // Allocate and initialize Unicode string.
  809. //
  810. RtlCreateUnicodeStringFromAsciiz( &ufileName, fileName.c_str() );
  811. RtlDosPathNameToNtPathName_U(
  812. ufileName.Buffer,
  813. &uNTName,
  814. NULL,
  815. NULL );
  816. //
  817. // Open the file.
  818. // Notice that if there are symbolic links in the path they are
  819. // traversed silently.
  820. //
  821. InitializeObjectAttributes(
  822. &ObjectAttributes,
  823. &uNTName,
  824. OBJ_CASE_INSENSITIVE,
  825. NULL,
  826. NULL );
  827. //
  828. // Make sure that we call open with the appropriate flags for:
  829. //
  830. // (1) directory versus non-directory
  831. // (2) reparse point
  832. //
  833. ULONG OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE;
  834. Status = NtOpenFile(
  835. &fileHandle,
  836. FILE_READ_DATA | SYNCHRONIZE,
  837. &ObjectAttributes,
  838. &IoStatusBlock,
  839. SHARE_ALL,
  840. OpenOptions );
  841. RtlFreeUnicodeString( &ufileName );
  842. if (!NT_SUCCESS( Status )) {
  843. cout << "Unable to open SIS link file: " << fileName << endl;
  844. return false;
  845. }
  846. //
  847. // Get the reparse point.
  848. //
  849. Status = NtFsControlFile(
  850. fileHandle,
  851. NULL,
  852. NULL,
  853. NULL,
  854. &IoStatusBlock,
  855. FSCTL_GET_REPARSE_POINT,
  856. NULL, // Input buffer
  857. 0, // Input buffer length
  858. ReparseBuffer, // Output buffer
  859. MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length
  860. NtClose( fileHandle );
  861. if (!NT_SUCCESS( Status )) {
  862. cout << "FSCTL_GET_REPARSE_POINT failed, " << (ULONG)IoStatusBlock.Information << ", " << fileName << endl;
  863. return false;
  864. }
  865. //
  866. // Copy the SIS link info from the reparse buffer to the caller's buffer.
  867. //
  868. ReparseBufferHeader = (PREPARSE_DATA_BUFFER) ReparseBuffer;
  869. if (ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SIS) {
  870. PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER) ReparseBufferHeader->GenericReparseBuffer.DataBuffer;
  871. linkInfo = *sisReparseBuffer;
  872. //
  873. // Now check to be sure that we understand this reparse point format version and
  874. // that it has the correct size.
  875. //
  876. if (ReparseBufferHeader->ReparseDataLength != sizeof(SI_REPARSE_BUFFER)
  877. || (sisReparseBuffer->ReparsePointFormatVersion != SIS_REPARSE_BUFFER_FORMAT_VERSION)) {
  878. //
  879. // We don't understand it, so either its corrupt or from a newer version of SIS.
  880. // Either way, we can't understand it, so punt.
  881. //
  882. cout << "Invalid format version in " << fileName
  883. << " Version: " << sisReparseBuffer->ReparsePointFormatVersion
  884. << ", expected: " << SIS_REPARSE_BUFFER_FORMAT_VERSION << endl;
  885. return FALSE;
  886. }
  887. //
  888. // Now check the checksum.
  889. //
  890. ComputeChecksum(
  891. sisReparseBuffer,
  892. sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum,
  893. &Checksum);
  894. if (Checksum.QuadPart != sisReparseBuffer->Checksum.QuadPart) {
  895. cout << "Invalid checksum in " << fileName << endl;
  896. return FALSE;
  897. }
  898. } else {
  899. cout << "Unexpected error. " << fileName << " : expected SIS link file, tag: " << ReparseBufferHeader->ReparseTag << endl;
  900. return false;
  901. }
  902. return true;
  903. }
  904. VOID
  905. SISVolume::ComputeChecksum(
  906. IN PVOID buffer,
  907. IN ULONG size,
  908. OUT PLARGE_INTEGER checksum)
  909. /*++
  910. Routine Description:
  911. Compute a checksum for a buffer. We use the "131 hash," which
  912. works by keeping a 64 bit running total, and for each 32 bits of
  913. data multiplying the 64 bits by 131 and adding in the next 32
  914. bits. Must be called at PASSIVE_LEVEL, and all aruments
  915. may be pagable.
  916. Arguments:
  917. buffer - pointer to the data to be checksummed
  918. size - size of the data to be checksummed
  919. checksum - pointer to large integer to receive the checksum. This
  920. may be within the buffer, and SipComputeChecksum guarantees that
  921. the initial value will be used in computing the checksum.
  922. Return Value:
  923. Returns STATUS_SUCCESS or an error returned from the actual disk write.
  924. --*/
  925. {
  926. LARGE_INTEGER runningTotal;
  927. ULONG *ptr = (ULONG *)buffer;
  928. ULONG bytesRemaining = size;
  929. runningTotal.QuadPart = 0;
  930. while (bytesRemaining >= sizeof(*ptr)) {
  931. runningTotal.QuadPart = runningTotal.QuadPart * 131 + *ptr;
  932. bytesRemaining -= sizeof(*ptr);
  933. ptr++;
  934. }
  935. if (bytesRemaining > 0) {
  936. ULONG extra;
  937. extra = 0;
  938. memmove(&extra, ptr, bytesRemaining);
  939. runningTotal.QuadPart = runningTotal.QuadPart * 131 + extra;
  940. }
  941. *checksum = runningTotal;
  942. }
  943. bool
  944. SISVolume::Create(string& Volume)
  945. {
  946. string ntVolume("\\\\.\\" + Volume);
  947. //
  948. // See if we can open the volume.
  949. //
  950. HANDLE hVolume = CreateFile(
  951. ntVolume.c_str(),
  952. GENERIC_READ,
  953. FILE_SHARE_READ | FILE_SHARE_WRITE,
  954. NULL,
  955. OPEN_EXISTING,
  956. FILE_ATTRIBUTE_NORMAL,
  957. NULL);
  958. if (hVolume == INVALID_HANDLE_VALUE) {
  959. cout << "Can't open " << Volume << endl;
  960. return false;
  961. } else {
  962. CloseHandle(hVolume);
  963. }
  964. //
  965. // The common store is the only thing we need to create.
  966. //
  967. return cs.Create(Volume);
  968. }
  969. void
  970. usage()
  971. {
  972. cout << "Usage: chksis [-vc] [drive:]\n -v: verbose\n -c: create SIS volume" << endl;
  973. }
  974. int
  975. __cdecl
  976. main(int argc, char *argv[])
  977. {
  978. string volume("C:");
  979. bool volumeArgSeen = false;
  980. bool create = false;
  981. SISVolume sis;
  982. for (int i = 1; i < argc; ++i) {
  983. if (argv[i][0] == '-') {
  984. if (volumeArgSeen) {
  985. usage();
  986. exit(1);
  987. }
  988. switch (argv[i][1]) {
  989. case 'v':
  990. verbose = true;
  991. break;
  992. case 'c':
  993. create = true;
  994. break;
  995. default:
  996. usage();
  997. exit(1);
  998. }
  999. } else {
  1000. volumeArgSeen = true;
  1001. volume.assign(argv[i]);
  1002. }
  1003. }
  1004. if (create) {
  1005. if (! volumeArgSeen) {
  1006. cout << "Must specify volume with -c" << endl;
  1007. exit(1);
  1008. }
  1009. sis.Create(volume);
  1010. exit(0);
  1011. }
  1012. if (! volumeArgSeen)
  1013. cout << "Checking " << volume << endl;
  1014. sis.Validate(volume);
  1015. return 0;
  1016. }