NT4/private/ntos/config/utils/hivestat.c

784 lines
19 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
hivestat.c
Abstract:
Dumps various statistics on hv (low) level structures in a hive. (See
regstat for higher level stuff.)
Statistics:
Short: # of bins
average bin size
max bin size
# of cells
# of free cells
# of allocated cells
average free cell size
total free size
max free cell size
average allocated size
total allocated size
max allocated cell size
overhead summary (header, bin headers, cell headers)
Long: bin#, offset, size
cell offset, size, allocated
cell offset, size, free
Usage: {[+|-][<option>]} <filename>
(+ = on by default, - = off by default)
+s = summary - all of the short statistics
-t[bafc] = trace, line per entry, bin, allocated, free, all cells
(+tbc == +tbaf)
-c = cell type summary
-a[kvs] = Access Export (key nodes, values, SDs)
Author:
Bryan M. Willman (bryanwi) 2-Sep-1992
Revision History:
--*/
/*
NOTE: Unlike other hive/registry tools, this one will not read the
entire hive into memory, but will instead read it in via
file I/O. This makes it faster/easier to apply to very large
hives.
*/
#include "regutil.h"
#include "edithive.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
UCHAR *helptext[] = {
"hivestat: ",
"Statistics: ",
" Short: # of bins ",
" average bin size ",
" max bin size ",
" # of cells ",
" # of free cells ",
" # of allocated cells ",
" average free cell size ",
" total free size ",
" max free cell size ",
" average allocated size ",
" total allocated size ",
" max allocated cell size ",
" overhead summary (header, bin headers, cell headers) ",
" Long: bin#, offset, size ",
" cell offset, size, allocated ",
" cell offset, size, free ",
"Usage: {[+|-][<option>]} <filename> ",
" (+ = on by default, - = off by default) ",
" +s = summary - all of the short statistics ",
" -t[bafc] = trace, line per entry, bin, allocated, free, all cells",
" (+tbc == +tbaf) ",
" -c = cell type summary ",
" -a[kvs] = Access Export (key nodes, values, SDs) ",
NULL
};
VOID
ParseArgs(
int argc,
char *argv[]
);
VOID
ScanHive(
VOID
);
VOID
ScanCell(
PHCELL Cell,
ULONG CellSize
);
VOID
ScanKeyNode(
IN PCM_KEY_NODE Node,
IN ULONG CellSize
);
VOID
ScanKeyValue(
IN PCM_KEY_VALUE Value,
IN ULONG CellSize
);
VOID
ScanKeySD(
IN PCM_KEY_SECURITY Security,
IN ULONG CellSize
);
VOID
ScanKeyIndex(
IN PCM_KEY_INDEX Index,
IN ULONG CellSize
);
VOID
ScanUnknown(
IN PCELL_DATA Data,
IN ULONG CellSize
);
//
// CONTROL ARGUMENTS
//
BOOLEAN DoCellType = FALSE;
BOOLEAN DoSummary = TRUE;
BOOLEAN DoTraceBin = FALSE;
BOOLEAN DoTraceFree = FALSE;
BOOLEAN DoTraceAlloc = FALSE;
BOOLEAN AccessKeys = FALSE;
BOOLEAN AccessValues = FALSE;
BOOLEAN AccessSD = FALSE;
LPCTSTR FileName = NULL;
ULONG HiveVersion;
//
// SUMMARY TOTALS
//
ULONG SizeKeyData=0;
ULONG SizeValueData=0;
ULONG SizeSDData=0;
ULONG SizeIndexData=0;
ULONG SizeUnknownData=0;
ULONG NumKeyData=0;
ULONG NumValueData=0;
ULONG NumSDData=0;
ULONG NumIndexData=0;
ULONG NumUnknownData=0;
void
main(
int argc,
char *argv[]
)
{
ParseArgs(argc, argv);
ScanHive();
exit(0);
}
VOID
ParseArgs(
int argc,
char *argv[]
)
/*++
Routine Description:
Read arguments and set control arguments and file name from them.
Arguments:
argc, argv, standard meaning
Return Value:
None.
--*/
{
char *p;
int i;
BOOLEAN command;
if (argc == 1) {
for (i = 0; helptext[i] != NULL; i++) {
fprintf(stderr, "%s\n", helptext[i]);
}
exit(1);
}
for (i = 1; i < argc; i++) {
p = argv[i];
if (*p == '+') {
// switch something on
command = TRUE;
} else if (*p == '-') {
// switch something off
command = FALSE;
} else {
FileName = p;
continue;
}
p++;
if (*p == '\0')
continue;
switch (*p) {
case 's':
case 'S':
DoSummary = command;
break;
case 'c':
case 'C':
DoCellType = command;
break;
case 'a':
case 'A':
p++;
while (*p != '\0') {
switch (*p) {
case 'k':
case 'K':
AccessKeys = command;
break;
case 's':
case 'S':
AccessSD = command;
break;
case 'v':
case 'V':
AccessValues = command;
break;
default:
break;
}
p++;
}
break;
case 't':
case 'T':
p++;
while (*p != '\0') {
switch (*p) {
case 'b':
case 'B':
DoTraceBin = command;
break;
case 'a':
case 'A':
DoTraceAlloc = command;
break;
case 'f':
case 'F':
DoTraceFree = command;
break;
case 'c':
case 'C':
DoTraceAlloc = command;
DoTraceFree = command;
break;
default:
break;
}
p++;
}
break;
default:
break;
}
}
return;
}
VOID
ScanHive(
)
/*++
Routine Description:
Scan the hive, report what we see, based on control arguments.
--*/
{
static char buffer[HBLOCK_SIZE];
PHBASE_BLOCK bbp;
HANDLE filehandle;
BOOL rf;
ULONG readcount;
ULONG hivelength;
ULONG hiveposition;
PHCELL cp;
PHCELL guard;
PHBIN hbp;
ULONG hoff;
ULONG StatBinCount = 0;
ULONG StatBinTotal = 0;
ULONG StatBinMax = 0;
ULONG StatFreeCount = 0;
ULONG StatFreeTotal = 0;
ULONG StatFreeMax = 0;
ULONG StatAllocCount = 0;
ULONG StatAllocTotal = 0;
ULONG StatAllocMax = 0;
ULONG binread;
ULONG binsize;
ULONG cellsize;
ULONG boff;
ULONG lboff;
ULONG SizeTotal;
//
// open the file
//
filehandle = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (filehandle == INVALID_HANDLE_VALUE) {
fprintf(stderr,
"hivestat: Could not open file '%s' error = %08lx\n",
FileName, GetLastError()
);
exit(1);
}
//
// read the header
//
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
fprintf(stderr, "hivestat: '%s' - cannot read base block!\n", FileName);
exit(1);
}
bbp = (PHBASE_BLOCK)(&(buffer[0]));
if ((bbp->Major != HSYS_MAJOR) ||
(bbp->Minor > HSYS_MINOR))
{
fprintf(stderr,
"hivestat: major/minor != %d/%d get newer hivestat\n",
HSYS_MAJOR, HSYS_MINOR
);
exit(1);
}
HiveVersion = bbp->Minor;
hivelength = bbp->Length + HBLOCK_SIZE;
hiveposition = HBLOCK_SIZE;
hoff = 0;
printf("hivestat: '%s'\n", FileName);
if (DoTraceBin || DoTraceFree || DoTraceAlloc) {
printf("\nTrace\n");
printf("bi=bin, fr=free, al=allocated\n");
printf("offset is file offset, sub HBLOCK to get HCELL\n");
printf("type,offset,size\n");
printf("\n");
}
//
// scan the hive
//
guard = (PHCELL)(&(buffer[0]) + HBLOCK_SIZE);
//
// hiveposition is file relative offset of next block we will read
//
// hoff is the file relative offset of the last block we read
//
// hivelength is actual length of file (header's recorded length plus
// the size of the header.
//
// cp is pointer into memory, within range of buffer, it's a cell pointer
//
while (hiveposition < hivelength) {
//
// read in first block of bin, check signature, get bin stats
//
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
fprintf(stderr, "hivestat: '%s' read error @%08lx\n", FileName, hiveposition);
exit(1);
}
hbp = (PHBIN)(&(buffer[0]));
if (hbp->Signature != HBIN_SIGNATURE) {
fprintf(stderr,
"hivestat: '%s' bad bin sign. @%08lx\n", FileName, hiveposition);
exit(1);
}
hiveposition += HBLOCK_SIZE;
hoff += HBLOCK_SIZE;
ASSERT(hoff+HBLOCK_SIZE == hiveposition);
StatBinCount++;
binsize = hbp->Size;
StatBinTotal += binsize;
if (binsize > StatBinMax) {
StatBinMax = binsize;
}
if (DoTraceBin) {
printf("bi,x%08lx,%ld\n", hoff, binsize);
}
//
// scan the bin
//
// cp = pointer to cell we are looking at
// boff = offset within bin
// lboff = last offset within bin, used only for consistency checks
// binread = number of bytes of bin we've read so far
//
cp = (PHCELL)((PUCHAR)hbp + sizeof(HBIN));
boff = sizeof(HBIN);
lboff = -1;
binread = HBLOCK_SIZE;
while (binread <= binsize) {
//
// if free, do free stuff
// else do alloc stuff
// do full stuff
//
if (cp->Size > 0) {
//
// free
//
cellsize = cp->Size;
StatFreeCount++;
StatFreeTotal += cellsize;
if (cellsize > StatFreeMax) {
StatFreeMax = cellsize;
}
if (DoTraceFree) {
printf("fr,x%08lx,%ld\n",
hoff+((PUCHAR)cp - &(buffer[0])), cellsize);
}
} else {
//
// alloc
//
cellsize = -1 * cp->Size;
StatAllocCount++;
StatAllocTotal += cellsize;
if (cellsize > StatAllocMax) {
StatAllocMax = cellsize;
}
if (DoTraceAlloc) {
printf("al,x%08lx,%ld\n",
hoff+((PUCHAR)cp - &(buffer[0])), cellsize);
}
ScanCell(cp,cellsize);
}
//
// do basic consistency check
//
#if 0
if (cp->Last != lboff) {
printf("e!,x%08lx bad LAST pointer %08lx\n",
hoff+((PUCHAR)cp - &(buffer[0])), cp->Last);
}
#endif
//
// advance to next cell
//
lboff = boff;
cp = (PHCELL)((PUCHAR)cp + cellsize);
boff += cellsize;
//
// scan ahead in bin, if cp has reached off end of block,
// AND there's bin left to read.
// do this BEFORE breaking out for boff at end.
//
while ( (cp >= guard) && (binread < binsize) ) {
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
fprintf(stderr, "hivestat: '%s' read error @%08lx\n", FileName, hiveposition);
exit(1);
}
cp = (PHCELL)((PUCHAR)cp - HBLOCK_SIZE);
hiveposition += HBLOCK_SIZE;
hoff += HBLOCK_SIZE;
binread += HBLOCK_SIZE;
ASSERT(hoff+HBLOCK_SIZE == hiveposition);
}
if (boff >= binsize) {
break; // we are done with this bin
}
}
}
//
// Traces are done, stats gathered, print summary
//
if (DoSummary) {
printf("\nSummary:\n");
printf("type\tcount/max single/total space\n");
printf("%s\t%7ld\t%7ld\t%7ld\n",
"bin", StatBinCount, StatBinMax, StatBinTotal);
printf("%s\t%7ld\t%7ld\t%7ld\n",
"free", StatFreeCount, StatFreeMax, StatFreeTotal);
printf("%s\t%7ld\t%7ld\t%7ld\n",
"alloc", StatAllocCount, StatAllocMax, StatAllocTotal);
}
if (DoSummary && DoCellType) {
printf("\n");
SizeTotal = SizeKeyData +
SizeValueData +
SizeSDData +
SizeIndexData +
SizeUnknownData;
printf("Total Key Data %7d (%5.2f %%)\n", SizeKeyData,
(float)SizeKeyData*100/SizeTotal);
printf("Total Value Data %7d (%5.2f %%)\n", SizeValueData,
(float)SizeValueData*100/SizeTotal);
printf("Total SD Data %7d (%5.2f %%)\n", SizeSDData,
(float)SizeSDData*100/SizeTotal);
printf("Total Index Data %7d (%5.2f %%)\n", SizeIndexData,
(float)SizeIndexData*100/SizeTotal);
printf("Total Unknown Data %7d (%5.2f %%)\n", SizeUnknownData,
(float)SizeUnknownData*100/SizeTotal);
printf("\n");
printf("Average Key Data %8.2f (%d cells)\n",
(float)SizeKeyData/NumKeyData,
NumKeyData);
printf("Average Value Data %8.2f (%d cells)\n",
(float)SizeValueData/NumValueData,
NumValueData);
printf("Average SD Data %8.2f (%d cells)\n",
(float)SizeSDData/NumSDData,
NumSDData);
printf("Average Index Data %8.2f (%d cells)\n",
(float)SizeIndexData/NumIndexData,
NumIndexData);
printf("Average Unknown Data %8.2f (%d cells)\n",
(float)SizeUnknownData/NumUnknownData,
NumUnknownData);
}
return;
}
VOID
ScanCell(
IN PHCELL Cell,
IN ULONG CellSize
)
/*++
Routine Description:
Given a pointer to an HCELL, this tries to figure out what type
of data is in it (key, value, SD, etc.) and gather interesting
statistics about it.
Arguments:
Cell - Supplies a pointer to the HCELL
CellSize - Supplies the size of the HCELL
Return Value:
None, sets some global statistics depending on content of the cell.
--*/
{
PCELL_DATA Data;
if (!DoCellType) {
return;
}
if (HiveVersion==1) {
Data = (PCELL_DATA)&Cell->u.OldCell.u.UserData;
} else {
Data = (PCELL_DATA)&Cell->u.NewCell.u.UserData;
}
//
// grovel through the data, see if we can figure out what it looks like
//
if ((Data->u.KeyNode.Signature == CM_KEY_NODE_SIGNATURE) &&
(CellSize > sizeof(CM_KEY_NODE))) {
//
// probably a key node
//
ScanKeyNode(&Data->u.KeyNode, CellSize);
} else if ((Data->u.KeyValue.Signature == CM_KEY_VALUE_SIGNATURE) &&
(CellSize > sizeof(CM_KEY_VALUE))) {
//
// probably a key value
//
ScanKeyValue(&Data->u.KeyValue, CellSize);
} else if ((Data->u.KeySecurity.Signature == CM_KEY_SECURITY_SIGNATURE) &&
(CellSize > sizeof(CM_KEY_SECURITY))) {
//
// probably a security descriptor
//
ScanKeySD(&Data->u.KeySecurity, CellSize);
} else if ((Data->u.KeyIndex.Signature == CM_KEY_INDEX_ROOT) ||
(Data->u.KeyIndex.Signature == CM_KEY_INDEX_LEAF)) {
//
// probably a key index
//
ScanKeyIndex(&Data->u.KeyIndex, CellSize);
} else {
//
// Nothing with a signature, could be either
// name
// key list
// value data
//
ScanUnknown(Data, CellSize);
}
}
VOID
ScanKeyNode(
IN PCM_KEY_NODE Node,
IN ULONG CellSize
)
{
int i;
SizeKeyData += CellSize;
NumKeyData++;
if (AccessKeys) {
printf("%d, %d, %d, %d, \"",
Node->SubKeyCounts[Stable],
Node->ValueList.Count,
Node->NameLength,
Node->ClassLength);
for (i=0; i < Node->NameLength/sizeof(WCHAR); i++) {
printf("%c",(CHAR)Node->Name[i]);
}
printf("\"\n");
}
}
VOID
ScanKeyValue(
IN PCM_KEY_VALUE Value,
IN ULONG CellSize
)
{
int i;
int DataLength;
SizeValueData += CellSize;
NumValueData++;
if (AccessValues) {
DataLength = Value->DataLength;
if (DataLength >= CM_KEY_VALUE_SPECIAL_SIZE) {
DataLength -= CM_KEY_VALUE_SPECIAL_SIZE;
}
printf("%d, %d, \"",
DataLength,
Value->NameLength);
for (i=0; i < Value->NameLength/sizeof(WCHAR); i++) {
printf("%c",(CHAR)Value->Name[i]);
}
printf("\"\n");
}
}
VOID
ScanKeySD(
IN PCM_KEY_SECURITY Security,
IN ULONG CellSize
)
{
SizeSDData += CellSize;
NumSDData++;
if (AccessSD) {
printf("%d,%d\n",
Security->ReferenceCount,
Security->DescriptorLength);
}
}
VOID
ScanKeyIndex(
IN PCM_KEY_INDEX Index,
IN ULONG CellSize
)
{
SizeIndexData += CellSize;
NumIndexData++;
}
VOID
ScanUnknown(
IN PCELL_DATA Data,
IN ULONG CellSize
)
{
SizeUnknownData += CellSize;
NumUnknownData++;
}