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