|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999 - 2000.
//
// File: file.cpp
//
// Contents: NtMarta file functions
//
// History: 4/99 philh Created
//----------------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop
extern "C" { #include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}
#include <windows.h>
#include <kernel.h>
#include <assert.h>
#include <ntstatus.h>
extern "C" { #include <lmcons.h>
#include <lmapibuf.h>
#include <lmdfs.h>
}
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stddef.h>
#include <file.h>
#ifdef STATIC
#undef STATIC
#endif
#define STATIC
//+-------------------------------------------------------------------------
// File Context data structures
//--------------------------------------------------------------------------
typedef struct _FILE_FIND_DATA FILE_FIND_DATA, *PFILE_FIND_DATA;
typedef struct _FILE_CONTEXT { DWORD dwRefCnt; DWORD dwFlags;
// Only closed when FILE_CONTEXT_CLOSE_HANDLE_FLAG is set
HANDLE hFile;
// Following is allocated and updated for FindFirst, FindNext
PFILE_FIND_DATA pFileFindData; } FILE_CONTEXT, *PFILE_CONTEXT;
#define FILE_CONTEXT_CLOSE_HANDLE_FLAG 0x1
typedef struct _QUERY_NAMES_INFO_BUFFER { FILE_NAMES_INFORMATION NamesInfo; WCHAR Names[MAX_PATH]; } QUERY_NAMES_INFO_BUFFER;
struct _FILE_FIND_DATA { HANDLE hDir; BOOL fRestartScan; // TRUE on first Find
QUERY_NAMES_INFO_BUFFER NamesInfoBuffer; };
//+-------------------------------------------------------------------------
// File allocation functions
//--------------------------------------------------------------------------
#define I_MartaFileZeroAlloc(size) \
LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, size) #define I_MartaFileNonzeroAlloc(size) \
LocalAlloc(LMEM_FIXED, size)
STATIC inline VOID I_MartaFileFree( IN LPVOID pv )
/*++
Routine Description:
Free the given memory.
Arguments:
pv - Ponter to memory to be freed.
Return Value:
None.
--*/
{ if (pv) LocalFree(pv); }
STATIC DWORD I_MartaFileGetNtParentString( IN OUT LPWSTR pwszNtParent )
/*++
Routine Description:
Given the name for a file/dir, get the name of its parent. Does not allocate memory. Scans till the first '\' from the right and deletes the name after that.
Arguments:
pwszNtParent - Object name which will be converted to its parent name.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr; DWORD cch; LPWSTR pwsz;
if (NULL == pwszNtParent) return ERROR_INVALID_NAME;
cch = wcslen(pwszNtParent); pwsz = pwszNtParent + cch; if (0 == cch) goto InvalidNameReturn; pwsz--;
//
// Remove any trailing '\'s
//
while (L'\\' == *pwsz) { if (pwsz == pwszNtParent) goto InvalidNameReturn; pwsz--; }
//
// Peal off the last path name component
//
while (L'\\' != *pwsz) { if (pwsz == pwszNtParent) goto InvalidNameReturn; pwsz--; }
//
// Remove all trailing '\'s from the parent.
//
while (L'\\' == *pwsz) { if (pwsz == pwszNtParent) goto InvalidNameReturn; pwsz--; } pwsz++; assert(L'\\' == *pwsz);
//
// Required to distinguish between the device and root directory.
//
pwsz++;
dwErr = ERROR_SUCCESS; CommonReturn: *pwsz = L'\0'; return dwErr; InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto CommonReturn; }
STATIC DWORD I_MartaFileInitContext( OUT PFILE_CONTEXT *ppFileContext )
/*++
Routine Description:
Allocate and initialize memory for the context.
Arguments:
ppFileContext - To return the pointer to the allcoated memory.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr; PFILE_CONTEXT pFileContext;
if (pFileContext = (PFILE_CONTEXT) I_MartaFileZeroAlloc( sizeof(FILE_CONTEXT))) { pFileContext->dwRefCnt = 1; dwErr = ERROR_SUCCESS; } else { pFileContext = NULL; dwErr = ERROR_NOT_ENOUGH_MEMORY; }
*ppFileContext = pFileContext; return dwErr; }
STATIC DWORD I_MartaFileNtOpenFile( IN PUNICODE_STRING pFileName, IN HANDLE hContainingDirectory, // NULL if pFileName is absolute
IN ACCESS_MASK AccessMask, IN OUT PFILE_CONTEXT pFileContext )
/*++
Routine Description:
Open the given file/dir with requested permissions and copy the handle into the supplied context.
Arguments:
pFileName - Name of the file/dir to be opened. hContainingDirectory - Handle to the parent dir. AccessMask - Desired access mask for the open. pFileContext - Handle will be copied into the context structure.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ // cut and paste code from windows\base\advapi\security.c SetFileSecurityW
NTSTATUS Status; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes( &Obja, pFileName, OBJ_CASE_INSENSITIVE, hContainingDirectory, NULL );
//
// Notice that FILE_OPEN_REPARSE_POINT inhibits the reparse behavior. Thus, the
// security will always be set, as before, in the file denoted by the name.
//
Status = NtOpenFile( &pFileContext->hFile, AccessMask, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_REPARSE_POINT );
//
// Back-level file systems may not support the FILE_OPEN_REPARSE_POINT
// flag. We treat this case explicitly.
//
if ( Status == STATUS_INVALID_PARAMETER ) { //
// Open without inhibiting the reparse behavior.
//
Status = NtOpenFile( &pFileContext->hFile, AccessMask, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0 ); }
if (NT_SUCCESS(Status)) { pFileContext->dwFlags |= FILE_CONTEXT_CLOSE_HANDLE_FLAG; return ERROR_SUCCESS; } else return RtlNtStatusToDosError(Status); }
DWORD MartaOpenFileNamedObject( IN LPCWSTR pwszObject, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pContext )
/*++
Routine Description:
Open the given file/dir with desired access mask and return a context handle.
Arguments:
pwszObject - Name of the file/dir which will be opened. AccessMask - Desired access mask with which the file/dir will be opened. pContext - To return a context handle. Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr = ERROR_SUCCESS; PFILE_CONTEXT pFileContext = NULL; UNICODE_STRING FileName; RTL_RELATIVE_NAME RelativeName; PVOID FreeBuffer = NULL;
if (NULL == pwszObject) goto InvalidNameReturn;
if (ERROR_SUCCESS != (dwErr = I_MartaFileInitContext(&pFileContext))) goto ErrorReturn;
//
// Convert the name into NT pathname.
//
if (!RtlDosPathNameToNtPathName_U( pwszObject, &FileName, NULL, &RelativeName )) goto InvalidNameReturn; FreeBuffer = FileName.Buffer;
if (RelativeName.RelativeName.Length ) { FileName = *(PUNICODE_STRING)&RelativeName.RelativeName; } else { RelativeName.ContainingDirectory = NULL; }
//
// Call the helper routine that does the actual open.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileNtOpenFile( &FileName, RelativeName.ContainingDirectory, AccessMask, pFileContext ))) goto ErrorReturn; CommonReturn: if (FreeBuffer) RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer); *pContext = (MARTA_CONTEXT) pFileContext; return dwErr;
ErrorReturn: if (pFileContext) { MartaCloseFileContext((MARTA_CONTEXT) pFileContext); pFileContext = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto ErrorReturn; }
void I_MartaFileFreeFindData( IN PFILE_FIND_DATA pFileFindData )
/*++
Routine Description:
Free up the memory associated with the internal structure.
Arguments:
pFileFindData - Internal file structure to be freed.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ if (NULL == pFileFindData) return; if (pFileFindData->hDir) NtClose(pFileFindData->hDir);
I_MartaFileFree(pFileFindData); }
DWORD MartaCloseFileContext( IN MARTA_CONTEXT Context )
/*++
Routine Description:
Close the context.
Arguments:
Context - Context to be closed.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context;
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) return ERROR_INVALID_PARAMETER;
//
// If the refcnt has gone to zero then free up the memory associated with
// the context handle. Also, close the file handle.
//
if (0 == --pFileContext->dwRefCnt) { if (pFileContext->pFileFindData) I_MartaFileFreeFindData(pFileContext->pFileFindData);
if (pFileContext->dwFlags & FILE_CONTEXT_CLOSE_HANDLE_FLAG) NtClose(pFileContext->hFile);
I_MartaFileFree(pFileContext); }
return ERROR_SUCCESS; }
DWORD MartaAddRefFileContext( IN MARTA_CONTEXT Context )
/*++
Routine Description:
Bump up the ref count for this context.
Arguments:
Context - Context whose ref count should be bumped up.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context;
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) return ERROR_INVALID_PARAMETER;
pFileContext->dwRefCnt++; return ERROR_SUCCESS; }
DWORD MartaOpenFileHandleObject( IN HANDLE Handle, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pContext )
/*++
Routine Description:
Given a file handle, open the context with the desired access mask and return a context handle.
Arguments:
Handle - Existing file handle. AccessMask - Desired access mask for file open. pContext - To return a handle to the context.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr; PFILE_CONTEXT pFileContext = NULL;
//
// Allocate and initialize context.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileInitContext(&pFileContext))) goto ErrorReturn;
//
// Duplicate the handle for desired access mask.
//
if (0 == AccessMask) pFileContext->hFile = Handle; else { if (!DuplicateHandle( GetCurrentProcess(), Handle, GetCurrentProcess(), &pFileContext->hFile, AccessMask, FALSE, // bInheritHandle
0 // fdwOptions
)) { dwErr = GetLastError(); goto ErrorReturn; } pFileContext->dwFlags |= FILE_CONTEXT_CLOSE_HANDLE_FLAG; }
dwErr = ERROR_SUCCESS; CommonReturn: *pContext = (MARTA_CONTEXT) pFileContext; return dwErr;
ErrorReturn: if (pFileContext) { MartaCloseFileContext((MARTA_CONTEXT) pFileContext); pFileContext = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; }
DWORD MartaGetFileParentContext( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pParentContext )
/*++
Routine Description:
Given the context for a file/dir, get the context for its parent.
Arguments:
Context - Context for the file/dir. AccessMask - Desired access mask with which the parent will be opened. pParentContext - To return the context for the parent.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr; LPWSTR pwszNtParentObject = NULL; PFILE_CONTEXT pFileContext = NULL; UNICODE_STRING FileName;
//
// Convert the context into the name of the file/dir.
//
if (ERROR_SUCCESS != (dwErr = MartaConvertFileContextToNtName( Context, &pwszNtParentObject))) goto ErrorReturn;
//
// Get the name of the parent.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileGetNtParentString( pwszNtParentObject))) goto NoParentReturn;
//
// Initialize the context structure.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileInitContext(&pFileContext))) goto ErrorReturn;
RtlInitUnicodeString(&FileName, pwszNtParentObject);
//
// Open the parent dir with the requested permissions.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileNtOpenFile( &FileName, NULL, // hContainingDirectory,
AccessMask, pFileContext ))) goto NoParentReturn; CommonReturn: I_MartaFileFree(pwszNtParentObject); *pParentContext = (MARTA_CONTEXT) pFileContext; return dwErr;
NoParentReturn: dwErr = ERROR_SUCCESS; ErrorReturn: if (pFileContext) { MartaCloseFileContext((MARTA_CONTEXT) pFileContext); pFileContext = NULL; } goto CommonReturn; }
DWORD MartaFindFirstFile( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pChildContext )
/*++
Routine Description:
FInd the first file/dir in the given directory.
Arguments:
Context - Context for the directory. AccessMask - Desired access mask for opening the child file/dir.
pChildContext - To return the context for the first child in the given dir. Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
Note: Does not free up the current context.
--*/
{ DWORD dwErr; NTSTATUS Status; PFILE_CONTEXT pFileParentContext = (PFILE_CONTEXT) Context; PFILE_CONTEXT pFileFirstContext = NULL; PFILE_FIND_DATA pFileFindData; // freed as part of pFileFirstContext
UNICODE_STRING FileName; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock;
//
// Allocate a context for the first child.
//
if (ERROR_SUCCESS != (dwErr = I_MartaFileInitContext(&pFileFirstContext))) goto ErrorReturn; if (NULL == (pFileFindData = (PFILE_FIND_DATA) I_MartaFileZeroAlloc( sizeof(FILE_FIND_DATA)))) goto NotEnoughMemoryReturn; pFileFindData->fRestartScan = TRUE; pFileFirstContext->pFileFindData = pFileFindData;
//
// Duplicate the parent's file handle for synchronized directory access
//
RtlInitUnicodeString(&FileName, NULL); InitializeObjectAttributes( &Obja, &FileName, OBJ_CASE_INSENSITIVE, pFileParentContext->hFile, NULL );
//
// Obtained following parameter values from windows\base\filefind.c
//
Status = NtOpenFile( &pFileFindData->hDir, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT );
//
// Back-level file systems may not support the FILE_OPEN_REPARSE_POINT
// flag. We treat this case explicitly.
//
if ( Status == STATUS_INVALID_PARAMETER ) {
//
// Open without inhibiting the reparse behavior.
//
Status = NtOpenFile( &pFileFindData->hDir, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); }
if (!NT_SUCCESS(Status)) goto StatusErrorReturn;
//
// Following closes / frees pFileFirstContext
//
dwErr = MartaFindNextFile( (MARTA_CONTEXT) pFileFirstContext, AccessMask, pChildContext ); CommonReturn: return dwErr;
StatusErrorReturn: dwErr = RtlNtStatusToDosError(Status); ErrorReturn: if (pFileFirstContext) MartaCloseFileContext((MARTA_CONTEXT) pFileFirstContext); *pChildContext = NULL;
assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn; }
STATIC BOOL I_MartaIsDfsJunctionPoint( IN MARTA_CONTEXT Context );
DWORD MartaFindNextFile( IN MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pSiblingContext )
/*++
Routine Description:
Get the next object in the tree. This is the sibling for the current context.
Arguments:
Context - Context for the current object.
AccessMask - Desired access mask for the opening the sibling. pSiblingContext - To return a handle for the sibling.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
Note:
Closes the current context. --*/
{ DWORD dwErr = ERROR_SUCCESS; NTSTATUS Status;
PFILE_CONTEXT pFilePrevContext = (PFILE_CONTEXT) Context; PFILE_CONTEXT pFileSiblingContext = NULL;
//
// Following don't need to be freed or closed
//
PFILE_FIND_DATA pFileFindData; IO_STATUS_BLOCK IoStatusBlock; PFILE_NAMES_INFORMATION pNamesInfo; HANDLE hDir;
if (ERROR_SUCCESS != (dwErr = I_MartaFileInitContext(&pFileSiblingContext))) goto ErrorReturn;
//
// Move the FindData on to the sibling context
//
pFileFindData = pFilePrevContext->pFileFindData; if (NULL == pFileFindData) goto InvalidParameterReturn; pFilePrevContext->pFileFindData = NULL; pFileSiblingContext->pFileFindData = pFileFindData;
hDir = pFileFindData->hDir; pNamesInfo = &pFileFindData->NamesInfoBuffer.NamesInfo; while (TRUE) { UNICODE_STRING FileName; DWORD cchFileName; LPCWSTR pwszFileName;
//
// Get the name of the sibling object.
//
Status = NtQueryDirectoryFile( hDir, NULL, // HANDLE Event OPTIONAL,
NULL, // PIO_APC_ROUTINE ApcRoutine OPTIONAL,
NULL, // ApcContext OPTIONAL,
&IoStatusBlock, pNamesInfo, sizeof(pFileFindData->NamesInfoBuffer), FileNamesInformation, TRUE, // BOOLEAN ReturnSingleEntry,
NULL, // PUNICODE_STRING FileName OPTIONAL,
pFileFindData->fRestartScan != FALSE ); if (ERROR_SUCCESS != Status) goto StatusErrorReturn;
pFileFindData->fRestartScan = FALSE;
FileName.Length = (USHORT) pNamesInfo->FileNameLength; FileName.MaximumLength = (USHORT) FileName.Length; FileName.Buffer = pNamesInfo->FileName; cchFileName = FileName.Length / sizeof(WCHAR); pwszFileName = FileName.Buffer;
// Skip "." and ".."
if (0 < cchFileName && L'.' == pwszFileName[0] && (1 == cchFileName || (2 == cchFileName && L'.' == pwszFileName[1]))) continue;
//
// For an error still return this context. This allows the caller
// to continue on to the next sibling object and know there was an
// error with this sibling object
//
dwErr = I_MartaFileNtOpenFile( &FileName, hDir, AccessMask, pFileSiblingContext );
//
// Per Praerit, skip DFS junction points.
//
if (ERROR_SUCCESS == dwErr && I_MartaIsDfsJunctionPoint(pFileSiblingContext)) { assert(pFileSiblingContext->dwFlags & FILE_CONTEXT_CLOSE_HANDLE_FLAG); if (pFileSiblingContext->dwFlags & FILE_CONTEXT_CLOSE_HANDLE_FLAG) { NtClose(pFileSiblingContext->hFile); pFileSiblingContext->hFile = NULL; pFileSiblingContext->dwFlags &= ~FILE_CONTEXT_CLOSE_HANDLE_FLAG; } continue; } else break; }
CommonReturn: MartaCloseFileContext(Context); *pSiblingContext = (MARTA_CONTEXT) pFileSiblingContext; return dwErr;
StatusErrorReturn: dwErr = RtlNtStatusToDosError(Status); ErrorReturn: if (pFileSiblingContext) { MartaCloseFileContext((MARTA_CONTEXT) pFileSiblingContext); pFileSiblingContext = NULL; }
//
// If there are no more chidren, return ERROR_SUCCESS with a NULL sibling
// context.
//
if (ERROR_NO_MORE_FILES == dwErr) dwErr = ERROR_SUCCESS; goto CommonReturn;
InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; }
#define WINDFS_DEVICE L"\\Device\\WinDfs"
#define WINDFS_DEVICE_LEN (sizeof(WINDFS_DEVICE) / sizeof(WCHAR) - 1)
#define WINDFS_PREFIX WINDFS_DEVICE L"\\Root"
#define WINDFS_PREFIX_LEN (sizeof(WINDFS_PREFIX) / sizeof(WCHAR) - 1)
#define MAX_QUERY_RETRY_CNT 16
STATIC DWORD I_MartaFileHandleToNtDfsName( IN HANDLE hFile, OUT LPWSTR *ppwszNtObject )
/*++
Routine Description:
Covert the given file handle for a DFS object into name. Allocates memory.
Arguments:
hFile - Handle for the DFS object. ppwszNtObject - To return the name of the DFS object.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
Note:
Couple of problems in the name returned by NtQueryObject for DFS objects: - Name contains 4 extra, bogus bytes (This is a BUG that should be fixed) - For logical drives, returns \Device\WinDfs\X:0\server\share. This needs to be converted to \Device\WinDfs\Root\server\share. Where X is the drive letter. This routine is called when it has already been determined the hFile refers to a DFS object name.
--*/
{ NTSTATUS Status; DWORD dwErr; LPWSTR pwszNtObject = NULL;
IO_STATUS_BLOCK IoStatusBlock; BYTE Buff[MAX_PATH * 4]; PFILE_NAME_INFORMATION pAllocNI = NULL; PFILE_NAME_INFORMATION pNI; // not allocated
LPWSTR pwszFileName; DWORD cchFileName; DWORD cchNtObject; ULONG cbNI; DWORD cRetry;
pNI = (PFILE_NAME_INFORMATION) Buff; cbNI = sizeof(Buff); cRetry = 0; while (TRUE) {
//
// This returns the filename without the Nt Dfs object name prefix.
//
// Assumption: the returned filename always has a leading '\'.
//
Status = NtQueryInformationFile( hFile, &IoStatusBlock, pNI, cbNI, FileNameInformation );
if (ERROR_SUCCESS == Status) break;
if (!(Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_INFO_LENGTH_MISMATCH || Status == STATUS_BUFFER_OVERFLOW)) goto StatusErrorReturn;
if (++cRetry >= MAX_QUERY_RETRY_CNT) goto InvalidNameReturn;
//
// Double buffer length and retry
//
cbNI = cbNI * 2; I_MartaFileFree(pAllocNI); if (NULL == (pAllocNI = (PFILE_NAME_INFORMATION) I_MartaFileNonzeroAlloc(cbNI))) goto NotEnoughMemoryReturn; pNI = pAllocNI; }
//
// Compute the length of the buffer required to hold the name.
//
pwszFileName = pNI->FileName; cchFileName = pNI->FileNameLength / sizeof(WCHAR); if (0 == cchFileName) goto InvalidNameReturn;
cchNtObject = WINDFS_PREFIX_LEN + cchFileName;
//
// Allocate memory.
//
if (NULL == (pwszNtObject = (LPWSTR) I_MartaFileNonzeroAlloc( (cchNtObject + 1) * sizeof(WCHAR)))) goto NotEnoughMemoryReturn;
//
// Copy the prefix and the file name.
//
memcpy(pwszNtObject, WINDFS_PREFIX, WINDFS_PREFIX_LEN * sizeof(WCHAR)); memcpy(pwszNtObject + WINDFS_PREFIX_LEN, pwszFileName, cchFileName * sizeof(WCHAR)); pwszNtObject[cchNtObject] = L'\0';
dwErr = ERROR_SUCCESS;
CommonReturn: I_MartaFileFree(pAllocNI); *ppwszNtObject = pwszNtObject; return dwErr;
StatusErrorReturn: dwErr = RtlNtStatusToDosError(Status); ErrorReturn: assert(NULL == pwszNtObject); assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn;
InvalidNameReturn: dwErr = ERROR_INVALID_NAME; goto ErrorReturn; }
STATIC BOOL I_MartaIsDfsJunctionPoint( IN MARTA_CONTEXT Context )
/*++
Routine Description:
Determine whether this is a DFS junction point.
Arguments:
Context - Context for which the caller want to determine whether this is a dfs junction point. Return Value:
TRUE if this is a DFS junction point. FALSE o/w.
--*/
{ BOOL fDfsJunctionPoint = FALSE; LPWSTR pwszNtObject = NULL; DWORD cchNtObject; LPWSTR pwszDfs; // not allocated
NET_API_STATUS NetStatus; LPBYTE pbNetInfo = NULL;
if (ERROR_SUCCESS != MartaConvertFileContextToNtName( Context, &pwszNtObject)) goto CommonReturn;
//
// Check the prefix.
//
if (0 != _wcsnicmp(pwszNtObject, WINDFS_PREFIX, WINDFS_PREFIX_LEN)) goto CommonReturn;
//
// Convert the NtDfs name to a UNC name
//
pwszDfs = pwszNtObject + WINDFS_PREFIX_LEN - 1; *pwszDfs = L'\\';
//
// Assumption: the following is only successful for DFS junction point
// filename.
//
NetStatus = NetDfsGetInfo( pwszDfs, NULL, // ServerName
NULL, // ShareName
1, &pbNetInfo ); if (0 == NetStatus) { fDfsJunctionPoint = TRUE; }
CommonReturn: if (pwszNtObject) LocalFree(pwszNtObject); if (pbNetInfo) NetApiBufferFree(pbNetInfo);
return fDfsJunctionPoint; }
DWORD MartaConvertFileContextToNtName( IN MARTA_CONTEXT Context, OUT LPWSTR *ppwszNtObject )
/*++
Routine Description:
Returns the NT Object Name for the given context. Allocates memory.
Arguments:
Context - Context for the file/dir.
ppwszNtbject - To return the name of the file/dir.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr = ERROR_SUCCESS; PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context; LPWSTR pwszNtObject = NULL;
BYTE Buff[MAX_PATH * 4]; ULONG cLen = 0; POBJECT_NAME_INFORMATION pNI; // not allocated
POBJECT_NAME_INFORMATION pAllocNI = NULL;
NTSTATUS Status; HANDLE hFile; // not opened
LPWSTR pwszPath; DWORD cchPath;
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) goto InvalidParameterReturn;
hFile = pFileContext->hFile;
//
// First, determine the size of the buffer we need.
//
pNI = (POBJECT_NAME_INFORMATION) Buff;
Status = NtQueryObject(hFile, ObjectNameInformation, pNI, sizeof(Buff), &cLen);
if (!NT_SUCCESS(Status)) { if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_INFO_LENGTH_MISMATCH || Status == STATUS_BUFFER_OVERFLOW) { //
// Allocate a big enough buffer
//
if (NULL == (pAllocNI = (POBJECT_NAME_INFORMATION) I_MartaFileNonzeroAlloc(cLen))) goto NotEnoughMemoryReturn;
pNI = pAllocNI;
Status = NtQueryObject(hFile, ObjectNameInformation, pNI, cLen, NULL); if (!NT_SUCCESS(Status)) goto StatusErrorReturn; } else goto StatusErrorReturn; }
pwszPath = pNI->Name.Buffer; cchPath = pNI->Name.Length / sizeof(WCHAR);
//
// For DFS names, call a helper routine.
//
if (WINDFS_DEVICE_LEN <= cchPath && 0 == _wcsnicmp(pwszPath, WINDFS_DEVICE, WINDFS_DEVICE_LEN)) dwErr = I_MartaFileHandleToNtDfsName(hFile, &pwszNtObject); else {
//
// Allocate and return the name of the object.
//
if (NULL == (pwszNtObject = (LPWSTR) I_MartaFileNonzeroAlloc( (cchPath + 1) * sizeof(WCHAR)))) goto NotEnoughMemoryReturn;
memcpy(pwszNtObject, pwszPath, cchPath * sizeof(WCHAR)); pwszNtObject[cchPath] = L'\0';
dwErr = ERROR_SUCCESS; }
CommonReturn: I_MartaFileFree(pAllocNI); *ppwszNtObject = pwszNtObject; return dwErr;
StatusErrorReturn: dwErr = RtlNtStatusToDosError(Status); ErrorReturn: assert(NULL == pwszNtObject); assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn;
InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; }
DWORD MartaGetFileProperties( IN MARTA_CONTEXT Context, IN OUT PMARTA_OBJECT_PROPERTIES pProperties )
/*++
Routine Description:
Return the properties for file/dir represented by the context.
Arguments:
Context - Context whose properties the caller has asked for. pProperties - To return the properties for this file/dir.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr = ERROR_SUCCESS; NTSTATUS Status; PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context; IO_STATUS_BLOCK IoStatusBlock; FILE_BASIC_INFORMATION BasicFileInfo;
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) goto InvalidParameterReturn;
//
// Query the attributes for the file/dir.
// In case of error, assume that it is a dir.
//
if (!NT_SUCCESS(Status = NtQueryInformationFile( pFileContext->hFile, &IoStatusBlock, &BasicFileInfo, sizeof(BasicFileInfo), FileBasicInformation))) pProperties->dwFlags |= MARTA_OBJECT_IS_CONTAINER; else if (BasicFileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) pProperties->dwFlags |= MARTA_OBJECT_IS_CONTAINER;
dwErr = ERROR_SUCCESS; CommonReturn: return dwErr;
ErrorReturn: assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; }
DWORD MartaGetFileTypeProperties( IN OUT PMARTA_OBJECT_TYPE_PROPERTIES pProperties )
/*++
Routine Description:
Return the properties of file system objects.
Arguments:
pProperties - To return the properties of file system objects.
Return Value:
ERROR_SUCCESS.
--*/
{ const GENERIC_MAPPING GenMap = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
//
// Propagation to be done on the client side.
//
pProperties->dwFlags |= MARTA_OBJECT_TYPE_MANUAL_PROPAGATION_NEEDED_FLAG;
//
// Tree organization of obects is present.
//
pProperties->dwFlags |= MARTA_OBJECT_TYPE_INHERITANCE_MODEL_PRESENT_FLAG;
//
// Return the generic mapping too.
//
pProperties->GenMap = GenMap;
return ERROR_SUCCESS; }
DWORD MartaGetFileRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor )
/*++
Routine Description:
Get the security descriptor for the given handle.
Arguments:
Context - Context for file/dir. SecurityInfo - Type of security information to be read. ppSecurityDescriptor - To return a self-relative security decriptor pointer.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ BOOL fResult; DWORD dwErr = ERROR_SUCCESS; PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context; DWORD cbSize; PISECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) goto InvalidParameterReturn;
//
// First, get the size we need
//
cbSize = 0; if (GetKernelObjectSecurity( pFileContext->hFile, SecurityInfo, NULL, // pSecDesc
0, &cbSize )) goto InternalErrorReturn;
dwErr = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwErr) { if (NULL == (pSecurityDescriptor = (PISECURITY_DESCRIPTOR) I_MartaFileNonzeroAlloc(cbSize))) goto NotEnoughMemoryReturn;
//
// Now get the security descriptor.
//
if (!GetKernelObjectSecurity( pFileContext->hFile, SecurityInfo, pSecurityDescriptor, cbSize, &cbSize )) goto LastErrorReturn; } else goto ErrorReturn;
dwErr = ERROR_SUCCESS; CommonReturn: *ppSecurityDescriptor = pSecurityDescriptor; return dwErr;
LastErrorReturn: dwErr = GetLastError(); ErrorReturn: if (pSecurityDescriptor) { I_MartaFileFree(pSecurityDescriptor); pSecurityDescriptor = NULL; } assert(ERROR_SUCCESS != dwErr); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn;
NotEnoughMemoryReturn: dwErr = ERROR_NOT_ENOUGH_MEMORY; goto ErrorReturn; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto ErrorReturn; InternalErrorReturn: dwErr = ERROR_INTERNAL_ERROR; goto ErrorReturn; }
DWORD MartaSetFileRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, IN PSECURITY_DESCRIPTOR pSecurityDescriptor )
/*++
Routine Description:
Set the given security descriptor on the file/dir represented by the context.
Arguments:
Context - Context for the file/dir.
SecurityInfo - Type of security info to be stamped on the file/dir.
pSecurityDescriptor - Security descriptor to be stamped. Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr; PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context;
//
// Basic validation on the context.
//
if (NULL == pFileContext || 0 == pFileContext->dwRefCnt) goto InvalidParameterReturn;
//
// Set the security on the file/dir.
//
if (!SetKernelObjectSecurity( pFileContext->hFile, SecurityInfo, pSecurityDescriptor )) goto LastErrorReturn;
dwErr = ERROR_SUCCESS; CommonReturn: return dwErr; InvalidParameterReturn: dwErr = ERROR_INVALID_PARAMETER; goto CommonReturn; LastErrorReturn: dwErr = GetLastError(); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_INTERNAL_ERROR; goto CommonReturn; }
ACCESS_MASK MartaGetFileDesiredAccess( IN SECURITY_OPEN_TYPE OpenType, IN BOOL Attribs, IN SECURITY_INFORMATION SecurityInfo )
/*++
Routine Description:
Gets the access required to open object to be able to set or get the specified security info.
Arguments:
OpenType - Flag indicating if the object is to be opened to read or write the security information
Attribs - TRUE indicates that additional access bits should be returned.
SecurityInfo - owner/group/dacl/sacl
Return Value:
Desired access mask with which open should be called.
--*/
{ ACCESS_MASK DesiredAccess = 0;
if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_OWNER; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_OWNER; break; } }
if (SecurityInfo & DACL_SECURITY_INFORMATION) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_DAC; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_DAC; break; } }
if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; }
//
// ONLY FOR FILES.
//
if (Attribs) { DesiredAccess |= FILE_READ_ATTRIBUTES | READ_CONTROL; }
return (DesiredAccess); }
DWORD MartaReopenFileContext( IN OUT MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask )
/*++
Routine Description:
Given the context for a file/dir, close the existing handle if one exists and reopen the context with new permissions.
Arguments:
Context - Context to be reopened. AccessMask - Permissions for the reopen.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ DWORD dwErr = ERROR_SUCCESS;
PFILE_CONTEXT pFileContext = (PFILE_CONTEXT) Context;
//
// Following don't need to be freed or closed
//
PFILE_FIND_DATA pFileFindData; IO_STATUS_BLOCK IoStatusBlock; PFILE_NAMES_INFORMATION pNamesInfo; HANDLE hDir;
UNICODE_STRING FileName;
//
// VishnuP: Bug #384222 (AV since Context == NULL).
// In MartaUpdateTree(), we don't error in case the
// ChildContext is NULL so return here too with success
//
if ( NULL == Context) { return ERROR_SUCCESS; }
//
// Extract the data needed to open the file.
//
pFileFindData = pFileContext->pFileFindData;
hDir = pFileFindData->hDir; pNamesInfo = &pFileFindData->NamesInfoBuffer.NamesInfo;
FileName.Length = (USHORT) pNamesInfo->FileNameLength; FileName.MaximumLength = (USHORT) FileName.Length; FileName.Buffer = pNamesInfo->FileName;
//
// Close the original handle. We do not expect to hit this given the way
// the code is organized now.
//
if (pFileContext->dwFlags & FILE_CONTEXT_CLOSE_HANDLE_FLAG) NtClose(pFileContext->hFile);
//
// Open the file with the access mask desired.
//
dwErr = I_MartaFileNtOpenFile( &FileName, hDir, AccessMask, pFileContext );
//
// In case of a successful open mark the context.
//
if (ERROR_SUCCESS == dwErr) { pFileContext->dwFlags |= FILE_CONTEXT_CLOSE_HANDLE_FLAG; }
return dwErr; }
DWORD MartaReopenFileOrigContext( IN OUT MARTA_CONTEXT Context, IN ACCESS_MASK AccessMask )
/*++
Routine Description:
This is a dummy routine.
Arguments:
Are ignored.
Return Value:
ERROR_SUCCESS
Note:
The context structure must be left untouched.
--*/
{ //
// This is a dummy routine. The real reopen is done by MartaFindFirstFile
// that is called just after this call. The context contains a valid handle
// which was used to set a new DACL on the file/dir.
//
return ERROR_SUCCESS; }
DWORD MartaGetFileNameFromContext( IN MARTA_CONTEXT Context, OUT LPWSTR *pObjectName )
/*++
Routine Description:
Get the name of the file/dir from the context. This routine allocates memory required to hold the name of the object.
Arguments:
Context - Handle to the context. pObjectName - To return the pointer to the allocated string.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ return MartaConvertFileContextToNtName(Context, pObjectName); }
DWORD MartaGetFileParentName( IN LPWSTR ObjectName, OUT LPWSTR *pParentName )
/*++
Routine Description:
Given the name of a file/dir return the name of its parent. The routine allocates memory required to hold the parent name.
Arguments:
ObjectName - Name of the file/dir. pParentName - To return the pointer to the allocated parent name. In case of the root of the tree, we return NULL parent with ERROR_SUCCESS.
Return Value:
ERROR_SUCCESS in case of success. ERROR_* otherwise
--*/
{ ULONG Length = wcslen(ObjectName) + 1; PWCHAR Name = (PWCHAR) I_MartaFileNonzeroAlloc(sizeof(WCHAR) * Length); DWORD dwErr = ERROR_SUCCESS;
*pParentName = NULL;
if (!Name) { return ERROR_NOT_ENOUGH_MEMORY; }
//
// Copy the name of the object into the allocated buffer.
//
wcscpy((WCHAR *) Name, ObjectName);
//
// Convert the object name intp its parent name.
//
dwErr = I_MartaFileGetNtParentString(Name);
if (ERROR_SUCCESS != dwErr) { I_MartaFileFree(Name);
//
// This is the root of the tree which does not have a parent. Return
// ERROR_SUCCESS with ParentName as NULL.
//
if (ERROR_INVALID_NAME == dwErr) return ERROR_SUCCESS;
return dwErr; }
*pParentName = Name;
return dwErr;
}
|