/*++ 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 */ 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; ifiifiMac; 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; iediedMac; 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; iediedMac; ied++) { FIRecord = FIRecords; for (ifi=0; ifiifiMac; 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 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