|
|
/*++
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 <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntioapi.h>
#include <windows.h>
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<unsigned long> (index); long hi = static_cast<long> (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<CsFile> 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<CsFile>::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<CsFile>::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<LinkFile> 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<LinkFile>::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; }
|