/*++ Copyright (c) 2000-2000 Microsoft Corporation Module Name: FileIo.c Abstract: This module implements various FileSystem routines used by the PGM Transport Author: Mohammad Shabbir Alam (MAlam) 3-30-2000 Revision History: --*/ #include "precomp.h" //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #endif //******************* Pageable Routine Declarations **************** //---------------------------------------------------------------------------- NTSTATUS BuildPgmDataFileName( IN tSEND_SESSION *pSend ) /*++ Routine Description: This routine build the string for the file name used for buffering data packets. Arguments: IN pSend -- the Send object Return Value: NONE -- since we don't expect any error --*/ { UNICODE_STRING ucPortNumber; WCHAR wcPortNumber[10]; USHORT MaxFileLength; ULONG RandomNumber; PAGED_CODE(); if (pPgmRegistryConfig->Flags & PGM_REGISTRY_SENDER_FILE_SPECIFIED) { MaxFileLength = pPgmRegistryConfig->ucSenderFileLocation.Length / sizeof(WCHAR); } else { MaxFileLength = sizeof (WS_DEFAULT_SENDER_FILE_LOCATION) / sizeof (WCHAR); } // // The file name is composed of the following: // "\\T" + 2DigitRandom# + UptoMAX_USHORTPort# + ".PGM" + "\0" // MaxFileLength += 2 + 2 + 5 + 4 + 1; if (!(pSend->pSender->DataFileName.Buffer = PgmAllocMem ((sizeof (WCHAR) * MaxFileLength), PGM_TAG('2')))) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "BuildPgmDataFileName", "STATUS_INSUFFICIENT_RESOURCES allocating <%d> bytes\n", MaxFileLength); return (STATUS_INSUFFICIENT_RESOURCES); } pSend->pSender->DataFileName.MaximumLength = sizeof (WCHAR) * MaxFileLength; pSend->pSender->DataFileName.Length = 0; // // First, set the root directory // if (pPgmRegistryConfig->Flags & PGM_REGISTRY_SENDER_FILE_SPECIFIED) { RtlAppendUnicodeToString (&pSend->pSender->DataFileName, pPgmRegistryConfig->ucSenderFileLocation.Buffer); } else { RtlAppendUnicodeToString (&pSend->pSender->DataFileName, WS_DEFAULT_SENDER_FILE_LOCATION); } RtlAppendUnicodeToString (&pSend->pSender->DataFileName, L"\\T"); // // Now, Append a random 2 digit value // ucPortNumber.MaximumLength = sizeof (wcPortNumber); ucPortNumber.Buffer = wcPortNumber; RandomNumber = GetRandomInteger (0, 99); if (RandomNumber < 10) { RtlAppendUnicodeToString (&pSend->pSender->DataFileName, L"0"); } RtlIntegerToUnicodeString (RandomNumber, 10, &ucPortNumber); RtlAppendUnicodeStringToString (&pSend->pSender->DataFileName, &ucPortNumber); // // Append the Port# // RtlIntegerToUnicodeString ((ULONG) pSend->TSIPort, 10, &ucPortNumber); RtlAppendUnicodeStringToString (&pSend->pSender->DataFileName, &ucPortNumber); // // Now, add the file name extension for id // RtlAppendUnicodeToString (&pSend->pSender->DataFileName, L".PGM"); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS PgmCreateDataFileAndMapSection( IN tSEND_SESSION *pSend ) /*++ Routine Description: This routine creates the file and creates a section mapping for it. This file is used for buffering the data packets on behalf of the sender Arguments: IN pSend -- the Send object Return Value: NTSTATUS - Final status of the create operation --*/ { OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; LARGE_INTEGER lgMaxDataFileSize; NTSTATUS Status; ULONGLONG Size, BlockSize, PacketsInWindow; ULONG DesiredAccess; ULONG FileAttributes, AllocationAttributes; ULONG ShareAccess; ULONG CreateDisposition; ULONG CreateOptions; ULONG Protection; SIZE_T ViewSize; KAPC_STATE ApcState; BOOLEAN fAttached; PAGED_CODE(); // // Make sure we are currently attached to the Application process // PgmAttachToProcessForVMAccess (pSend, &ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); // // First build the File name string // Status = BuildPgmDataFileName (pSend); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "BuildPgmDataFileName returned <%x>\n", Status); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); return (Status); } // // Compute the size of the Data file required to hold 2 * Window size // Also make it a multiple of the MTU and the FECGroupSize (if applicable) // PacketsInWindow = pSend->pAssociatedAddress->WindowSizeInBytes / pSend->pAssociatedAddress->OutIfMTU; PacketsInWindow += PacketsInWindow + pSend->FECGroupSize - 1; if (PacketsInWindow > SENDER_MAX_WINDOW_SIZE_PACKETS) { PacketsInWindow = SENDER_MAX_WINDOW_SIZE_PACKETS; if (pSend->pAssociatedAddress->WindowSizeInBytes > ((PacketsInWindow >> 1) * pSend->pAssociatedAddress->OutIfMTU)) { pSend->pAssociatedAddress->WindowSizeInBytes = (PacketsInWindow >> 1) * pSend->pAssociatedAddress->OutIfMTU; pSend->pAssociatedAddress->WindowSizeInMSecs = (BITS_PER_BYTE * pSend->pAssociatedAddress->WindowSizeInBytes) / pSend->pAssociatedAddress->RateKbitsPerSec; } } BlockSize = pSend->FECGroupSize * pSend->pSender->PacketBufferSize; Size = PacketsInWindow * pSend->pSender->PacketBufferSize; Size = (Size / BlockSize) * BlockSize; pSend->pSender->MaxDataFileSize = Size; pSend->pSender->MaxPacketsInBuffer = Size / pSend->pSender->PacketBufferSize; lgMaxDataFileSize.QuadPart = Size; PgmZeroMemory (&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES)); InitializeObjectAttributes (&ObjectAttributes, &pSend->pSender->DataFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // // We need to open the data file. This file contains data // and will be mapped into memory. Read and Write access // are requested. // DesiredAccess = FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_GENERIC_READ | FILE_GENERIC_WRITE; // Using the FILE_ATTRIBUTE_TEMPORARY flag: // you let the system know that the file is likely to be short lived. // The temporary file is created as a normal file. The system needs to do // a minimal amount of lazy writes to the file system to keep the disk // structures (directories and so forth) consistent. This gives the // appearance that the file has been written to the disk. However, unless // the Memory Manager detects an inadequate supply of free pages and // starts writing modified pages to the disk, the Cache Manager's Lazy // Writer may never write the data pages of this file to the disk. // If the system has enough memory, the pages may remain in memory for // any arbitrary amount of time. Because temporary files are generally // short lived, there is a good chance the system will never write the pages to the disk. FileAttributes = FILE_ATTRIBUTE_TEMPORARY; ShareAccess = 0; // Gives the caller exclusive access to the open file CreateDisposition = FILE_CREATE; // If the file already exists, fail the request and do not create or // open the given file. If it does not, create the given file. // Delete the file when the last handle to it is is passed to ZwClose. CreateOptions = FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE; PgmZeroMemory (&IoStatusBlock, sizeof(IO_STATUS_BLOCK)); Status = ZwCreateFile (&pSend->pSender->FileHandle, DesiredAccess, &ObjectAttributes, &IoStatusBlock, &lgMaxDataFileSize, // AllocationSize FileAttributes, ShareAccess, CreateDisposition, CreateOptions, NULL, // EaBuffer 0); // EaLength if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "ZwCreateFile for <%wZ> returned <%x>\n", &pSend->pSender->DataFileName, Status); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); pSend->pSender->FileHandle = NULL; return (Status); } // // Now we have a handle to our open test file. We now create a section // object with this handle. // DesiredAccess = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE; Protection = PAGE_READWRITE; AllocationAttributes = SEC_COMMIT; PgmZeroMemory (&ObjectAttributes, sizeof (OBJECT_ATTRIBUTES)); InitializeObjectAttributes (&ObjectAttributes, NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwCreateSection (&pSend->pSender->SectionHandle, DesiredAccess, &ObjectAttributes, // NULL ? &lgMaxDataFileSize, Protection, AllocationAttributes, pSend->pSender->FileHandle); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "ZwCreateSection for <%wZ> returned <%x>\n", &pSend->pSender->DataFileName, Status); ZwClose (pSend->pSender->FileHandle); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); return (Status); } // // Reference the section object, if a view is mapped to the section // object, the object is not dereferenced as the virtual address // descriptor contains a pointer to the section object. // Status = ObReferenceObjectByHandle (pSend->pSender->SectionHandle, 0, 0, KernelMode, &pSend->pSender->pSectionObject, NULL ); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "ObReferenceObjectByHandle for SectionHandle=<%x> returned <%x>\n", pSend->pSender->SectionHandle, Status); ZwClose (pSend->pSender->SectionHandle); ZwClose (pSend->pSender->FileHandle); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); return (Status); } // // Our section object has been created and linked to the file // object that was previous opened. Now we map a view on // this section. // ViewSize = 0; Protection = PAGE_READWRITE; Status = ZwMapViewOfSection (pSend->pSender->SectionHandle, NtCurrentProcess(), &pSend->pSender->SendDataBufferMapping, 0L, // ZeroBits 0L, // CommitSize (initially committed region) NULL, // &SectionOffset &ViewSize, ViewUnmap, // InheritDisposition: for child processes 0L, // AllocationType Protection); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "ZwMapViewOfSection for <%wZ> returned <%x>\n", &pSend->pSender->DataFileName, Status); ObDereferenceObject (pSend->pSender->pSectionObject); pSend->pSender->pSectionObject = NULL; ZwClose (pSend->pSender->SectionHandle); pSend->pSender->SectionHandle = NULL; ZwClose (pSend->pSender->FileHandle); pSend->pSender->FileHandle = NULL; PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); return (Status); } PgmLog (PGM_LOG_INFORM_STATUS, DBG_FILEIO, "PgmCreateDataFileAndMapSection", "Mapped <%wZ> to address<%x>, Filelength=<%d>\n", &pSend->pSender->DataFileName, pSend->pSender->SendDataBufferMapping, Size); pSend->pSender->BufferSizeAvailable = pSend->pSender->MaxDataFileSize; pSend->pSender->LeadingWindowOffset = pSend->pSender->TrailingWindowOffset = 0; // // Now, reference the process // ObReferenceObject (pSend->Process); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CREATE_DATA_FILE); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS PgmUnmapAndCloseDataFile( IN tSEND_SESSION *pSend ) /*++ Routine Description: This routine cleansup the file mapping and closes the file handles. The file should automatically get deleted on closing the handle since we used the FILE_DELETE_ON_CLOSE option while creating the file. Arguments: IN pSend -- the Send object Return Value: NTSTATUS - Final status of the operation (STATUS_SUCCESS) --*/ { NTSTATUS Status; KAPC_STATE ApcState; BOOLEAN fAttached; PgmAttachToProcessForVMAccess (pSend, &ApcState, &fAttached, REF_PROCESS_ATTACH_CLOSE_DATA_FILE); Status = ZwUnmapViewOfSection (NtCurrentProcess(), (PVOID) pSend->pSender->SendDataBufferMapping); ASSERT (NT_SUCCESS (Status)); Status = ObDereferenceObject (pSend->pSender->pSectionObject); ASSERT (NT_SUCCESS (Status)); pSend->pSender->pSectionObject = NULL; Status = ZwClose (pSend->pSender->SectionHandle); ASSERT (NT_SUCCESS (Status)); pSend->pSender->SectionHandle = NULL; Status = ZwClose (pSend->pSender->FileHandle); ASSERT (NT_SUCCESS (Status)); PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_CLOSE_DATA_FILE); ObDereferenceObject (pSend->Process); // Since we had referenced it when the file was created pSend->pSender->SendDataBufferMapping = NULL; pSend->pSender->pSectionObject = NULL; pSend->pSender->SectionHandle = NULL; pSend->pSender->FileHandle = NULL; PgmLog (PGM_LOG_INFORM_STATUS, DBG_SEND, "PgmUnmapAndCloseDataFile", "pSend = <%x>\n", pSend); return (STATUS_SUCCESS); }