windows-nt/Source/XPSP1/NT/ds/security/ntmarta/newsrc/filctx.cpp
2020-09-26 16:20:57 +08:00

1958 lines
42 KiB
C++

//+---------------------------------------------------------------------------
//
// 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;
}