NT4/private/ntos/srv/slmcheck.c
2020-09-30 17:12:29 +02:00

1609 lines
39 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
slmcheck.c
Abstract:
This source file defines a single function that will check the
consistency of a SLM Status file.
Author:
Steve Wood (stevewo) 06-Sep-1991
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#ifdef SLMDBG
BOOLEAN SrvDisallowSlmAccessEnabled = TRUE;
#define toupper(c) ( (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c )
NTSTATUS
SrvpValidateStatusFile(
IN PVOID StatusFileData,
IN ULONG StatusFileLength,
OUT PULONG FileOffsetOfInvalidData
);
VOID
SrvReportCorruptSlmStatus (
IN PUNICODE_STRING StatusFile,
IN NTSTATUS Status,
IN ULONG Offset,
IN ULONG Operation,
IN PSESSION Session
)
{
NTSTATUS status;
ANSI_STRING ansiStatusFile;
TIME time;
TIME_FIELDS timeFields;
status = RtlUnicodeStringToAnsiString( &ansiStatusFile, StatusFile, TRUE );
ASSERT( NT_SUCCESS(status) );
KeQuerySystemTime( &time );
RtlTimeToTimeFields( &time, &timeFields );
//
// Send a broadcast message.
//
SrvSendSecondClassMailslot(
ansiStatusFile.Buffer,
StatusFile->Length + 1,
"NTSLMCORRUPT",
"SlmCheck"
);
SrvPrint4( "\n*** CORRUPT STATUS FILE DETECTED ***\n"
" File: %Z\n"
" Status: 0x%lx, Offset: 0x%lx, detected on %s\n",
&ansiStatusFile, Status, Offset,
Operation == SLMDBG_CLOSE ? "close" : "rename" );
SrvPrint3( " Workstation: %wZ, User: %wZ, OS: %wZ\n",
&Session->Connection->ClientMachineNameString,
&Session->UserName,
&SrvClientTypes[Session->Connection->SmbDialect] );
SrvPrint3( " Current time: %d-%d-%d ",
timeFields.Month, timeFields.Day, timeFields.Year );
SrvPrint3( "%d:%d:%d\n",
timeFields.Hour, timeFields.Minute, timeFields.Second );
RtlFreeAnsiString( &ansiStatusFile );
return;
} // SrvReportCorruptSlmStatus
VOID
SrvReportSlmStatusOperations (
IN PRFCB Rfcb
)
{
ULONG first, last, i;
PRFCB_TRACE trace;
TIME_FIELDS timeFields;
PSZ command;
BOOLEAN oplockBreak;
SrvPrint2( " Number of operations: %d, number of writes: %d\n",
Rfcb->OperationCount, Rfcb->WriteCount );
if ( Rfcb->TraceWrapped || (Rfcb->NextTrace != 0) ) {
first = Rfcb->TraceWrapped ? Rfcb->NextTrace : 0;
last = Rfcb->NextTrace == 0 ? SLMDBG_TRACE_COUNT - 1 :
Rfcb->NextTrace - 1;
i = first;
while ( TRUE ) {
trace = &Rfcb->Trace[i];
RtlTimeToTimeFields( &trace->Time, &timeFields );
SrvPrint2( " %s%d: ", i < 10 ? "0" : "", i );
SrvPrint3( "%d-%d-%d ",
timeFields.Month, timeFields.Day, timeFields.Year );
SrvPrint4( "%s%d:%s%d:",
timeFields.Hour < 10 ? "0" : "", timeFields.Hour,
timeFields.Minute < 10 ? "0" : "", timeFields.Minute );
SrvPrint2( "%s%d: ",
timeFields.Second < 10 ? "0" : "", timeFields.Second );
oplockBreak = FALSE;
switch ( trace->Command ) {
case SMB_COM_READ:
case SMB_COM_WRITE:
case SMB_COM_READ_ANDX:
case SMB_COM_WRITE_ANDX:
case SMB_COM_LOCK_AND_READ:
case SMB_COM_WRITE_AND_UNLOCK:
case SMB_COM_WRITE_AND_CLOSE:
case SMB_COM_READ_RAW:
case SMB_COM_WRITE_RAW:
case SMB_COM_LOCK_BYTE_RANGE:
case SMB_COM_UNLOCK_BYTE_RANGE:
case SMB_COM_LOCKING_ANDX:
switch ( trace->Command ) {
case SMB_COM_READ:
command = "Read";
break;
case SMB_COM_WRITE:
command = "Write";
break;
case SMB_COM_READ_ANDX:
command = "Read And X";
break;
case SMB_COM_WRITE_ANDX:
command = "Write And X";
break;
case SMB_COM_LOCK_AND_READ:
command = "Lock And Read";
break;
case SMB_COM_WRITE_AND_UNLOCK:
command = "Write And Unlock";
break;
case SMB_COM_WRITE_AND_CLOSE:
command = "Write And Close";
break;
case SMB_COM_READ_RAW:
if ( (trace->Flags & 1) == 0 ) {
command = "Read Raw (copy)";
} else {
command = "Read Raw (MDL)";
}
break;
case SMB_COM_WRITE_RAW:
if ( (trace->Flags & 2) == 0 ) {
if ( (trace->Flags & 1) == 0 ) {
command = "Write Raw (copy, no immed)";
} else {
command = "Write Raw (MDL, no immed)";
}
} else {
if ( (trace->Flags & 1) == 0 ) {
command = "Write Raw (copy, immed)";
} else {
command = "Write Raw (MDL, immed)";
}
}
break;
case SMB_COM_LOCK_BYTE_RANGE:
command = "Lock Byte Range";
break;
case SMB_COM_UNLOCK_BYTE_RANGE:
command = "Unlock Byte Range";
break;
case SMB_COM_LOCKING_ANDX:
if ( trace->Flags == 0 ) {
command = "Locking And X (lock)";
} else if ( trace->Flags == 1 ) {
command = "Locking And X (unlock)";
} else {
command = "Locking And X (release oplock)";
oplockBreak = TRUE;
}
break;
}
if ( !oplockBreak ) {
SrvPrint3( "%s, ofs = 0x%lx, len = 0x%lx\n",
command, trace->Data.ReadWrite.Offset,
trace->Data.ReadWrite.Length );
} else {
SrvPrint1( "%s\n", command );
}
break;
default:
SrvPrint2( "command = 0x%lx, flags = 0x%lx\n",
trace->Command, trace->Flags );
}
if ( i == last ) break;
if ( ++i == SLMDBG_TRACE_COUNT ) i = 0;
}
}
return;
} // SrvReportSlmStatusOperations
VOID
SrvCreateMagicSlmName (
IN PUNICODE_STRING StatusFile,
OUT PUNICODE_STRING MagicFile
)
{
LONG fileLength;
ULONG dirLength;
PCHAR magicName = "\\status.nfw";
PCHAR src;
PWCH dest;
fileLength = (strlen( magicName ) + 1) * sizeof(WCHAR);
dirLength = SrvGetSubdirectoryLength( StatusFile );
MagicFile->MaximumLength = (USHORT)(dirLength + fileLength);
MagicFile->Length = (USHORT)(MagicFile->MaximumLength - sizeof(WCHAR));
MagicFile->Buffer = ExAllocatePool( PagedPool, MagicFile->MaximumLength );
ASSERT( MagicFile->Buffer != NULL );
RtlCopyMemory( MagicFile->Buffer, StatusFile->Buffer, dirLength );
src = magicName;
dest = (PWCH)((PCHAR)MagicFile->Buffer + dirLength);
for ( fileLength = strlen(magicName); fileLength >= 0; fileLength-- ) {
*dest++ = *src++;
}
return;
} // SrvCreateMagicSlmName
VOID
SrvDisallowSlmAccess (
IN PUNICODE_STRING StatusFile,
IN HANDLE RootDirectory
)
{
NTSTATUS status;
UNICODE_STRING file;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK iosb;
SrvCreateMagicSlmName( StatusFile, &file );
InitializeObjectAttributes(
&objectAttributes,
&file,
OBJ_CASE_INSENSITIVE,
RootDirectory,
NULL
);
SrvPrint1( "Disallowing access to SLM directory %wZ\n", &file );
status = IoCreateFile(
&fileHandle,
GENERIC_READ,
&objectAttributes,
&iosb,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE,
NULL,
0,
CreateFileTypeNone,
NULL,
0
);
ExFreePool( file.Buffer );
if ( NT_SUCCESS(status) ) {
status = iosb.Status;
}
if ( NT_SUCCESS(status) ) {
NtClose( fileHandle );
} else {
SrvPrint1( "Attempt to disallow SLM access failed: 0x%lx\n", status );
}
return;
} // SrvDisallowSlmAccess
VOID
SrvDisallowSlmAccessA (
IN PANSI_STRING StatusFile,
IN HANDLE RootDirectory
)
{
NTSTATUS status;
UNICODE_STRING unicodeStatusFile;
status = RtlAnsiStringToUnicodeString(
&unicodeStatusFile,
StatusFile,
TRUE
);
ASSERT( NT_SUCCESS(status) );
SrvDisallowSlmAccess( &unicodeStatusFile, RootDirectory );
RtlFreeUnicodeString( &unicodeStatusFile );
return;
} // SrvDisallowSlmAccessA
BOOLEAN
SrvIsSlmAccessDisallowed (
IN PUNICODE_STRING StatusFile,
IN HANDLE RootDirectory
)
{
NTSTATUS status;
UNICODE_STRING file;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK iosb;
if ( !SrvDisallowSlmAccessEnabled ) {
return FALSE;
}
SrvCreateMagicSlmName( StatusFile, &file );
InitializeObjectAttributes(
&objectAttributes,
&file,
OBJ_CASE_INSENSITIVE,
RootDirectory,
NULL
);
status = IoCreateFile(
&fileHandle,
GENERIC_READ,
&objectAttributes,
&iosb,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL,
0,
CreateFileTypeNone,
NULL,
0
);
ExFreePool( file.Buffer );
if ( NT_SUCCESS(status) ) {
status = iosb.Status;
}
if ( NT_SUCCESS(status) ) {
NtClose( fileHandle );
return TRUE;
} else {
return FALSE;
}
} // SrvIsSlmAccessDisallowed
BOOLEAN
SrvIsEtcFile (
IN PUNICODE_STRING FileName
)
{
if ( ((RtlUnicodeStringToAnsiSize( FileName ) - 1) >= 4) &&
(toupper(FileName->Buffer[0]) == 'E') &&
(toupper(FileName->Buffer[1]) == 'T') &&
(toupper(FileName->Buffer[2]) == 'C') &&
( FileName->Buffer[3] == '\\') ) {
return TRUE;
} else {
LONG i;
for ( i = 0;
i < (LONG)RtlUnicodeStringToAnsiSize( FileName ) - 1 - 4;
i++ ) {
if ( ( FileName->Buffer[i] == '\\') &&
(toupper(FileName->Buffer[i+1]) == 'E' ) &&
(toupper(FileName->Buffer[i+2]) == 'T' ) &&
(toupper(FileName->Buffer[i+3]) == 'C' ) &&
( FileName->Buffer[i+4] == '\\') ) {
return TRUE;
}
}
}
return FALSE;
} // SrvIsEtcFile
BOOLEAN
SrvIsSlmStatus (
IN PUNICODE_STRING StatusFile
)
{
UNICODE_STRING baseName;
if ( !SrvIsEtcFile( StatusFile ) ) {
return FALSE;
}
SrvGetBaseFileName( StatusFile, &baseName );
if ( ((RtlUnicodeStringToAnsiSize( &baseName ) - 1) == 10) &&
(toupper(baseName.Buffer[0]) == 'S') &&
(toupper(baseName.Buffer[1]) == 'T') &&
(toupper(baseName.Buffer[2]) == 'A') &&
(toupper(baseName.Buffer[3]) == 'T') &&
(toupper(baseName.Buffer[4]) == 'U') &&
(toupper(baseName.Buffer[5]) == 'S') &&
( baseName.Buffer[6] == '.') &&
(toupper(baseName.Buffer[7]) == 'S') &&
(toupper(baseName.Buffer[8]) == 'L') &&
(toupper(baseName.Buffer[9]) == 'M') ) {
return TRUE;
}
return FALSE;
} // SrvIsSlmStatus
BOOLEAN
SrvIsTempSlmStatus (
IN PUNICODE_STRING StatusFile
)
{
UNICODE_STRING baseName;
if ( !SrvIsEtcFile( StatusFile ) ) {
return FALSE;
}
SrvGetBaseFileName( StatusFile, &baseName );
if ( ((RtlUnicodeStringToAnsiSize( &baseName ) - 1) == 5) &&
(toupper(baseName.Buffer[0]) == 'T') &&
( baseName.Buffer[1] == '0') ) {
return TRUE;
}
return FALSE;
} // SrvIsTempSlmStatus
NTSTATUS
SrvValidateSlmStatus(
IN HANDLE StatusFile,
OUT PULONG FileOffsetOfInvalidData
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_STANDARD_INFORMATION FileInformation;
HANDLE Section;
PVOID ViewBase;
ULONG ViewSize;
*FileOffsetOfInvalidData = -1;
if ( !SrvDisallowSlmAccessEnabled ) {
return STATUS_SUCCESS;
}
Status = NtQueryInformationFile( StatusFile,
&IoStatus,
(PVOID) &FileInformation,
sizeof( FileInformation ),
FileStandardInformation
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = NtCreateSection( &Section,
SECTION_ALL_ACCESS,
NULL,
NULL,
PAGE_READONLY,
SEC_COMMIT,
StatusFile
);
if (!NT_SUCCESS( Status )) {
if (Status == STATUS_MAPPED_FILE_SIZE_ZERO) {
return( STATUS_SUCCESS );
}
return( Status );
}
ViewBase = NULL;
ViewSize = 0;
Status = NtMapViewOfSection( Section,
NtCurrentProcess(),
&ViewBase,
0L,
0L,
NULL,
&ViewSize,
ViewShare,
0L,
PAGE_READONLY
);
NtClose( Section );
if (!NT_SUCCESS( Status )) {
if (Status == STATUS_MAPPED_FILE_SIZE_ZERO) {
return( STATUS_SUCCESS );
}
else {
return( Status );
}
}
Status = SrvpValidateStatusFile( ViewBase,
FileInformation.EndOfFile.LowPart,
FileOffsetOfInvalidData
);
(VOID)NtUnmapViewOfSection( NtCurrentProcess(),
ViewBase
);
return( Status );
}
/*
Status File
The status file is a binary record of the state of just one directory of
a project. Each directory is world unto itself, having all the necessary
information to operate on that directory as well as synchronize with the
master version of the same directory.
The format of the status file is:
sh
rgfi[sh.ifiMac] (at sh.posRgfi)
rged[sh.iedMac] (at sh.posRged)
rgfs[sh.ifiMac] (one for each ed; at rged[ied].posRgfs)
The rgfi and rgfs are in the same order; that is, there is a one to one
relationship between an ifi and an ifs; thus, the information in rgfi[ifi]
pertains to the same file as rged[ied].rgfs[ifi] (you get the picture). The
rgfi is sorted according to the file name (case insensitive for DOS).
When a file is deleted, we simply mark it deleted in the fs and set the
fDeleted bit in the fi. Later we may reuse the fi for a new file, shifting
the fi (and fs) about to make room if neccessary. This means that once an fi
(and the corresponding fs) is added, the contents are always valid!
When a directory is deleted, we actually remove the ed and shift up the
remainder.
A word on fm. Fm fall in exactly three class and transitions are well
defined:
1. IN - fmIn, fmCopyIn, fmAdd, fmGhost; the file is checked in for
the ed and should be read only for the user. In the cases
of fmAdd and fmGhost, there is no local copy of the file.
BI is nil.
2. OUT - fmOut, fmMerge, fmVerify; the file is checked out for the ed
and there may be a BI.
3. DELETED - fmNonExistent, fmDelIn, fmDelOut
the file is either non-existent or on its way to being so.
Locking
SLM currently accesses a status file in three different ways:
"read entire file, write nothing"
- commands like status which merely display information.
"read entire file, write a single ed's information"
- concurrent ssync; multiple executing SLMs can concurrently modify
their ed's fields.
"read entire file, write entire file"
- commands like in which may modify any other ed's information.
These following locking levels correspond to these types of access:
lckNil - no locking in effect.
lckEd - locking on a per-ed basis.
lckAll - entire file locked.
This locking variable is stored in the sh.lck field of the status file header.
These fields are also used for locking:
sh.fAdminLock - locked by the project administrator.
sh.nmLocker - (for lckAll or fAdminLock) the nm of the
user who has the status file locked.
rged[].fLocked - (for lckEd access) fTrue if this ed is locked.
When a user loads the status file with FLoadStatus, he specifies the type of
locking to apply to the file. FLoadStatus compares the current locking level
to the desired level and determines whether or not to grant the lock:
Current Desired lock:
sh.lck: lckNil lckEd lckAll
lckNil OK,- OK,lckEd OK,lckAll
lckEd OK,- OK,- (if this ED is DENIED,-
not already locked)
lckAll OK,- DENIED,- DENIED,-
(pairs are (OK/DENIED, new lck), '-' means no change.)
In addition, a lock will not be granted if fAdminLock unless the nmInvoker
matches the sh.nmLocker.
When FlushStatus is run, it takes different actions depending upon the
current in-memory sh.lck:
lckNil none.
lckEd write [ied,rgfs] record to a temporary file with mode
mmInstall1Ed.
lckAll set sh.lck to lckNil and write the entire status file with
mode mmInstall.
It is only when the script is run that the status file locking actually
changes. The possible script actions are
mmInstall1Ed Open, lockfile status file. Read sh and rged. Read ied and
rgfs from temporary file. Write rgfs. Clear rged[ied].fLocked.
Write rged[ied]. If no rged[].fLocked, clear sh.lck and write
sh. Unlock and close status file. Delete temporary file.
mmInstall Rename old status file to status.bak, rename new file to
status.slm.
Finally, the following actions are taken if AbortStatus is called:
lckNil none.
lckEd Open, lockfile status file. Read sh and rged. Clear
rged[ied].fLocked. Write rged[ied]. If no rged[].fLocked,
clear sh.lck and write sh. Unlock and close status file.
lckAll Open and lockfile status file, read sh, sh.lck to lckNil, clear
nmLocker (if !sh.fAdminLock), write sh, unlock and close file.
status file.
*/
typedef short LCK;
#define lckNil (LCK)0
#define lckEd (LCK)1
#define lckAll (LCK)2
#define lckMax (LCK)3
/* Load status flags */
typedef unsigned short LS;
#define flsNone 0 /* Do nothing special. */
#define flsJustFi (1<<0) /* Just load SH and FI. */
#define flsJustEd (1<<1) /* Just load SH and ED. */
#define flsExtraEd (1<<2) /* Make space for an extra ED. */
#define FlsFromCfiAdd(cfi) ((cfi) << 3) /* Make space for cfiAdd extra FI. */
#define CfiAddOfLs(ls) ((ls) >> 3) /* Extra cfiAdd from LS. */
/* max 2^15-1 enlisted directories */
typedef unsigned short IED;
typedef unsigned short IED2; /* for SLMCK version 2 status file */
#define iedMax ((IED)32767)
#define iedNil ((IED)-1)
typedef unsigned short IFS;
typedef unsigned short IFS2; /* for SLMCK version 2 status file */
typedef unsigned short IFI;
typedef unsigned short IFI2; /* for SLMCK version 2 status file */
#define ifiNil ((IFI)-1)
/* File Mode */
typedef unsigned short FM;
#define fmMin (FM)0
#define fmNonExistent (FM)0 /* fs unused */
#define fmIn (FM)1 /* file checked in */
#define fmOut (FM)2 /* file checked out */
#define fmAdd (FM)3 /* file to be added -> fmIn */
#define fmDelIn (FM)4 /* to be deleted (was in) -> fmNonExistent */
#define fmDelOut (FM)5 /* to be deleted (was out) -> fmNonExistent */
#define fmCopyIn (FM)6 /* new copy of file needed -> fmIn */
#define fmMerge (FM)7 /* merge with src directory -> fmOut */
#define fmVerify (FM)10 /* was merged; need verification -> fmOut */
#define fmConflict (FM)11 /* merged, conflicted; needs repair -> fmOut */
#define fmGhost (FM)12 /* ghosted, not copied locally */
#define fmMax 13
#define fmNil fmMax
#define FValidFm(fm) ( (fm) <= fmMerge ||\
((fm) >= fmVerify && (fm) < fmMax))
/* BI - Base Id: unique number for producing the name of baseline files.
Base files are named:
$sroot/etc/$proj/$subdir/B<unique>
*/
typedef unsigned short BI;
#define biNil 4095
#define biMin 0
/* File kinds */
typedef unsigned short FK; /* file kind */
#define fkNil (FK)0 /* bad fk */
#define fkMin (FK)1
#define fkDir (FK)1 /* directory */
#define fkText (FK)2 /* ordinary text file */
#define fkAscii (FK)3 /* NYI future enhancement */
#define fkWord (FK)4 /* NYI future enhancement */
#define fkBinary (FK)5 /* backed up binary file */
#define fkUnrec (FK)6 /* not backed up file */
#define fkVersion (FK)7 /* automatically updated version.h */
#define fkObject (FK)8 /* NYI future enhancement */
#define fkUserMin (FK)16 /* NYI future enhancement */
#define fkMax (FK)26
#if (X386 || OS2)
# pragma pack(2)
#endif
typedef unsigned short BIT;
typedef unsigned short BITS;
typedef char NM;
#define cchFileMax 14
#define cchUserMax 14
#define cchProjMax 14
#define cchMachMax 16
#define cchDirMax 8
#define cchDosName 8 /* first component of DOS filename */
#define cchDosExt 3 /* extension of DOS filename */
#define cchVolMax 128 // Maximum length of volume name
typedef char PTH;
#define cchPthMax 96
#define cchPvNameMax 32
#if (X386 || OS2)
# pragma pack(2)
#endif
/* Version types */
#define MAGIC (short)0x9001
#define VERSION 2 /* status file version */
typedef struct
{
short rmj; /* major */
short rmm; /* minor */
short rup; /* revision (these names were used by RAID...) */
char szName[cchPvNameMax+1]; /* name */
} PV; /* project version */
#if (X386 || OS2)
# pragma pack()
#endif
typedef short FV; /* file version */
#define fvInit (FV)0
#define fvLim (FV)32767
/* Status Header */
typedef struct
{
short magic;
short version;
IFI ifiMac; /* same as ifsMac */
IED iedMac; /* # of enlisted directories */
PV pv; /* project version */
BIT fRelease:1; /* true if project just released? */
BIT fAdminLock:1; /* locked by sadmin lock */
BITS rgfSpare:14; /* remaining bits in word */
#ifdef X386
BITS lck:16;
#else
LCK lck; /* current locking level */
#endif
NM nmLocker[cchUserMax];/* name of locker */
short wSpare; /* was diNext */
BI biNext; /* next bi for base files */
PTH pthSSubDir[cchPthMax]; /* system project subdirectory */
short rgwSpare[4]; /* spare */
} SH;
#if (X386 || OS2)
# pragma pack()
#endif
/* fTrue if the status header lock information is consistent. */
#define FShLockInvariants(psh) \
((psh)->lck >= lckNil && (psh)->lck < lckMax && \
(!FEmptyNm((psh)->nmLocker) == (psh->lck == lckAll || psh->fAdminLock)))
#if (X386 || OS2)
# pragma pack(2)
#endif
/* File Information; sorted by name */
typedef struct
{
NM nmFile[cchFileMax]; /* name of file */
FV fv; /* file version */
BITS fk:5; /* file kind */
BIT fDeleted:1; /* true if the file is not used. */
BIT fMarked:1; /* marked for processing (only in memory) */
BITS rgfSpare:9; /* spare */
BITS wSpare:16;
} FI;
#if (X386 || OS2)
# pragma pack()
#endif
#if (X386 || OS2)
# pragma pack(2)
#endif
/* Enlisted Directory */
typedef struct
{
PTH pthEd[cchPthMax]; /* path to user's root directory */
NM nmOwner[cchUserMax]; /* user who enlisted the directory */
BIT fLocked:1; /* ed is locked */
BIT fNewVer:1;
BITS rgfSpare:14;
BITS wSpare:16;
} ED;
#if (X386 || OS2)
# pragma pack()
#endif
#if (X386 || OS2)
# pragma pack(2)
#endif
/* File Status; rgfs same size and order as rgfi */
typedef struct
{
BITS fm:4; /* in, out, etc. */
BITS bi:12; /* saved base file or biNil */
#ifdef X386
BITS fv:16; /* version last synced to */
#else
FV fv;
#endif
} FS;
#if (X386 || OS2)
# pragma pack()
#endif
/* Positions of various structures in the status file. */
#define PosSh() (POS)0
#define PosRgfi(psh) ((long)sizeof(SH))
#define PosRged(psh) (PosRgfi(psh) + (long)(psh)->ifiMac * sizeof(FI))
#define PosEd(psh,ied) (PosRged(psh) + (long)(ied) * sizeof(ED))
#define PosRgrgfs(psh) (PosRged(psh) + (long)(psh)->iedMac * sizeof(ED))
#define PosRgfsIed(psh,ied) \
(PosRgrgfs(psh) + (long)(ied) * (psh)->ifiMac * sizeof(FS))
typedef SH *PSH;
typedef ED *PED;
typedef FI *PFI;
typedef FS *PFS;
NTSTATUS
CheckSH(
IN PSH SHRecord
);
NTSTATUS
CheckFI(
IN PFI FIRecord,
IN IFI ifi
);
NTSTATUS
CheckED(
IN PED EDRecord,
IN IED ied
);
NTSTATUS
CheckFS(
IN PSH SHRecord,
IN PFI FIRecord,
IN PFS FSRecord,
IN IED ied,
IN IFI ifi
);
int
ValidatePath(
IN IED ied,
IN PED EDRecord,
IN char *Path
);
BOOLEAN
GetPathToken(
OUT char *Token,
IN int MaxLength,
IN PCHAR Path
);
int
ValidateName(
IN IED ied,
IN char *Name,
IN int MaxLength,
IN BOOLEAN FileName
);
NTSTATUS
SrvpValidateStatusFile(
IN PVOID StatusFileData,
IN ULONG StatusFileLength,
OUT PULONG FileOffsetOfInvalidData
)
{
NTSTATUS Status;
IFI ifi;
IED ied;
PSH SHRecord;
PFI FIRecords;
PED EDRecords;
PFS FSRecords;
PFI FIRecord;
PED EDRecord;
PFS FSRecord;
PCHAR EndOfData;
//
// Validate the Status File header first
//
SHRecord = (PSH)StatusFileData;
Status = CheckSH( SHRecord );
if (!NT_SUCCESS( Status )) {
*FileOffsetOfInvalidData = 0;
return( Status );
}
//
// Compute pointers to remaining structures in status file
//
FIRecords = (PFI)(SHRecord + 1);
EDRecords = (PED)((PCHAR)FIRecords + (sizeof( FI ) *
SHRecord->ifiMac
)
);
FSRecords = (PFS)((PCHAR)EDRecords + (sizeof( ED ) *
SHRecord->iedMac
)
);
//
// Calculate the end of file and compare it against the actual file
// length. Error if different.
//
EndOfData = (PCHAR)FSRecords + (sizeof( FS ) *
SHRecord->ifiMac *
SHRecord->iedMac
);
if ((ULONG)(EndOfData - (PCHAR)StatusFileData) != StatusFileLength) {
*FileOffsetOfInvalidData = 0;
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x4000) );
}
//
// Assume success for empty status files.
//
Status = STATUS_SUCCESS;
//
// Validate each file information records.
//
FIRecord = FIRecords;
for (ifi=0; ifi<SHRecord->ifiMac; ifi++) {
Status = CheckFI( FIRecord, ifi );
if (!NT_SUCCESS( Status )) {
*FileOffsetOfInvalidData = ((PCHAR)FIRecord - (PCHAR)StatusFileData);
return( Status );
}
FIRecord++;
}
//
// Validate each enlisted directory record.
//
EDRecord = EDRecords;
FSRecord = FSRecords;
for (ied=0; ied<SHRecord->iedMac; ied++) {
Status = CheckED( EDRecord, ied );
if (!NT_SUCCESS( Status )) {
*FileOffsetOfInvalidData = ((PCHAR)EDRecord - (PCHAR)StatusFileData);
return( Status );
}
EDRecord++;
}
//
// Validate set of file status records associated with each enlisted
// directory record.
//
FSRecord = FSRecords;
for (ied=0; ied<SHRecord->iedMac; ied++) {
FIRecord = FIRecords;
for (ifi=0; ifi<SHRecord->ifiMac; ifi++) {
Status = CheckFS( SHRecord,
FIRecord,
FSRecord,
ied,
ifi
);
if (!NT_SUCCESS( Status )) {
*FileOffsetOfInvalidData = ((PCHAR)FSRecord - (PCHAR)StatusFileData);
return( Status );
}
FSRecord++;
FIRecord++;
}
}
return( STATUS_SUCCESS );
}
NTSTATUS
CheckSH(
IN PSH SHRecord
)
{
if (SHRecord->magic != MAGIC) {
#if DBG
SrvPrint2( "CheckSH: Magic Value is incorrect (%0x != %0x)\n",
SHRecord->magic,
MAGIC
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x4001) );
}
if (SHRecord->version != VERSION) {
#if DBG
SrvPrint2( "CheckSH: Version Value is incorrect (%0x != %0x)\n",
SHRecord->version,
VERSION
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x4002) );
}
return( STATUS_SUCCESS );
}
NTSTATUS
CheckFI(
IN PFI FIRecord,
IN IFI ifi
)
{
int i;
i = ValidateName( (IED)ifi,
FIRecord->nmFile,
cchFileMax,
TRUE
);
if (i != -1) {
#if DBG
SrvPrint1( "CheckFI( %3d ): ", ifi );
SrvPrint4( "File name invalid in %d char (%02x) - %.*Fs\n",
i,
FIRecord->nmFile[ i ],
cchFileMax,
FIRecord->nmFile
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x5000 + ifi) );
}
return( STATUS_SUCCESS );
}
NTSTATUS
CheckED(
IN PED EDRecord,
IN IED ied
)
{
int i;
i = ValidatePath( ied, EDRecord, EDRecord->pthEd );
if (i != -1) {
#if DBG
SrvPrint1( "CheckED( %d ): ", ied );
SrvPrint4( "%s %s (User Path invalid in %d char (%02x))\n",
EDRecord->nmOwner,
EDRecord->pthEd,
i,
EDRecord->pthEd[ i ]
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x6000 + ied) );
}
i = ValidateName( ied,
EDRecord->nmOwner,
cchUserMax,
FALSE
);
if (i != -1) {
#if DBG
SrvPrint1( "CheckED( %d ): ", ied );
SrvPrint4( "%s %s (Owner invalid in %d char (%02x))\n",
EDRecord->nmOwner,
EDRecord->pthEd,
i,
EDRecord->nmOwner[ i ]
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x7000 + ied) );
}
return( STATUS_SUCCESS );
}
#define MODE_VALID 0x1
#define MODE_BASEVALID 0x2
unsigned char ValidModes[] = {
MODE_VALID, // fs unused
MODE_VALID, // file checked in
MODE_VALID | MODE_BASEVALID, // file checked out
MODE_VALID, // file to be added
MODE_VALID, // to be deleted (was in)
MODE_VALID | MODE_BASEVALID, // to be deleted (was out)
MODE_VALID, // new copy of file needed
MODE_VALID | MODE_BASEVALID, // merge with src directory
0,
0,
MODE_VALID | MODE_BASEVALID, // was merged; need verification
MODE_VALID | MODE_BASEVALID, // merged, conflicted; needs repair
MODE_VALID // ghosted, not copied locally
};
NTSTATUS
CheckFS(
IN PSH SHRecord,
IN PFI FIRecord,
IN PFS FSRecord,
IN IED ied,
IN IFI ifi
)
{
BOOLEAN Result = TRUE;
FS NewFSRecord;
SHRecord, FIRecord;
NewFSRecord = *FSRecord;
#if 0
if (FSRecord->fv < fvInit || FSRecord->fv > FIRecord->fv) {
#if DBG
SrvPrint3( "CheckFS( %d, %d ): Invalid file version (%d)\n",
ied, ifi, FSRecord->fv
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x8001) );
}
#endif
if (FSRecord->fm > fmMax ||
!(ValidModes[ FSRecord->fm ] & MODE_VALID)
) {
#if DBG
SrvPrint3( "CheckFS( %d, %d ): Invalid file mode (%d)\n",
ied, ifi, FSRecord->fm
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x8002) );
}
#if 0
if (FSRecord->bi != biNil &&
(FSRecord->bi > SHRecord->biNext ||
!(ValidModes[ FSRecord->fm ] & MODE_BASEVALID)
)
) {
#if DBG
SrvPrint4( "CheckFS( %d, %d ): Invalid file base index (%d) biNext == %d\n",
ied, ifi, FSRecord->bi, SHRecord->biNext
);
#endif
return( (NTSTATUS)((ULONG)STATUS_UNSUCCESSFUL + 0x8003) );
}
#endif
return( STATUS_SUCCESS );
}
int
ValidatePath(
IN IED ied,
IN PED EDRecord,
IN char *Path
)
{
char *s = Path;
UCHAR ComponentName[ 32 ];
int i;
int MaxLength = cchPthMax;
EDRecord;
if (*s++ != '/') {
return( 0 );
}
if (*s++ != '/') {
return( 1 );
}
if (((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')) && s[ 1 ] == ':') {
s += 2;
if (GetPathToken( ComponentName, cchVolMax, s )) {
i = ValidateName( ied, ComponentName, cchVolMax, FALSE );
if (i != -1) {
return( i + (s - Path) );
}
}
else {
return( s - Path );
}
}
else {
if (GetPathToken( ComponentName, cchMachMax, s )) {
i = ValidateName( ied, ComponentName, cchMachMax, FALSE );
if (i != -1) {
return( i + (s - Path) );
}
}
else {
return( s - Path );
}
}
while (TRUE) {
s += strlen( ComponentName );
if (*s == '/') {
s++;
}
else {
break;
}
if (!GetPathToken( ComponentName, cchFileMax, s )) {
return( s - Path );
}
i = ValidateName( ied, ComponentName, cchFileMax, TRUE );
if (i != -1) {
return( i + (s - Path) );
}
}
i = s - Path;
if (i > MaxLength) {
return( MaxLength );
}
return( -1 );
}
BOOLEAN
GetPathToken(
OUT char *Token,
IN int MaxLength,
IN PCHAR Path
)
{
int Length;
char c, *dst;
dst = Token;
Length = 0;
while (c = *Path++) {
if (c == '/') {
break;
}
if (Length++ >= MaxLength) {
*dst = '\0';
return( FALSE );
}
else {
*dst++ = c;
}
}
*dst = '\0';
while (Length++ < MaxLength) {
*dst++ = '\0';
}
return( TRUE );
}
unsigned char ValidChars[ 128 ] = {
0, /* 00 (NUL) */
0, /* 01 (SOH) */
0, /* 02 (STX) */
0, /* 03 (ETX) */
0, /* 04 (EOT) */
0, /* 05 (ENQ) */
0, /* 06 (ACK) */
0, /* 07 (BEL) */
0, /* 08 (BS) */
0, /* 09 (HT) */
0, /* 0A (LF) */
0, /* 0B (VT) */
0, /* 0C (FF) */
0, /* 0D (CR) */
0, /* 0E (SI) */
0, /* 0F (SO) */
0, /* 10 (DLE) */
0, /* 11 (DC1) */
0, /* 12 (DC2) */
0, /* 13 (DC3) */
0, /* 14 (DC4) */
0, /* 15 (NAK) */
0, /* 16 (SYN) */
0, /* 17 (ETB) */
0, /* 18 (CAN) */
0, /* 19 (EM) */
0, /* 1A (SUB) */
0, /* 1B (ESC) */
0, /* 1C (FS) */
0, /* 1D (GS) */
0, /* 1E (RS) */
0, /* 1F (US) */
0, /* 20 SPACE */
3, /* 21 ! */
1, /* 22 " */
3, /* 23 # */
3, /* 24 $ */
3, /* 25 % */
3, /* 26 & */
3, /* 27 ' */
3, /* 28 ( */
3, /* 29 ) */
3, /* 2A * */
3, /* 2B + */
3, /* 2C , */
3, /* 2D - */
3, /* 2E . */
1, /* 2F / */
3, /* 30 0 */
3, /* 31 1 */
3, /* 32 2 */
3, /* 33 3 */
3, /* 34 4 */
3, /* 35 5 */
3, /* 36 6 */
3, /* 37 7 */
3, /* 38 8 */
3, /* 39 9 */
1, /* 3A : */
1, /* 3B ; */
1, /* 3C < */
3, /* 3D = */
1, /* 3E > */
3, /* 3F ? */
3, /* 40 @ */
3, /* 41 A */
3, /* 42 B */
3, /* 43 C */
3, /* 44 D */
3, /* 45 E */
3, /* 46 F */
3, /* 47 G */
3, /* 48 H */
3, /* 49 I */
3, /* 4A J */
3, /* 4B K */
3, /* 4C L */
3, /* 4D M */
3, /* 4E N */
3, /* 4F O */
3, /* 50 P */
3, /* 51 Q */
3, /* 52 R */
3, /* 53 S */
3, /* 54 T */
3, /* 55 U */
3, /* 56 V */
3, /* 57 W */
3, /* 58 X */
3, /* 59 Y */
3, /* 5A Z */
1, /* 5B [ */
1, /* 5C \ */
1, /* 5D ] */
3, /* 5E ^ */
3, /* 5F _ */
3, /* 60 ` */
3, /* 61 a */
3, /* 62 b */
3, /* 63 c */
3, /* 64 d */
3, /* 65 e */
3, /* 66 f */
3, /* 67 g */
3, /* 68 h */
3, /* 69 i */
3, /* 6A j */
3, /* 6B k */
3, /* 6C l */
3, /* 6D m */
3, /* 6E n */
3, /* 6F o */
3, /* 70 p */
3, /* 71 q */
3, /* 72 r */
3, /* 73 s */
3, /* 74 t */
3, /* 75 u */
3, /* 76 v */
3, /* 77 w */
3, /* 78 x */
3, /* 79 y */
3, /* 7A z */
3, /* 7B { */
1, /* 7C | */
3, /* 7D } */
3, /* 7E ~ */
0, /* 7F (DEL) */
};
int
ValidateName(
IN IED ied,
IN char *Name,
IN int MaxLength,
IN BOOLEAN FileName
)
{
int i, Length, PeriodIndex;
unsigned char c;
unsigned char *s;
ied;
Length = -1;
PeriodIndex = -1;
s = (unsigned char *)Name;
for (i=0; i<MaxLength; i++) {
c = *s++;
if (Length == -1) {
if (!c) {
if (!i) {
return( 0 );
}
Length = i;
}
else
if (c > 0x7F) {
return( i );
}
else
if (FileName) {
if (!(ValidChars[ c ] & 0x2)) {
return( i );
}
else
if (c == '.') {
if (PeriodIndex != -1 || i > 8) {
return( i );
}
PeriodIndex = i;
}
else
if (PeriodIndex != -1) {
if (i > (PeriodIndex + 3)) {
return( i );
}
}
}
else
if (!(ValidChars[ c ] & 0x1)) {
return( i );
}
}
else
if (c) {
return( i );
}
}
return( -1 );
}
#endif