Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1156 lines
38 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
rename.c
Abstract:
This module implements the user mode DAV miniredir routine(s) pertaining to
the ReName call.
Author:
Rohan Kumar [RohanK] 20-Jan-2000
Revision History:
--*/
#include "pch.h"
#pragma hdrstop
#include "ntumrefl.h"
#include "usrmddav.h"
#include "global.h"
#include "UniUtf.h"
#include "nodefac.h"
VOID
DavAsyncSetFileInformationCompletion(
PDAV_USERMODE_WORKITEM DavWorkItem
);
ULONG
DavFsReName(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles ReName requests for the DAV Mini-Redir that get
reflected from the kernel.
Arguments:
DavWorkItem - The buffer that contains the request parameters and options.
Return Value:
The return status for the operation
--*/
{
ULONG WStatus = ERROR_SUCCESS;
PDAV_USERMODE_RENAME_REQUEST DavReNameRequest = NULL;
PWCHAR ServerName = NULL, OldPathName = NULL, NewPathName = NULL;
PWCHAR UtfServerName = NULL, UtfNewPathName = NULL;
ULONG UtfServerNameLength = 0, UtfNewPathNameLength = 0;
PWCHAR UrlBuffer = NULL, HeaderBuff = NULL, CanName = NULL;
ULONG HeaderLength = 0, HeaderLengthInBytes = 0;
ULONG_PTR CallBackContext = (ULONG_PTR)0;
BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE;
ULONG ServerID = 0, urlLength = 0, TagLen = 0, convLen = 0;
PPER_USER_ENTRY PerUserEntry = NULL;
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
URL_COMPONENTSW UrlComponents;
BOOL didImpersonate = FALSE, BStatus = FALSE;
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
//
// Get the request buffer pointer from the DavWorkItem.
//
DavReNameRequest = &(DavWorkItem->ReNameRequest);
//
// The first character is a '\' which has to be stripped from the
// ServerName.
//
ServerName = &(DavReNameRequest->ServerName[1]);
if ( !ServerName && ServerName[0] != L'\0' ) {
DavPrint((DEBUG_ERRORS, "DavFsReName: ServerName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsReName: ServerName = %ws.\n", ServerName));
ServerID = DavReNameRequest->ServerID;
DavPrint((DEBUG_MISC, "DavFsReName: ServerID = %d.\n", ServerID));
//
// The first character is a '\' which has to be stripped from the
// OldPathName.
//
OldPathName = &(DavReNameRequest->OldPathName[1]);
if ( !OldPathName ) {
DavPrint((DEBUG_ERRORS, "DavFsReName: OldPathName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// The file name can contain \ characters. Replace them by / characters.
//
CanName = OldPathName;
while (*CanName) {
if (*CanName == L'\\') {
*CanName = L'/';
}
CanName++;
}
DavPrint((DEBUG_MISC, "DavFsReName: OldPathName = %ws.\n", OldPathName));
//
// The first character is a '\' which has to be stripped from the
// NewPathName.
//
NewPathName = &(DavReNameRequest->NewPathName[1]);
if ( !NewPathName ) {
DavPrint((DEBUG_ERRORS, "DavFsReName: NewPathName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// The file name can contain \ characters. Replace them by / characters.
//
CanName = NewPathName;
while (*CanName) {
if (*CanName == L'\\') {
*CanName = L'/';
}
CanName++;
}
DavPrint((DEBUG_MISC, "DavFsReName: NewPathName = %ws.\n", NewPathName));
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
//
// If we are using WinInet synchronously, then we need to impersonate the
// clients context now.
//
#ifndef DAV_USE_WININET_ASYNCHRONOUSLY
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsReName/UMReflectorImpersonate. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
#endif
//
// If we have a dummy share name in the OldPathName and the NewPathName, we
// need to remove it right now before we contact the server.
//
DavRemoveDummyShareFromFileName(OldPathName);
DavRemoveDummyShareFromFileName(NewPathName);
//
// We need to convert the ServerName and the NewPathName into the UTF-8
// format before we call into the InternetCreateUrlW function. This is
// because if the localized Unicode characters are passed into this
// function it converts them into ?. For example all the chinese unicode
// characters will be converted into ?s.
//
UtfServerNameLength = WideStrToUtfUrlStr(ServerName, (wcslen(ServerName) + 1), NULL, 0);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/WideStrToUtfUrlStr(1). Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
UtfServerName = LocalAlloc(LPTR, UtfServerNameLength * sizeof(WCHAR));
if (UtfServerName == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/LocalAlloc. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
UtfServerNameLength = WideStrToUtfUrlStr(ServerName, (wcslen(ServerName) + 1), UtfServerName, UtfServerNameLength);
if (GetLastError() != ERROR_SUCCESS) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/WideStrToUtfUrlStr(2). Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
UtfNewPathNameLength = WideStrToUtfUrlStr(NewPathName, (wcslen(NewPathName) + 1), NULL, 0);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/WideStrToUtfUrlStr(3). Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
UtfNewPathName = LocalAlloc(LPTR, UtfNewPathNameLength * sizeof(WCHAR));
if (UtfNewPathName == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/LocalAlloc. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
UtfNewPathNameLength = WideStrToUtfUrlStr(NewPathName, (wcslen(NewPathName) + 1), UtfNewPathName, UtfNewPathNameLength);
if (GetLastError() != ERROR_SUCCESS) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/WideStrToUtfUrlStr(4). Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Create the URL with the NewPathName to be sent to the server. Initialize
// the UrlComponents structure before making the call.
//
UrlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
UrlComponents.lpszScheme = NULL;
UrlComponents.dwSchemeLength = 0;
UrlComponents.nScheme = INTERNET_SCHEME_HTTP;
UrlComponents.lpszHostName = UtfServerName;
UrlComponents.dwHostNameLength = wcslen(UtfServerName);
UrlComponents.nPort = DEFAULT_HTTP_PORT;
UrlComponents.lpszUserName = NULL;
UrlComponents.dwUserNameLength = 0;
UrlComponents.lpszPassword = NULL;
UrlComponents.dwPasswordLength = 0;
UrlComponents.lpszUrlPath = UtfNewPathName;
UrlComponents.dwUrlPathLength = wcslen(UtfNewPathName);
UrlComponents.lpszExtraInfo = NULL;
UrlComponents.dwExtraInfoLength = 0;
ReturnVal = InternetCreateUrlW(&(UrlComponents),
0,
NULL,
&(urlLength));
if (!ReturnVal) {
ULONG urlLengthInWChars = 0;
WStatus = GetLastError();
if (WStatus == ERROR_INSUFFICIENT_BUFFER) {
UrlBuffer = (PWCHAR) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT,
urlLength);
if (UrlBuffer != NULL) {
ZeroMemory(UrlBuffer, urlLength);
urlLengthInWChars = ( urlLength/sizeof(WCHAR) );
ReturnVal = InternetCreateUrlW(&(UrlComponents),
0,
UrlBuffer,
&(urlLengthInWChars));
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/InternetCreateUrl. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
} else {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/LocalAlloc. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
} else {
DavPrint((DEBUG_ERRORS,
"DavFsReName/InternetCreateUrl. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
}
DavPrint((DEBUG_MISC, "DavFsReName: URL: %ws\n", UrlBuffer));
//
// We now need to create the Destination header that we will add to the
// request to be sent to the server. This header has the following format.
// "Destination: URL"
//
TagLen = wcslen(L"Destination: ");
convLen = wcslen(UrlBuffer);
HeaderLength = TagLen + convLen;
HeaderLengthInBytes = ( (1 + HeaderLength) * sizeof(WCHAR) );
HeaderBuff = (PWCHAR) LocalAlloc(LPTR, HeaderLengthInBytes);
if (HeaderBuff == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/LocalAlloc. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
wcscpy(HeaderBuff, L"Destination: ");
wcscpy(&(HeaderBuff[TagLen]), UrlBuffer);
DavWorkItem->AsyncReName.HeaderBuff = HeaderBuff;
DavPrint((DEBUG_MISC, "DavFsReName: HeaderBuff: %ws\n", HeaderBuff));
//
// We need to call this only if "DAV_USE_WININET_ASYNCHRONOUSLY" has been
// defined. Otherwise, if we are using WinInet synchronously, then we
// would have already done this in the DavWorkerThread function. This
// ultimately gets deleted (the impersonation token that is) in the
// DavAsyncCreateCompletion function.
//
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Set the DavCallBackContext.
//
WStatus = DavFsSetTheDavCallBackContext(DavWorkItem);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsReName/DavFsSetTheDavCallBackContext. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
CallBackContextInitialized = TRUE;
//
// Store the address of the DavWorkItem which serves as a callback in the
// variable CallBackContext. This will now be used in all the async calls
// that follow.
//
CallBackContext = (ULONG_PTR)(DavWorkItem);
#endif
//
// Allocate memory for the INTERNET_ASYNC_RESULT structure.
//
DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
sizeof(INTERNET_ASYNC_RESULT));
if (DavWorkItem->AsyncResult == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsReName/LocalAlloc. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// A User Entry for this user must have been created during the create call
// earlier. The user entry contains the handle used to send an HttpOpen
// request.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = TRUE;
ReturnVal = DavDoesUserEntryExist(ServerName,
ServerID,
&(DavReNameRequest->LogonID),
&PerUserEntry,
&ServerHashEntry);
//
// If the following request in the kernel get cancelled even before the
// corresponding usermode thread gets a chance to execute this code, then
// it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
// finalized before the thread that is handling the create comes here. This
// could happen if this request was the only one for this share and the
// server as well. This is why we need to check if the ServerHashEntry and
// the PerUserEntry are valid before proceeding.
//
if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
WStatus = ERROR_CANCELLED;
DavPrint((DEBUG_ERRORS, "DavFsReName: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
goto EXIT_THE_FUNCTION;
}
DavWorkItem->AsyncReName.ServerHashEntry = ServerHashEntry;
DavWorkItem->AsyncReName.PerUserEntry = PerUserEntry;
//
// Add a reference to the user entry.
//
PerUserEntry->UserEntryRefCount++;
//
// Since a create had succeeded earlier, the entry must be good.
//
ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
ASSERT(PerUserEntry->DavConnHandle != NULL);
DavConnHandle = PerUserEntry->DavConnHandle;
//
// And yes, we obviously have to leave the critical section
// before returning.
//
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
//
// We now call the HttpOpenRequest function and return.
//
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
//
// Convert the unicode directory path to UTF-8 URL format.
// Space and other white characters will remain untouched - these should
// be taken care of by wininet calls.
//
BStatus = DavHttpOpenRequestW(DavConnHandle,
L"MOVE",
OldPathName,
L"HTTP/1.1",
NULL,
NULL,
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES,
CallBackContext,
L"DavFsReName",
&DavOpenHandle);
if(BStatus == FALSE) {
WStatus = GetLastError();
goto EXIT_THE_FUNCTION;
}
if (DavOpenHandle == NULL) {
WStatus = GetLastError();
if (WStatus != ERROR_IO_PENDING) {
DavPrint((DEBUG_ERRORS,
"DavFsReName/HttpOpenRequest. Error Val = %d\n",
WStatus));
}
goto EXIT_THE_FUNCTION;
}
//
// Cache the DavOpenHandle in the DavWorkItem.
//
DavWorkItem->AsyncReName.DavOpenHandle = DavOpenHandle;
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
DavPrint((DEBUG_ERRORS,
"DavFsReName/DavAsyncCommonStates. Error Val = %08lx\n",
WStatus));
}
EXIT_THE_FUNCTION: // Do the necessary cleanup and return.
//
// We could have taken the lock and come down an error path without
// releasing it. If thats the case, then we need to release the lock now.
//
if (EnCriSec) {
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
}
if (UrlBuffer != NULL) {
HLOCAL FreeHandle;
ULONG FreeStatus;
FreeHandle = LocalFree((HLOCAL)UrlBuffer);
if (FreeHandle != NULL) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsRename/LocalFree. Error Val = %d\n", FreeStatus));
}
}
if (UtfServerName != NULL) {
LocalFree(UtfServerName);
UtfServerName = NULL;
}
if (UtfNewPathName != NULL) {
LocalFree(UtfNewPathName);
UtfNewPathName = NULL;
}
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Some resources should not be freed if we are returning ERROR_IO_PENDING
// because they will be used in the callback functions.
//
if (WStatus != ERROR_IO_PENDING) {
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
DavAsyncReNameCompletion(DavWorkItem);
} else {
DavPrint((DEBUG_MISC, "DavFsReName: Returning ERROR_IO_PENDING.\n"));
}
#else
//
// If we are using WinInet synchronously, then we should never get back
// ERROR_IO_PENDING from WinInet.
//
ASSERT(WStatus != ERROR_IO_PENDING);
//
// If this thread impersonated a user, we need to revert back.
//
if (didImpersonate) {
RevertToSelf();
}
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request. This is done here because the async completion routine that is
// called immediately afterwards needs the status set.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
INTERNET_CACHE_ENTRY_INFOW CEI;
CEI.LastAccessTime.dwLowDateTime = 0;
CEI.LastAccessTime.dwHighDateTime = 0;
SetUrlCacheEntryInfo(DavReNameRequest->Url,&CEI,CACHE_ENTRY_ACCTIME_FC);
DavPrint((DEBUG_MISC,
"DavFsRename Reset LastAccessTime for %ws\n",DavReNameRequest->Url));
DavWorkItem->Status = STATUS_SUCCESS;
}
DavAsyncReNameCompletion(DavWorkItem);
#endif
return WStatus;
}
DWORD
DavAsyncReName(
PDAV_USERMODE_WORKITEM DavWorkItem,
BOOLEAN CalledByCallBackThread
)
/*++
Routine Description:
This is the callback routine for the ReName operation.
Arguments:
DavWorkItem - The DAV_USERMODE_WORKITEM value.
CalledByCallbackThread - TRUE, if this function was called by the thread
which picks of the DavWorkItem from the Callback
function. This happens when an Async WinInet call
returns ERROR_IO_PENDING and completes later.
Return Value:
ERROR_SUCCESS or the appropriate error value.
--*/
{
ULONG WStatus = ERROR_SUCCESS;
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem;
BOOL didImpersonate = FALSE;
HINTERNET DavOpenHandle = NULL;
ULONG HttpResponseStatus = 0;
PCHAR DataBuff = NULL;
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
if (CalledByCallBackThread) {
//
// We are running in the context of a worker thread which has different
// credentials than the user that initiated the I/O request. Before
// proceeding further, we should impersonate the user that initiated the
// request.
//
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/UMReflectorImpersonate. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
//
// Before proceeding further, check to see if the Async operation failed.
// If it did, then cleanup and move on.
//
if ( !DavWorkItem->AsyncResult->dwResult ) {
WStatus = DavWorkItem->AsyncResult->dwError;
//
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then
// WinInet is trying to authenticate itself with the server. In
// such a scenario this is what happens.
//
// Client ----Request-----> Server
// Server ----AccessDenied-----> Client
// Client----Challenge Me-------> Server
// Server-----Challenge--------> Client
// Client-----Challenge Resp----> Server
//
if (WStatus == ERROR_INTERNET_FORCE_RETRY) {
ASSERT(DavWorkItem->DavOperation == DAV_CALLBACK_HTTP_END);
//
// We need to repeat the HttpSend and HttpEnd request calls.
//
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/DavAsyncCommonStates. Error Val ="
" %08lx\n", WStatus));
}
} else {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName. AsyncFunction failed. Error Val = %d\n",
WStatus));
}
goto EXIT_THE_FUNCTION;
}
}
#else
ASSERT(CalledByCallBackThread == FALSE);
#endif
DavOpenHandle = DavWorkItem->AsyncReName.DavOpenHandle;
WStatus = DavQueryAndParseResponseEx(DavOpenHandle, &(HttpResponseStatus));
if (WStatus != ERROR_SUCCESS) {
//
// The MOVE request that was sent to the server failed.
// If the response status is HTTP_STATUS_PRECOND_FAILED then it means
// that we tried to rename a file to a file which already exists and
// ReplaceIfExists (sent by the caller) was FALSE. In such a case we
// return ERROR_ALREADY_EXISTS.
//
if (HttpResponseStatus == HTTP_STATUS_PRECOND_FAILED) {
WStatus = ERROR_ALREADY_EXISTS;
} else {
WStatus = ERROR_UNABLE_TO_MOVE_REPLACEMENT;
}
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/DavQueryAndParseResponse. WStatus = %d, HttpResponseStatus = %d\n",
WStatus, HttpResponseStatus));
}
//
// If we get back a 207 (DAV_MULTI_STATUS) as the response, we need to
// parse the returned XML and see if the status code was 200. If it
// wasn't it means that the MOVE failed.
//
if (HttpResponseStatus == DAV_MULTI_STATUS) {
DWORD NumRead = 0, NumOfFileEntries = 0, TotalDataBytesRead = 0;
BOOL ReturnVal = FALSE, readDone = FALSE;
DAV_FILE_ATTRIBUTES DavFileAttributes;
PVOID Ctx1 = NULL, Ctx2 = NULL;
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, NUM_OF_BYTES_TO_READ);
if (DataBuff == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/LocalAlloc: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Read the response and parse it.
//
do {
ReturnVal = InternetReadFile(DavOpenHandle,
(LPVOID)DataBuff,
NUM_OF_BYTES_TO_READ,
&(NumRead));
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/InternetReadFile: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavAsyncReName: NumRead = %d\n", NumRead));
//
// We reject files whose attributes are greater than a
// certain size (DavFileAttributesLimitInBytes). This
// is a parameter that can be set in the registry. This
// is done to avoid attacks by rogue servers.
//
TotalDataBytesRead += NumRead;
if (TotalDataBytesRead > DavFileAttributesLimitInBytes) {
WStatus = ERROR_BAD_NET_RESP;
DavPrint((DEBUG_ERRORS, "DavAsyncReName. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes));
goto EXIT_THE_FUNCTION;
}
readDone = (NumRead == 0) ? TRUE : FALSE;
WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, NumRead, readDone);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/DavPushData. WStatus = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
if (readDone) {
break;
}
} while ( TRUE );
memset(&DavFileAttributes, 0, sizeof(DavFileAttributes));
InitializeListHead( &(DavFileAttributes.NextEntry) );
WStatus = DavParseData(&DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/DavParseData. WStatus = %d\n",
WStatus));
DavFinalizeFileAttributesList(&DavFileAttributes, FALSE);
goto EXIT_THE_FUNCTION;
}
if (DavFileAttributes.InvalidNode) {
WStatus = ERROR_SHARING_VIOLATION;
}
DavFinalizeFileAttributesList(&DavFileAttributes, FALSE);
DavCloseContext(Ctx1, Ctx2);
}
EXIT_THE_FUNCTION:
//
// If we did impersonate, we need to revert back.
//
if (didImpersonate) {
ULONG RStatus;
RStatus = UMReflectorRevert(UserWorkItem);
if (RStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncReName/UMReflectorRevert. Error Val = %d\n",
RStatus));
}
}
if (DataBuff != NULL) {
LocalFree(DataBuff);
DataBuff = NULL;
}
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Some resources should not be freed if we are returning ERROR_IO_PENDING
// because they will be used in the callback functions.
//
if ( WStatus != ERROR_IO_PENDING && CalledByCallBackThread ) {
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
//
// Call the DavAsyncReNameCompletion routine.
//
DavAsyncReNameCompletion(DavWorkItem);
//
// This thread now needs to send the response back to the kernel. It
// does not wait in the kernel (to get another request) after submitting
// the response.
//
UMReflectorCompleteRequest(DavReflectorHandle, UserWorkItem);
} else {
DavPrint((DEBUG_MISC, "DavAsyncReName: Returning ERROR_IO_PENDING.\n"));
}
#endif
return WStatus;
}
VOID
DavAsyncReNameCompletion(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles the ReName completion. It basically frees up the
resources allocated during the ReName operation.
Arguments:
DavWorkItem - The DAV_USERMODE_WORKITEM value.
Return Value:
none.
--*/
{
if (DavWorkItem->AsyncReName.DavOpenHandle != NULL) {
BOOL ReturnVal;
ULONG FreeStatus;
HINTERNET DavOpenHandle = DavWorkItem->AsyncReName.DavOpenHandle;
ReturnVal = InternetCloseHandle( DavOpenHandle );
if (!ReturnVal) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncReNameCompletion/InternetCloseHandle. Error Val "
"= %d\n", FreeStatus));
}
}
if (DavWorkItem->AsyncReName.HeaderBuff != NULL) {
HLOCAL FreeHandle;
ULONG FreeStatus;
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncReName.HeaderBuff);
if (FreeHandle != NULL) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncReNameCompletion/LocalFree. Error Val = %d\n",
FreeStatus));
}
}
if (DavWorkItem->AsyncResult != NULL) {
HLOCAL FreeHandle;
ULONG FreeStatus;
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
if (FreeHandle != NULL) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncReNameCompletion/LocalFree. Error Val = %d\n",
FreeStatus));
}
}
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
//
// We are done with the per user entry, so finalize it.
//
if (DavWorkItem->AsyncReName.PerUserEntry) {
DavFinalizePerUserEntry( &(DavWorkItem->AsyncReName.PerUserEntry) );
}
return;
}
ULONG
DavFsSetFileInformation(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles SetFileInformation requests for the DAV Mini-Redir that get
reflected from the kernel.
Arguments:
DavWorkItem - The buffer that contains the request parameters and options.
Return Value:
The return status for the operation
--*/
{
ULONG WStatus = ERROR_SUCCESS;
NTSTATUS NtStatus = STATUS_SUCCESS;
PWCHAR ServerName = NULL, DirectoryPath = NULL, CanName = NULL;
PWCHAR OpenVerb = NULL;
ULONG_PTR CallBackContext = (ULONG_PTR)0;
BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE, fSetDirectoryEntry = FALSE;
PDAV_USERMODE_SETFILEINFORMATION_REQUEST SetFileInformationRequest = &(DavWorkItem->SetFileInformationRequest);
ULONG ServerID;
PPER_USER_ENTRY PerUserEntry = NULL;
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
HINTERNET DavConnHandle, DavOpenHandle;
PBYTE DataBuff = NULL;
LARGE_INTEGER FileSize, ByteOffset;
BY_HANDLE_FILE_INFORMATION FileInfo;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
static UINT UniqueTempId = 1;
BOOL didImpersonate = FALSE;
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
BOOLEAN didITakeAPUEReference = FALSE;
//
// The first character is a '\' which has to be stripped.
//
ServerName = &(SetFileInformationRequest->ServerName[1]);
if (!ServerName) {
DavPrint((DEBUG_ERRORS, "DavFsSetFileInformation: ServerName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsSetFileInformation: ServerName = %ws.\n", ServerName));
ServerID = SetFileInformationRequest->ServerID;
DavPrint((DEBUG_MISC, "DavFsSetFileInformation: ServerID = %d.\n", ServerID));
//
// The first character is a '\' which has to be stripped.
//
DirectoryPath = &(SetFileInformationRequest->PathName[1]);
if (!DirectoryPath) {
DavPrint((DEBUG_ERRORS, "DavFsSetFileInformation: DirectoryPath is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsSetFileInformation: DirectoryPath = %ws.\n", DirectoryPath));
//
// The DirectoryPath can contain \ characters. Replace them by / characters.
//
CanName = DirectoryPath;
while (*CanName) {
if (*CanName == L'\\') {
*CanName = L'/';
}
CanName++;
}
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
//
// If we have a dummy share name in the DirectoryPath, we need to remove it
// right now before we contact the server.
//
DavRemoveDummyShareFromFileName(DirectoryPath);
//
// A User Entry for this user must have been created during the create call
// earlier. The user entry contains the handle used to send an HttpOpen
// request.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = TRUE;
ReturnVal = DavDoesUserEntryExist(ServerName,
ServerID,
&(SetFileInformationRequest->LogonID),
&PerUserEntry,
&ServerHashEntry);
//
// If the following request in the kernel get cancelled even before the
// corresponding usermode thread gets a chance to execute this code, then
// it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
// finalized before the thread that is handling the create comes here. This
// could happen if this request was the only one for this share and the
// server as well. This is why we need to check if the ServerHashEntry and
// the PerUserEntry are valid before proceeding.
//
if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
WStatus = ERROR_CANCELLED;
DavPrint((DEBUG_ERRORS, "DavFsSetFileInformation: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
goto EXIT_THE_FUNCTION;
}
DavWorkItem->ServerUserEntry.PerUserEntry = PerUserEntry;
//
// Add a reference to the user entry and set didITakeAPUEReference to TRUE.
//
PerUserEntry->UserEntryRefCount++;
didITakeAPUEReference = TRUE;
//
// Since a create had succeeded earlier, the entry must be good.
//
ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
ASSERT(PerUserEntry->DavConnHandle != NULL);
//
// And yes, we obviously have to leave the critical section
// before returning.
//
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsSetFileInformation/UMReflectorImpersonate. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
DavWorkItem->DavMinorOperation = DavMinorProppatchFile;
WStatus = DavSetBasicInformation(DavWorkItem,
PerUserEntry->DavConnHandle,
DirectoryPath,
SetFileInformationRequest->fCreationTimeChanged,
SetFileInformationRequest->fLastAccessTimeChanged,
SetFileInformationRequest->fLastModifiedTimeChanged,
SetFileInformationRequest->fFileAttributesChanged,
&SetFileInformationRequest->FileBasicInformation.CreationTime,
&SetFileInformationRequest->FileBasicInformation.LastAccessTime,
&SetFileInformationRequest->FileBasicInformation.LastWriteTime,
SetFileInformationRequest->FileBasicInformation.FileAttributes);
if (WStatus != ERROR_SUCCESS) {
ULONG LogStatus;
DavPrint((DEBUG_ERRORS,
"DavFsSetFileInformation/DavSetBasicInformation. WStatus = %d\n",
WStatus));
LogStatus = DavFormatAndLogError(DavWorkItem, WStatus);
if (LogStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsSetFileInformation/DavFormatAndLogError. LogStatus = %d\n",
LogStatus));
}
}
RevertToSelf();
didImpersonate = FALSE;
EXIT_THE_FUNCTION:
if (EnCriSec) {
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
}
//
// If didITakeAPUEReference is TRUE we need to remove the reference we
// took on the PerUserEntry.
//
if (didITakeAPUEReference) {
DavFinalizePerUserEntry( &(DavWorkItem->ServerUserEntry.PerUserEntry) );
}
//
// If we are using WinInet synchronously, then we should never get back
// ERROR_IO_PENDING from WinInet.
//
ASSERT(WStatus != ERROR_IO_PENDING);
//
// If this thread impersonated a user, we need to revert back.
//
if (didImpersonate) {
RevertToSelf();
}
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request. This is done here because the async completion routine that is
// called immediately afterwards needs the status set.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
return WStatus;
}