/*++ Copyright (c) 1996 Microsoft Corporation Module Name: chksis.cpp Abstract: This module implements a utility that examines all SIS files on a volume looking for errors and optionally displaying file information. Author: Scott Cutshall Fall, 1997 --*/ #include #include #include #include #include #include #include #include #include using namespace std; bool verbose = false; typedef LONGLONG INDEX; // // Convert a 32bit value to a base 36 representation in // the caller provided string. // void IntegerToBase36String(ULONG val, string& s) { // // Maximum number of "digits" in a base 36 representation of a 32 bit // value is 7. // char rs[8]; ULONG v = val; rs[7] = 0; for (int i = 7; i == 7 || v != 0;) { ULONG d = v % 36; v = v / 36; --i; if (d < 10) rs[i] = '0' + d; else rs[i] = 'a' + d - 10; } s.assign(&rs[i]); } // // A put operator for INDEX types. Implemented as IndexToSISFileName(). // #ifndef _WIN64 ostream& operator<<(ostream& out, INDEX& index) { unsigned long lo = static_cast (index); long hi = static_cast (index >> 32); string s("1234567"); IntegerToBase36String(lo, s); out << s << '.'; IntegerToBase36String(hi, s); out << s; return out; } #endif // // A common store file object. Holds the file's index, name, internal refcount, // external refcount, and identity operations. // class CsFile { public: CsFile(INDEX i = 0, int r = 0, string n = "") : index(i), internalRefCount(r), name(n), externalRefCount(0) {} void Validate() { if (internalRefCount != externalRefCount) { cout << name << " Reference Count: " << internalRefCount; cout << ". " << externalRefCount << " external references identified." << endl; } } friend bool operator<(const CsFile& a, const CsFile& b) { return a.index < b.index; } friend bool operator>(const CsFile& a, const CsFile& b) { return a.index > b.index; } friend bool operator==(const CsFile& a, const CsFile& b) { return a.index == b.index; } void IncRefCount() { ++externalRefCount; } void display() { cout << "CS Index: " << (INDEX) index << " Ref Count: " << internalRefCount << endl; } private: // // Index of this entry's file. // INDEX index; // // The file name. This is somewhat redundant with the index (ie. the // name is derived from the index), so it isn't absolutely necessary. // string name; // // Reference count read from the file's refcount stream. // int internalRefCount; // // Number of valid references to this file detected during scan. // int externalRefCount; }; // // The SIS Common Store object. Holds all common store file objects, and // validation and query operations. // class CommonStore { public: CommonStore(int vsize = 0) : maxIndex(0) { if (vsize > 0) csFiles.resize(vsize); } // // Method to create a common store on a volume. // bool Create(string& Volume); // // Validate the common store directory and initialize this class. // void Validate(string& Volume); // // Validate the reference counts. Assumes all external references // have been identified. // void ValidateRefCounts(); // // All indices must be less than maxIndex; // bool ValidateIndex(INDEX i) { return i <= maxIndex; } // // Lookup a common store index and add a ref if found. // CsFile *Query(INDEX index); private: bool FileNameToIndex(string& fileName, INDEX& csIndex); // // Index from the MaxIndex file. // INDEX maxIndex; // // Database of content files. All CS files are examined and added to the database, // sorted, and subsequently used during the SIS link scan. // vector csFiles; }; // // Various SIS file and directory names. // const string maxIndexFileName("MaxIndex"); const string logFileName("LogFile"); const string csDir("\\SIS Common Store\\"); // // Create a common store directory on a volume. // // todo: // - Verify that the volume is ntfs. // - Verify that the SIS driver is loaded. // bool CommonStore::Create(string& Volume) { const string CommonStoreDir = Volume + "\\SIS Common Store"; USHORT comp = COMPRESSION_FORMAT_DEFAULT; DWORD transferCount; bool rc; if (! CreateDirectory(CommonStoreDir.c_str(), NULL) ) { cout << "Cannot create Common Store directory, " << GetLastError() << endl; return false; } if (verbose) cout << CommonStoreDir << " created" << endl; // // Open the Common Store directory and enable compression. // HANDLE CSDirHandle = CreateFile( CommonStoreDir.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (CSDirHandle == INVALID_HANDLE_VALUE) { cout << "Can't open Common Store directory." << endl; rc = false; } else { rc = 0 != DeviceIoControl( CSDirHandle, FSCTL_SET_COMPRESSION, &comp, sizeof(comp), NULL, 0, &transferCount, NULL); CloseHandle(CSDirHandle); } if (!rc) cout << "Cannot enable compression on Common Store directory, " << GetLastError() << endl; // // Chdir into the common store directory. // if (SetCurrentDirectory(CommonStoreDir.c_str()) == 0) { // // Unable to chdir into the common store. // cout << "\"\\SIS Common Store\" directory not found" << endl; return false; } rc = true; // // Create the MaxIndex file. // HANDLE hMaxIndex = CreateFile( maxIndexFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (hMaxIndex == INVALID_HANDLE_VALUE) { cout << "Can't create \"\\SIS Common Store\\MaxIndex\"" << endl; rc = false; } else { DWORD bytesWritten; maxIndex = 1; if (! WriteFile( hMaxIndex, &maxIndex, sizeof maxIndex, &bytesWritten, NULL) || (bytesWritten < sizeof maxIndex)) { cout << "Can't write MaxIndex, " << GetLastError() << endl; rc = false; } else { CloseHandle(hMaxIndex); if (verbose) cout << "MaxIndex: " << (INDEX) maxIndex << endl; rc = true; } } return rc; } // // Validate the common store directory. // void CommonStore::Validate(string& Volume) { WIN32_FIND_DATA findData; HANDLE findHandle; const string fileNameMatchAny = "*"; const string CommonStoreDir = Volume + "\\SIS Common Store"; cout << "Checking Common Store" << endl; // // Chdir into the common store directory. // if (SetCurrentDirectory(CommonStoreDir.c_str()) == 0) { // // Unable to chdir into the common store. // cout << "\"\\SIS Common Store\" directory not found" << endl; return; } // // Validate and read the contents of the MaxIndex file. // HANDLE hMaxIndex = CreateFile( maxIndexFileName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hMaxIndex == INVALID_HANDLE_VALUE) { cout << "Can't open \"\\SIS Common Store\\MaxIndex\"" << endl; } else { DWORD bytesRead; if (! ReadFile( hMaxIndex, &maxIndex, sizeof maxIndex, &bytesRead, NULL)) { cout << "Can't read MaxIndex, " << GetLastError() << endl; } if (bytesRead < sizeof maxIndex) { cout << "Invalid MaxIndex" << endl; } CloseHandle(hMaxIndex); if (verbose) cout << "MaxIndex: " << (INDEX) maxIndex << endl; } // // Enumerate and validate all files in the common store directory. // Save the file name and reference count for later lookup when validating // the SIS link files. // findHandle = FindFirstFile( fileNameMatchAny.c_str(), &findData ); if (INVALID_HANDLE_VALUE == findHandle) { cout << CommonStoreDir << " is empty." << endl; return; } do { ULONG refCount; string fileName; fileName = findData.cFileName; if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // // Ignore . and .. // if ( findData.cFileName[0] == '.' ) { if (( findData.cFileName[1] == 0 ) || (( findData.cFileName[1] == '.' ) && ( findData.cFileName[2] == 0 ))) continue; } cout << "Common Store directory skipped: " << fileName << endl; continue; } if ((_stricmp(maxIndexFileName.c_str(),fileName.c_str()) == 0) || (_stricmp(logFileName.c_str(),fileName.c_str()) == 0)) { // // Skip the MaxIndex and LogFile files. // continue; } // // Verify that: // - the file name is a valid index. // - this is a normal file (ie. not a reparse point). // - there is a refcount stream of proper format. // INDEX csIndex; refCount = 0; if (! FileNameToIndex(fileName, csIndex)) { cout << "Unknown file in Common Store: " << fileName << endl; continue; } if (! ValidateIndex(csIndex)) { cout << "Invalid CSIndex: " << fileName << endl; } if ( IO_REPARSE_TAG_SIS == findData.dwReserved0 ) { cout << "SIS link found in Common Store: " << fileName << endl; } else { // // Read in the refcount; // string refName(fileName + ":sisrefs$"); HANDLE hRefCount = CreateFile( refName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hRefCount == INVALID_HANDLE_VALUE) { cout << "Can't open ref count stream, " << refName << ", " << GetLastError() << endl; } else { DWORD bytesRead; if (! ReadFile( hRefCount, &refCount, sizeof refCount, &bytesRead, NULL)) { cout << "Can't read " << refName << ", " << GetLastError() << endl; } if (bytesRead < sizeof refCount) { cout << "Invalid ref count in " << refName << endl; } CloseHandle(hRefCount); } CsFile csFile(csIndex, refCount, fileName); // // Add this file to our database. Expand the database if necessary. // if (0 == csFiles.capacity()) csFiles.reserve(csFiles.size() + 200); csFiles.push_back(csFile); if (verbose) csFile.display(); } } while ( FindNextFile( findHandle, &findData ) ); FindClose( findHandle ); // // Sort the database for subsequent lookups. // sort(csFiles.begin(), csFiles.end()); } // // Validate the reference counts. Assumes all external references // have been identified. // void CommonStore::ValidateRefCounts() { vector::iterator p; for (p = csFiles.begin(); p != csFiles.end(); ++p) { p->Validate(); } } // // Lookup the specified index in the common store. // CsFile * CommonStore::Query(INDEX index) { CsFile key(index); // // Use a binary search to lookup the index. // vector::iterator p = lower_bound(csFiles.begin(), csFiles.end(), key); if (p == csFiles.end() || *p > key) return NULL; // not found return p; } // // Extract the index from a common store file name. // bool CommonStore::FileNameToIndex(string& fileName, INDEX& csIndex) { char c; const size_t len = fileName.length(); ULONG hi = 0, lo = 0; // // Format: "_low.high", where low.high is the base 36 representation of // the index value. // size_t i = 0; if (len < 2 || fileName.at(i) != '_') { cout << "Invalid Common Store file name: " << fileName << endl; return false; } while (++i < len && (c = fileName.at(i)) != '.') { INDEX d; if (c >= '0' && c <= '9') { d = c - '0'; } else if (c >= 'a' && c <= 'z') { d = c - 'a' + 10; } else { cout << "Invalid Common Store file name: " << fileName << endl; return false; } lo = lo * 36 + d; } if (c != '.') { cout << "Invalid Common Store file name: " << fileName << endl; return false; } while (++i < len) { INDEX d; c = fileName.at(i); if (c >= '0' && c <= '9') { d = c - '0'; } else if (c >= 'a' && c <= 'z') { d = c - 'a' + 10; } else { cout << "Invalid Common Store file name: " << fileName << endl; return false; } hi = hi * 36 + d; } csIndex = (INDEX) hi << 32 | lo; return true; } class LinkFile { public: LinkFile(INDEX i = 0, LONGLONG id = 0, INDEX cs = 0, int v = 0, string n = 0) : index(i), NtfsId(id), csIndex(cs), version(v), name(n) {} friend bool operator<(const LinkFile& a, const LinkFile& b) { return a.index < b.index; } friend bool operator>(const LinkFile& a, const LinkFile& b) { return a.index > b.index; } friend bool operator==(const LinkFile& a, const LinkFile& b) { return a.index == b.index; } INDEX& LinkIndex() { return index; } string& FileName() { return name; } void display() { cout << "Link: " << name << " CS Index: " << csIndex << " Link Index:" << index << " Id:" << NtfsId << " Version: " << version << endl; } private: // // This file's Ntfs Id. // LONGLONG NtfsId; // // Link index associated with this file. // INDEX index; // // The common store file (index) associated with this link. // INDEX csIndex; // // The revision number of this link file. // ULONG version; // // The fully qualified file name. // string name; }; // // The SIS Volume object. // class SISVolume { public: // // Validate all SIS files on the volume. // void Validate(string& Volume); // // Set up a volume for use with SIS. // bool Create(string& Volume); private: // // The bits that are actually in a SIS reparse point. // // // Version 1 // typedef struct _SI_REPARSE_BUFFER_V1 { // // A version number so that we can change the reparse point format // and still properly handle old ones. This structure describes // version 1. // ULONG ReparsePointFormatVersion; // // The index of the common store file. // INDEX CSIndex; // // The index of this link file. // INDEX LinkIndex; } SI_REPARSE_BUFFER_V1, *PSI_REPARSE_BUFFER_V1; // // Version 2 // typedef struct _SI_REPARSE_BUFFER_V2 { // // A version number so that we can change the reparse point format // and still properly handle old ones. This structure describes // version 2. // ULONG ReparsePointFormatVersion; // // The index of the common store file. // INDEX CSIndex; // // The index of this link file. // INDEX LinkIndex; // // The file ID of the link file. // LONGLONG LinkFileNtfsId; // // A "131 hash" checksum of this structure. // N.B. Must be last. // LARGE_INTEGER Checksum; } SI_REPARSE_BUFFER_V2, *PSI_REPARSE_BUFFER_V2; // // The bits that are actually in a SIS reparse point. Version 3. // typedef struct _SI_REPARSE_BUFFER { // // A version number so that we can change the reparse point format // and still properly handle old ones. This structure describes // version 1. // ULONG ReparsePointFormatVersion; // // The index of the common store file. // INDEX CSIndex; // // The index of this link file. // INDEX LinkIndex; // // The file ID of the link file. // LONGLONG LinkFileNtfsId; // // The file ID of the common store file. // LONGLONG CSFileNtfsId; // // A "131 hash" checksum of this structure. // N.B. Must be last. // LARGE_INTEGER Checksum; } SI_REPARSE_BUFFER, *PSI_REPARSE_BUFFER; #define SIS_REPARSE_BUFFER_FORMAT_VERSION_1 1 #define SIS_REPARSE_BUFFER_FORMAT_VERSION_2 2 #define SIS_REPARSE_BUFFER_FORMAT_VERSION 3 #define SIS_MAX_REPARSE_DATA_VALUE_LENGTH (sizeof(SI_REPARSE_BUFFER)) #define SIS_REPARSE_DATA_SIZE (sizeof(REPARSE_DATA_BUFFER)+SIS_MAX_REPARSE_DATA_VALUE_LENGTH) void Walk(string& dirName); bool GetLinkInfo(string& fileName, SI_REPARSE_BUFFER& linkInfo); void ComputeChecksum(PVOID buffer, ULONG size, PLARGE_INTEGER checksum); void ValidateLink(); // // The common store object associated with this volume. // CommonStore cs; // // Database of link files. The link files are recorded to verify that // duplicate link indices do not occur, and also to be able to identify // all link files associated with a particular common store file. // vector linkFiles; }; void SISVolume::Validate(string& Volume) { string ntVolume("\\\\.\\" + Volume); // // See if we can open the volume. // HANDLE hVolume = CreateFile( ntVolume.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVolume == INVALID_HANDLE_VALUE) { cout << "Can't open " << Volume << endl; return; } else { CloseHandle(hVolume); } // // Check the common store directory and it's files. This will also build // a database of common store files that will be used to validate the link // files. // cs.Validate(Volume); cout << "Checking Link Files" << endl; // // Enumerate all of the files on the volume looking for SIS links. // // if the file is a SIS reparse point then validate it: // - link index (against MaxIndex and other link indices) // - CS index (lookup in CommonStore) // Walk( Volume + "\\" ); // // Now we can check the reference counts in the common store files. // cout << "Checking Reference Counts" << endl; cs.ValidateRefCounts(); // // Check for duplicate link indices. // cout << "Checking Link Indices" << endl; sort(linkFiles.begin(), linkFiles.end()); vector::iterator p = linkFiles.begin(); if (p != linkFiles.end()) { for (++p; p != linkFiles.end(); ++p) { if (p == (p-1)) { cout << "Duplicate link index (" << (INDEX) p->LinkIndex() << "): "; cout << p->FileName() << ", " << (p-1)->FileName() << endl; } } } } void SISVolume::Walk(string& dirName) { WIN32_FIND_DATA findData; HANDLE findHandle; const string fileNameMatchAny = dirName + "*"; // // Enumerate all files in the specified directory, looking for SIS links. // findHandle = FindFirstFile( fileNameMatchAny.c_str(), &findData ); if (INVALID_HANDLE_VALUE == findHandle) { // // Empty directory. // return; } do { // // Check for a SIS link. // if (( findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) && ( findData.dwReserved0 == IO_REPARSE_TAG_SIS )) { if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // // File is both a directory and a SIS link -- illegal. // cout << dirName << findData.cFileName << " SIS link directory." << endl; } SI_REPARSE_BUFFER linkInfo; // // Read the reparse point data to get the link index and // common store index. // if (! GetLinkInfo(dirName + findData.cFileName, linkInfo)) { cout << dirName << findData.cFileName << " : invalid link information." << endl; continue; } // // Create a LinkFile object. // LinkFile lf(linkInfo.LinkIndex, linkInfo.LinkFileNtfsId, linkInfo.CSIndex, linkInfo.ReparsePointFormatVersion, dirName + findData.cFileName); // // And add it to our database. Expand the database first if necessary. // if (0 == linkFiles.capacity()) linkFiles.reserve(linkFiles.size() + 200); linkFiles.push_back(lf); if (! cs.ValidateIndex(linkInfo.LinkIndex)) { cout << "Invalid Link index: " << lf.FileName() << "(" << (INDEX) linkInfo.LinkIndex << ")" << endl; } // // Find the common store file. // CsFile *pcsFile = cs.Query(linkInfo.CSIndex); if (pcsFile == 0) { // // cs file was not found. // cout << "Common Store file " << (INDEX) linkInfo.CSIndex << " not found." << endl; } else { // // Update the external reference count on the common store file. // pcsFile->IncRefCount(); } // // Make sure the link index isn't in use as a common store index. // pcsFile = cs.Query(linkInfo.LinkIndex); if (pcsFile != 0) { cout << "Link index collision with common store file. Link: "; cout << lf.FileName() << ", index: " << (INDEX) linkInfo.LinkIndex << endl; } if (verbose) lf.display(); } else if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // // Ignore \. and \.. // if ( findData.cFileName[0] == '.' ) { if (( findData.cFileName[1] == 0 ) || (( findData.cFileName[1] == '.' ) && ( findData.cFileName[2] == 0 ))) continue; } // // Walk down this directory. // Walk( dirName + findData.cFileName + "\\" ); } } while ( FindNextFile( findHandle, &findData ) ); FindClose( findHandle ); } #define SHARE_ALL (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) bool SISVolume::GetLinkInfo(string& fileName, SI_REPARSE_BUFFER& linkInfo) { NTSTATUS Status = STATUS_SUCCESS; HANDLE fileHandle; UNICODE_STRING ufileName, uNTName; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL; UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; LARGE_INTEGER Checksum; // // Allocate and initialize Unicode string. // RtlCreateUnicodeStringFromAsciiz( &ufileName, fileName.c_str() ); RtlDosPathNameToNtPathName_U( ufileName.Buffer, &uNTName, NULL, NULL ); // // Open the file. // Notice that if there are symbolic links in the path they are // traversed silently. // InitializeObjectAttributes( &ObjectAttributes, &uNTName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Make sure that we call open with the appropriate flags for: // // (1) directory versus non-directory // (2) reparse point // ULONG OpenOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE; Status = NtOpenFile( &fileHandle, FILE_READ_DATA | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, SHARE_ALL, OpenOptions ); RtlFreeUnicodeString( &ufileName ); if (!NT_SUCCESS( Status )) { cout << "Unable to open SIS link file: " << fileName << endl; return false; } // // Get the reparse point. // Status = NtFsControlFile( fileHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_GET_REPARSE_POINT, NULL, // Input buffer 0, // Input buffer length ReparseBuffer, // Output buffer MAXIMUM_REPARSE_DATA_BUFFER_SIZE ); // Output buffer length NtClose( fileHandle ); if (!NT_SUCCESS( Status )) { cout << "FSCTL_GET_REPARSE_POINT failed, " << (ULONG)IoStatusBlock.Information << ", " << fileName << endl; return false; } // // Copy the SIS link info from the reparse buffer to the caller's buffer. // ReparseBufferHeader = (PREPARSE_DATA_BUFFER) ReparseBuffer; if (ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SIS) { PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER) ReparseBufferHeader->GenericReparseBuffer.DataBuffer; linkInfo = *sisReparseBuffer; // // Now check to be sure that we understand this reparse point format version and // that it has the correct size. // if (ReparseBufferHeader->ReparseDataLength != sizeof(SI_REPARSE_BUFFER) || (sisReparseBuffer->ReparsePointFormatVersion != SIS_REPARSE_BUFFER_FORMAT_VERSION)) { // // We don't understand it, so either its corrupt or from a newer version of SIS. // Either way, we can't understand it, so punt. // cout << "Invalid format version in " << fileName << " Version: " << sisReparseBuffer->ReparsePointFormatVersion << ", expected: " << SIS_REPARSE_BUFFER_FORMAT_VERSION << endl; return FALSE; } // // Now check the checksum. // ComputeChecksum( sisReparseBuffer, sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum, &Checksum); if (Checksum.QuadPart != sisReparseBuffer->Checksum.QuadPart) { cout << "Invalid checksum in " << fileName << endl; return FALSE; } } else { cout << "Unexpected error. " << fileName << " : expected SIS link file, tag: " << ReparseBufferHeader->ReparseTag << endl; return false; } return true; } VOID SISVolume::ComputeChecksum( IN PVOID buffer, IN ULONG size, OUT PLARGE_INTEGER checksum) /*++ Routine Description: Compute a checksum for a buffer. We use the "131 hash," which works by keeping a 64 bit running total, and for each 32 bits of data multiplying the 64 bits by 131 and adding in the next 32 bits. Must be called at PASSIVE_LEVEL, and all aruments may be pagable. Arguments: buffer - pointer to the data to be checksummed size - size of the data to be checksummed checksum - pointer to large integer to receive the checksum. This may be within the buffer, and SipComputeChecksum guarantees that the initial value will be used in computing the checksum. Return Value: Returns STATUS_SUCCESS or an error returned from the actual disk write. --*/ { LARGE_INTEGER runningTotal; ULONG *ptr = (ULONG *)buffer; ULONG bytesRemaining = size; runningTotal.QuadPart = 0; while (bytesRemaining >= sizeof(*ptr)) { runningTotal.QuadPart = runningTotal.QuadPart * 131 + *ptr; bytesRemaining -= sizeof(*ptr); ptr++; } if (bytesRemaining > 0) { ULONG extra; extra = 0; memmove(&extra, ptr, bytesRemaining); runningTotal.QuadPart = runningTotal.QuadPart * 131 + extra; } *checksum = runningTotal; } bool SISVolume::Create(string& Volume) { string ntVolume("\\\\.\\" + Volume); // // See if we can open the volume. // HANDLE hVolume = CreateFile( ntVolume.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVolume == INVALID_HANDLE_VALUE) { cout << "Can't open " << Volume << endl; return false; } else { CloseHandle(hVolume); } // // The common store is the only thing we need to create. // return cs.Create(Volume); } void usage() { cout << "Usage: chksis [-vc] [drive:]\n -v: verbose\n -c: create SIS volume" << endl; } int __cdecl main(int argc, char *argv[]) { string volume("C:"); bool volumeArgSeen = false; bool create = false; SISVolume sis; for (int i = 1; i < argc; ++i) { if (argv[i][0] == '-') { if (volumeArgSeen) { usage(); exit(1); } switch (argv[i][1]) { case 'v': verbose = true; break; case 'c': create = true; break; default: usage(); exit(1); } } else { volumeArgSeen = true; volume.assign(argv[i]); } } if (create) { if (! volumeArgSeen) { cout << "Must specify volume with -c" << endl; exit(1); } sis.Create(volume); exit(0); } if (! volumeArgSeen) cout << "Checking " << volume << endl; sis.Validate(volume); return 0; }