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.
2239 lines
78 KiB
2239 lines
78 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
davclose.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the user mode DAV MiniRedir routines pertaining to
|
|
closing of files.
|
|
|
|
Author:
|
|
|
|
Rohan Kumar [RohanK] 02-June-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ntumrefl.h"
|
|
#include "usrmddav.h"
|
|
#include "global.h"
|
|
#include "nodefac.h"
|
|
#include "UniUtf.h"
|
|
|
|
CHAR rgXmlHeader[] = "Content-Type: text/xml; charset=\"utf-8\"";
|
|
CHAR rgPropPatchHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><D:propertyupdate xmlns:D=\"DAV:\" xmlns:Z=\"urn:schemas-microsoft-com:\"><D:set><D:prop>";
|
|
CHAR rgPropPatchTrailer[] = "</D:prop></D:set></D:propertyupdate>";
|
|
CHAR rgCreationTimeTagHeader[] = "<Z:Win32CreationTime>";
|
|
CHAR rgCreationTimeTagTrailer[] = "</Z:Win32CreationTime>";
|
|
CHAR rgLastAccessTimeTagHeader[] = "<Z:Win32LastAccessTime>";
|
|
CHAR rgLastAccessTimeTagTrailer[] = "</Z:Win32LastAccessTime>";
|
|
CHAR rgLastModifiedTimeTagHeader[] = "<Z:Win32LastModifiedTime>";
|
|
CHAR rgLastModifiedTimeTagTrailer[] = "</Z:Win32LastModifiedTime>";
|
|
CHAR rgFileAttributesTagHeader[] = "<Z:Win32FileAttributes>";
|
|
CHAR rgFileAttributesTagTrailer[] = "</Z:Win32FileAttributes>";
|
|
CHAR rgDummyAttributes[] = "<Z:Dummy>0</Z:Dummy>";
|
|
|
|
#define MAX_DWORD 0xffffffff
|
|
|
|
//
|
|
// These two functions are used in saving an encrypted file on the server.
|
|
//
|
|
|
|
DWORD
|
|
DavReadRawCallback(
|
|
PBYTE DataBuffer,
|
|
PVOID CallbackContext,
|
|
ULONG DataLength
|
|
);
|
|
|
|
|
|
BOOL
|
|
DavConvertTimeToXml(
|
|
IN PCHAR lpTagHeader,
|
|
IN DWORD dwHeaderSize,
|
|
IN PCHAR lpTagTrailer,
|
|
IN DWORD dwTrailerSize,
|
|
IN LARGE_INTEGER *lpTime,
|
|
OUT PCHAR *lplpBuffer,
|
|
IN OUT DWORD *lpdwBufferSize
|
|
);
|
|
|
|
DWORD
|
|
DavSetProperties(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET hDavConnect,
|
|
LPWSTR lpPathName,
|
|
LPSTR lpPropertiesBuffer
|
|
);
|
|
|
|
DWORD
|
|
DavTestProppatch(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET hDavConnect,
|
|
LPWSTR lpPathName
|
|
);
|
|
|
|
extern DWORD
|
|
DavSetAclForEncryptedFile(
|
|
PWCHAR FilePath
|
|
);
|
|
|
|
//
|
|
// Implementation of functions begins here.
|
|
//
|
|
|
|
ULONG
|
|
DavFsClose(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles DAV close request 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_CLOSE_REQUEST CloseRequest = &(DavWorkItem->CloseRequest);
|
|
ULONG ServerID;
|
|
PPER_USER_ENTRY PerUserEntry = NULL;
|
|
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
|
|
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
|
|
PBYTE DataBuff = NULL;
|
|
LARGE_INTEGER FileSize, ByteOffset;
|
|
BY_HANDLE_FILE_INFORMATION FileInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE FileHandle = NULL;
|
|
UNICODE_STRING UnicodeFileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
static UINT UniqueTempId = 1;
|
|
BOOL didImpersonate = FALSE;
|
|
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
|
|
BOOL BStatus = FALSE;
|
|
|
|
DavPrint((DEBUG_MISC, "DavFsClose: FileName = %ws.\n", CloseRequest->FileName));
|
|
DavPrint((DEBUG_MISC, "DavFsClose: Modified = %d.\n", CloseRequest->FileWasModified));
|
|
|
|
UnicodeFileName.Buffer = NULL;
|
|
UnicodeFileName.Length = 0;
|
|
UnicodeFileName.MaximumLength = 0;
|
|
|
|
//
|
|
// If any of the time values have changed, then we need to PROPPATCH the
|
|
// information back to the server.
|
|
//
|
|
if ( !CloseRequest->DeleteOnClose &&
|
|
( CloseRequest->fCreationTimeChanged ||
|
|
CloseRequest->fLastAccessTimeChanged ||
|
|
CloseRequest->fLastModifiedTimeChanged ||
|
|
CloseRequest->fFileAttributesChanged ) ) {
|
|
fSetDirectoryEntry = TRUE;
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC, "DavFsClose: fSetDirectoryEntry = %x \n", fSetDirectoryEntry));
|
|
|
|
if ( CloseRequest->isDirectory &&
|
|
!CloseRequest->DeleteOnClose &&
|
|
!fSetDirectoryEntry ) {
|
|
//
|
|
// If this is a directory close, then the only reason to contact the
|
|
// server is when we are deleting the directory and all the files
|
|
// under it. If not, we can return right now.
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if ( !CloseRequest->isDirectory ) {
|
|
|
|
//
|
|
// We need to close the handle only if it was created in the user mode.
|
|
//
|
|
if ( !CloseRequest->createdInKernel && CloseRequest->Handle ) {
|
|
|
|
DavPrint((DEBUG_MISC, "DavFsClose: OpenHandle = %08lx.\n", CloseRequest->Handle));
|
|
|
|
//
|
|
// Close the handle that was opened during the Create call.
|
|
//
|
|
ASSERT((CloseRequest->UserModeKey) == ((PVOID)CloseRequest->Handle));
|
|
|
|
ReturnVal = CloseHandle(CloseRequest->Handle);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/CloseHandle: Return Val = %08lx.\n", WStatus));
|
|
} else {
|
|
CloseRequest->UserModeKey = NULL;
|
|
CloseRequest->Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// DeleteOnClose FileCreatedLocally FileModified Action
|
|
// ------------- ------------------ ------------ -------
|
|
// 0 0 0 NOTHING
|
|
// 0 0 1 PUT
|
|
// 0 1 0 PUT
|
|
// 0 1 1 PUT
|
|
// 1 0 0 DELETE
|
|
// 1 0 1 DELETE
|
|
// 1 1 0 NOTHING
|
|
// 1 1 1 NOTHING
|
|
//
|
|
// The FileCreatedLocally no longer matters since we PUT the file
|
|
// immediately as soon as we create a local copy to claim the name on
|
|
// the server.
|
|
//
|
|
|
|
//
|
|
// If this file doesn't have to be deleted, was not created locally and
|
|
// was not written to, or direntry not modified, or the file was not
|
|
// LOCKed, then we are done.
|
|
//
|
|
if ( !(CloseRequest->DeleteOnClose) &&
|
|
!(CloseRequest->FileWasModified) &&
|
|
!(fSetDirectoryEntry) &&
|
|
!(CloseRequest->OpaqueLockToken) ) {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// In all other cases (or combinations of the above three booleans), we
|
|
// need to go to the server. So, before we procced to decide what to do
|
|
// on the server with this file, we need to set up the parameters for the
|
|
// WinInet calls.
|
|
//
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ServerName = &(CloseRequest->ServerName[1]);
|
|
if (!ServerName) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsClose: ServerName is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC, "DavFsClose: ServerName = %ws.\n", ServerName));
|
|
|
|
ServerID = CloseRequest->ServerID;
|
|
DavPrint((DEBUG_MISC, "DavFsClose: ServerID = %d.\n", ServerID));
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
DirectoryPath = &(CloseRequest->PathName[1]);
|
|
if (!DirectoryPath) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsClose: DirectoryPath is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC, "DavFsClose: 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);
|
|
|
|
//
|
|
// 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,
|
|
"DavFsClose/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, "DavFsClose/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,
|
|
&(CloseRequest->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, "DavFsClose: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.PerUserEntry = PerUserEntry;
|
|
|
|
DavWorkItem->AsyncClose.ServerHashEntry = ServerHashEntry;
|
|
|
|
//
|
|
// 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;
|
|
|
|
if ( !CloseRequest->isDirectory ) {
|
|
|
|
//
|
|
// If the file has to be deleted on close, we need to send a DELETE for
|
|
// this file to the server. It does not matter if the file has been
|
|
// modified or not.
|
|
//
|
|
if ( (CloseRequest->DeleteOnClose) ) {
|
|
|
|
DavWorkItem->DavMinorOperation = DavMinorDeleteFile;
|
|
|
|
OpenVerb = L"DELETE";
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = NULL;
|
|
|
|
} else if (CloseRequest->FileWasModified) {
|
|
|
|
//
|
|
// The file has been changed and needs to be PUT on the server.
|
|
//
|
|
DavWorkItem->DavMinorOperation = DavMinorPutFile;
|
|
|
|
OpenVerb = L"PUT";
|
|
|
|
//
|
|
// We need to check if this file is encrypted. If it is, we need to
|
|
// BackUp the encrypted file to a temp file and send the BackedUp file
|
|
// to the server.
|
|
//
|
|
if ( !( CloseRequest->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED ) ) {
|
|
|
|
DavPrint((DEBUG_MISC, "DavFsClose. This is NOT an Encrypted file.\n"));
|
|
|
|
//
|
|
// Create an NT path name for the cached file. This is used in the
|
|
// NtCreateFile call below.
|
|
//
|
|
ReturnVal = RtlDosPathNameToNtPathName_U(CloseRequest->FileName,
|
|
&(UnicodeFileName),
|
|
NULL,
|
|
NULL);
|
|
if (!ReturnVal) {
|
|
WStatus = ERROR_BAD_PATHNAME;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/RtlDosPathNameToNtPathName. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
InitializeObjectAttributes(&(ObjectAttributes),
|
|
&(UnicodeFileName),
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
NULL);
|
|
|
|
//
|
|
// This #if 0 below was added because the NtCreateFile was failing
|
|
// with ERROR_ACCESS_DENIED. This is because this file has been
|
|
// created in the LocalService's %USERPROFILE% and you need to be
|
|
// in the context of the LocalService before calling NtCreateFile.
|
|
// By impersonating below we were getting into the context of the
|
|
// user and hence the call failed.
|
|
//
|
|
|
|
#if 0
|
|
//
|
|
// We are running in the context of the Web Client service. Before
|
|
// contacting the server below, we need to impersonate the client
|
|
// that issued this request.
|
|
//
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/UMReflectorImpersonate(1). Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
didImpersonate = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// Create a handle to the local file, for reading its attributes and data.
|
|
// We read the whole file into a buffer and send it across to the server.
|
|
//
|
|
NtStatus = NtCreateFile(&(FileHandle),
|
|
(SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA),
|
|
&(ObjectAttributes),
|
|
&(IoStatusBlock),
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
|
FILE_OPEN,
|
|
(FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT),
|
|
NULL,
|
|
0);
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
//
|
|
// We convert the NtStatus to DOS error here. The Win32
|
|
// error code is finally set to an NTSTATUS value in
|
|
// the DavFsCreate function just before returning.
|
|
//
|
|
WStatus = RtlNtStatusToDosError(NtStatus);
|
|
FileHandle = NULL;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/NtCreateFile(1). Error Val = %08lx\n",
|
|
NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
ReturnVal = GetFileInformationByHandle(FileHandle, &(FileInfo));
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/GetFileInformationByHandle: Return Val = %08lx.\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
didImpersonate = FALSE;
|
|
}
|
|
|
|
FileSize.LowPart = FileInfo.nFileSizeLow;
|
|
FileSize.HighPart = FileInfo.nFileSizeHigh;
|
|
|
|
if ( FileSize.QuadPart > (LONGLONG)0 ) {
|
|
|
|
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, FileSize.LowPart);
|
|
if (DataBuff == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/LocalAlloc. Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = DataBuff;
|
|
|
|
//
|
|
// Start reading at the first byte.
|
|
//
|
|
ByteOffset.LowPart = 0;
|
|
ByteOffset.HighPart = 0;
|
|
|
|
NtStatus = NtReadFile(FileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&(IoStatusBlock),
|
|
DataBuff,
|
|
FileSize.LowPart,
|
|
&(ByteOffset),
|
|
NULL);
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
//
|
|
// We convert the NtStatus to DOS error here. The Win32
|
|
// error code is finally set to an NTSTATUS value in
|
|
// the DavFsCreate function just before returning.
|
|
//
|
|
WStatus = RtlNtStatusToDosError(NtStatus);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/NtReadFile. Error Val = %08lx\n",
|
|
NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.DataBuffSizeInBytes = FileSize.LowPart;
|
|
|
|
NtStatus = NtClose(FileHandle);
|
|
FileHandle = NULL;
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
//
|
|
// We convert the NtStatus to DOS error here. The Win32
|
|
// error code is finally set to an NTSTATUS value in
|
|
// the DavFsCreate function just before returning.
|
|
//
|
|
WStatus = RtlNtStatusToDosError(NtStatus);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/NtClose. Error Val = %08lx\n",
|
|
NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
} else {
|
|
|
|
DavPrint((DEBUG_MISC, "DavFsClose. Zero Byte File.\n"));
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD err;
|
|
UINT tempErr;
|
|
BOOL copyErr;
|
|
PVOID RawContext = NULL;
|
|
|
|
//
|
|
// This is an encrypted file. Create a BackUp stream, store it into
|
|
// a temp file and PUT the temp file (BLOB) on the server.
|
|
//
|
|
DavPrint((DEBUG_MISC, "DavFsClose. This is an Encrypted file.\n"));
|
|
|
|
//
|
|
// We loop till we can come up with a FileName in the TEMP directory
|
|
// of the user which has not been used.
|
|
//
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsClose: FileName = %ws\n", CloseRequest->FileName));
|
|
|
|
//
|
|
// If the file was opened as non-encrypted, the local cache file does not have
|
|
// the ACL allowing everyone to access. Set the ACL here before impersonating.
|
|
//
|
|
WStatus = DavSetAclForEncryptedFile(CloseRequest->FileName);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncClose/DavSetAclForEncryptedFile. Error Val"
|
|
" = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// We are running in the context of the Web Client service. Before contacting
|
|
// the server below, we need to impersonate the client that issued this
|
|
// request.
|
|
//
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/UMReflectorImpersonate(2). Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
didImpersonate = TRUE;
|
|
|
|
//
|
|
// Open a Raw context to the file.
|
|
//
|
|
WStatus = OpenEncryptedFileRawW(CloseRequest->FileName, 0, &(RawContext));
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/OpenEncryptedFileRaw. Error Val = %d %ws\n",
|
|
WStatus,CloseRequest->FileName));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// The extra space prepared for the EFS stream.
|
|
//
|
|
|
|
DavWorkItem->AsyncClose.DataBuffAllocationSize = (CloseRequest->FileSize >> 4) + 0x1000;
|
|
|
|
if (MAX_DWORD - CloseRequest->FileSize < DavWorkItem->AsyncClose.DataBuffAllocationSize) {
|
|
WStatus = ERROR_NO_SYSTEM_RESOURCES;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/backup size exceeds MRX_DWORD!"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.DataBuffAllocationSize += CloseRequest->FileSize;
|
|
|
|
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, DavWorkItem->AsyncClose.DataBuffAllocationSize);
|
|
|
|
if (DataBuff == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/LocalAlloc. Error Val = %d\n", WStatus));
|
|
|
|
if (RawContext) {
|
|
CloseEncryptedFileRaw(RawContext);
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = DataBuff;
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsClose: allocate backup buffer %x %x\n",DataBuff,DavWorkItem->AsyncClose.DataBuffAllocationSize));
|
|
|
|
WStatus = ReadEncryptedFileRaw((PFE_EXPORT_FUNC)DavReadRawCallback,
|
|
(PVOID)DavWorkItem,
|
|
RawContext);
|
|
|
|
if (RawContext) {
|
|
CloseEncryptedFileRaw(RawContext);
|
|
}
|
|
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/ReadEncryptedFileRaw. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
didImpersonate = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT( (fSetDirectoryEntry == TRUE) || (CloseRequest->OpaqueLockToken != NULL) );
|
|
|
|
//
|
|
// If it is only an attribute change, we send the PROPPATCH. If the
|
|
// file was LOCKed on Create, we send the UNLOCK request.
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (CloseRequest->DeleteOnClose) {
|
|
//
|
|
// This is a directory and needs to be deleted from the server.
|
|
//
|
|
DavWorkItem->DavMinorOperation = DavMinorDeleteFile;
|
|
|
|
OpenVerb = L"DELETE";
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = NULL;
|
|
} else if (fSetDirectoryEntry) {
|
|
//
|
|
// If this is a directory close, then the only reason to contact the
|
|
// server is when we are deleting the directory and all the files
|
|
// under it. If not, we can return right now.
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are running in the context of the Web Client service. Before contacting
|
|
// the server below, we need to impersonate the client that issued this
|
|
// request.
|
|
//
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/UMReflectorImpersonate(3). Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
didImpersonate = TRUE;
|
|
|
|
//
|
|
// We now call the DavHttpOpenRequest function.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
|
|
|
|
//
|
|
// Convert the unicode object name 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,
|
|
OpenVerb,
|
|
DirectoryPath,
|
|
L"HTTP/1.1",
|
|
NULL,
|
|
NULL,
|
|
INTERNET_FLAG_KEEP_CONNECTION |
|
|
INTERNET_FLAG_NO_COOKIES,
|
|
CallBackContext,
|
|
L"DavFsClose",
|
|
&DavOpenHandle);
|
|
if(BStatus == FALSE) {
|
|
WStatus = GetLastError();
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
if (DavOpenHandle == NULL) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/DavHttpOpenRequestW. Error Val = %d\n",
|
|
WStatus));
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
DavWorkItem->AsyncClose.DavOpenHandle = DavOpenHandle;
|
|
|
|
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
|
|
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/DavAsyncCommonStates. Error Val = %08lx\n",
|
|
WStatus));
|
|
}
|
|
|
|
if (WStatus == ERROR_SUCCESS) {
|
|
|
|
INTERNET_CACHE_ENTRY_INFOW CEI;
|
|
|
|
CEI.LastAccessTime.dwLowDateTime = 0;
|
|
CEI.LastAccessTime.dwHighDateTime = 0;
|
|
|
|
SetUrlCacheEntryInfo(CloseRequest->Url,&CEI,CACHE_ENTRY_ACCTIME_FC);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsClose Reset LastAccessTime for %ws\n",CloseRequest->Url));
|
|
|
|
if (CloseRequest->FileWasModified &&
|
|
(CloseRequest->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
|
|
//
|
|
// Reset the LastModifiedTime on URL cache of the encrypted file
|
|
// so that the public cache will be updated on the next GET.
|
|
//
|
|
CEI.LastModifiedTime.dwLowDateTime = 0;
|
|
CEI.LastModifiedTime.dwHighDateTime = 0;
|
|
|
|
SetUrlCacheEntryInfo(CloseRequest->Url,&CEI,CACHE_ENTRY_MODTIME_FC);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsClose Reset LastModifiedTime %ws\n",CloseRequest->Url));
|
|
}
|
|
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (fSetDirectoryEntry && (WStatus == ERROR_SUCCESS)) {
|
|
|
|
if (!didImpersonate) {
|
|
//
|
|
// If we are using WinInet synchronously, then we need to impersonate the
|
|
// clients context now. This is becuase the DavSetProperties call below
|
|
// contacts the DAV Server and we need to be impersonating the correct
|
|
// client when contacting it.
|
|
//
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/UMReflectorImpersonate(4). Error Val = %d\n",
|
|
WStatus));
|
|
} else {
|
|
didImpersonate = TRUE;
|
|
}
|
|
}
|
|
|
|
if (WStatus == ERROR_SUCCESS) {
|
|
|
|
DavWorkItem->DavMinorOperation = DavMinorProppatchFile;
|
|
|
|
WStatus = DavSetBasicInformation(DavWorkItem,
|
|
DavConnHandle,
|
|
DirectoryPath,
|
|
CloseRequest->fCreationTimeChanged,
|
|
CloseRequest->fLastAccessTimeChanged,
|
|
CloseRequest->fLastModifiedTimeChanged,
|
|
CloseRequest->fFileAttributesChanged,
|
|
&CloseRequest->CreationTime,
|
|
&CloseRequest->LastAccessTime,
|
|
&CloseRequest->LastModifiedTime,
|
|
CloseRequest->dwFileAttributes);
|
|
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
|
|
ULONG LogStatus;
|
|
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/DavSetBasicInformation. WStatus = %d\n",
|
|
WStatus));
|
|
|
|
LogStatus = DavFormatAndLogError(DavWorkItem, WStatus);
|
|
if (LogStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/DavFomatAndLogError. LogStatus = %d\n",
|
|
LogStatus));
|
|
}
|
|
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsClose set BasicInformation(2). %d %x %ws\n",
|
|
WStatus,CloseRequest->dwFileAttributes,DirectoryPath));
|
|
|
|
//
|
|
// If the PROPPATCH fails, we don't fail the close call. This is
|
|
// because the PUT (if one was needed) has suceeded and we reset the
|
|
// FileWasModified flag in the FCB based on whether this call succeeds.
|
|
// On the final close, we check to see if this flag is set to FALSE
|
|
// and pop up a box saying that the "delayed write failed". We
|
|
// shouldn't be doing it if the PUT succeeds and the PROPPATCH fails.
|
|
// We log an entry in the EventLog (under application) that the
|
|
// PROPPATCH has failed though.
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the file was LOCKed on the server on Create, we need to UnLock it
|
|
// now. If the UNLOCK fails, we don't fail the close call. Also, we don't
|
|
// need to UNLOCK the file if we have already deleted it.
|
|
//
|
|
if (CloseRequest->OpaqueLockToken && !CloseRequest->DeleteOnClose) {
|
|
ULONG UnLockStatus;
|
|
if (!didImpersonate) {
|
|
//
|
|
// If we are using WinInet synchronously, then we need to impersonate
|
|
// the clients context now. This is becuase the DavUnLockFile call
|
|
// below contacts the DAV Server and we need to be impersonating the
|
|
// correct client when contacting it.
|
|
//
|
|
UnLockStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (UnLockStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/UMReflectorImpersonate(5). UnLockStatus = %d\n",
|
|
UnLockStatus));
|
|
} else {
|
|
didImpersonate = TRUE;
|
|
}
|
|
}
|
|
UnLockStatus = DavUnLockTheFileOnTheServer(DavWorkItem);
|
|
if (UnLockStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsClose/DavUnLockTheFileOnTheServer: UnLockStatus = %08lx\n",
|
|
UnLockStatus));
|
|
}
|
|
}
|
|
|
|
if (EnCriSec) {
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = FALSE;
|
|
}
|
|
|
|
//
|
|
// The function RtlDosPathNameToNtPathName_U allocates memory from the
|
|
// processes heap. If we did, we need to free it now.
|
|
//
|
|
if (UnicodeFileName.Buffer != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeFileName.Buffer);
|
|
}
|
|
|
|
//
|
|
// 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();
|
|
}
|
|
|
|
if (FileHandle != NULL) {
|
|
NtClose(FileHandle);
|
|
FileHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
DavAsyncCloseCompletion(DavWorkItem);
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavAsyncClose(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
BOOLEAN CalledByCallBackThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine for the close 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;
|
|
|
|
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncClose.DavOpenHandle;
|
|
|
|
#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,
|
|
"DavAsyncClose/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,
|
|
"DavAsyncClose/DavAsyncCommonStates. Error Val ="
|
|
" %08lx\n", WStatus));
|
|
}
|
|
|
|
} else {
|
|
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncClose. AsyncFunction failed. Error Val = %d\n",
|
|
WStatus));
|
|
|
|
}
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
ASSERT(CalledByCallBackThread == FALSE);
|
|
|
|
#endif
|
|
|
|
//
|
|
// We return what ever the response code from the Http server was for this
|
|
// request.
|
|
//
|
|
WStatus = DavQueryAndParseResponse(DavOpenHandle);
|
|
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
|
|
ULONG LogStatus;
|
|
|
|
LogStatus = DavFormatAndLogError(DavWorkItem, WStatus);
|
|
if (LogStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncClose/DavFormatAndLogError. LogStatus = %d\n",
|
|
LogStatus));
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
|
|
EXIT_THE_FUNCTION:
|
|
#endif
|
|
|
|
//
|
|
// If we did impersonate, we need to revert back.
|
|
//
|
|
if (didImpersonate) {
|
|
ULONG RStatus;
|
|
RStatus = UMReflectorRevert(UserWorkItem);
|
|
if (RStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncClose/UMReflectorRevert. Error Val = %d\n",
|
|
RStatus));
|
|
}
|
|
}
|
|
|
|
#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 DavAsyncCloseCompletion routine.
|
|
//
|
|
DavAsyncCloseCompletion(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, "DavAsyncClose: Returning ERROR_IO_PENDING.\n"));
|
|
}
|
|
|
|
#endif
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavAsyncCloseCompletion(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the Async Close completion. It basically frees up
|
|
the resources allocated during the Async Close operation.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
if (DavWorkItem->AsyncClose.DavOpenHandle != NULL) {
|
|
BOOL ReturnVal;
|
|
ULONG FreeStatus;
|
|
HINTERNET DavOpenHandle = DavWorkItem->AsyncClose.DavOpenHandle;
|
|
ReturnVal = InternetCloseHandle( DavOpenHandle );
|
|
if (!ReturnVal) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCloseCompletion/InternetCloseHandle. "
|
|
"Error Val = %d\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
if (DavWorkItem->AsyncClose.DataBuff != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncClose.DataBuff);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCloseCompletion/LocalFree. Error Val = %d\n",
|
|
FreeStatus));
|
|
}
|
|
}
|
|
|
|
if (DavWorkItem->AsyncClose.InternetBuffers != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncClose.InternetBuffers);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCloseCompletion/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,
|
|
"DavAsyncCloseCompletion/LocalFree. Error Val = %d\n",
|
|
FreeStatus));
|
|
}
|
|
}
|
|
|
|
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
|
|
|
|
//
|
|
// We are done with the per user entry, so finalize it.
|
|
//
|
|
if (DavWorkItem->AsyncClose.PerUserEntry) {
|
|
DavFinalizePerUserEntry( &(DavWorkItem->AsyncClose.PerUserEntry) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
DavReadRawCallback(
|
|
PBYTE DataBuffer,
|
|
PVOID CallbackContext,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call-back function for ReadEncryptedFileRaw(). This function allocate a buffer for
|
|
async close and writes back the data to this buffer specified on because
|
|
ReadEncryptedFileRaw() provides the raw data to this callback function
|
|
which in turn stores it in a backup file. This call-back function is called
|
|
until there is no more data left.
|
|
|
|
Arguments:
|
|
|
|
DataBuffer - Data to be written.
|
|
|
|
CallbackContext - Handle to the Backup file.
|
|
|
|
DataLength - Size of the DataBuffer.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
DWORD BytesWritten = 0;
|
|
BOOL ReturnVal;
|
|
PBYTE PreviousBuffer = NULL;
|
|
ULONG PreviousDataLength = 0;
|
|
PDAV_USERMODE_WORKITEM DavWorkItem = (PDAV_USERMODE_WORKITEM)CallbackContext;
|
|
|
|
DavPrint((DEBUG_MISC, "DavReadRawCallback: DataLength = %d\n", DataLength));
|
|
|
|
if ( !DataLength ) {
|
|
return WStatus;
|
|
}
|
|
|
|
ASSERT(DavWorkItem->AsyncClose.DataBuff != NULL);
|
|
|
|
PreviousDataLength = DavWorkItem->AsyncClose.DataBuffSizeInBytes;
|
|
|
|
//
|
|
// If the backup size exceeds the pre-allocation size, we have to allocate a bigger buffer.
|
|
//
|
|
if (PreviousDataLength + DataLength > DavWorkItem->AsyncClose.DataBuffAllocationSize) {
|
|
|
|
if ((MAX_DWORD - PreviousDataLength < DataLength) ||
|
|
(MAX_DWORD - PreviousDataLength - DataLength < 0x10000)) {
|
|
WStatus = ERROR_NO_SYSTEM_RESOURCES;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavReadRawCallback/backup size exceeds MRX_DWORD!"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
PreviousBuffer = DavWorkItem->AsyncClose.DataBuff;
|
|
DavWorkItem->AsyncClose.DataBuffAllocationSize = DataLength+PreviousDataLength+0x10000;
|
|
|
|
DavWorkItem->AsyncClose.DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, DavWorkItem->AsyncClose.DataBuffAllocationSize);
|
|
|
|
if (DavWorkItem->AsyncClose.DataBuff == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavReadRawCallback/LocalAlloc. Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RtlCopyMemory(DavWorkItem->AsyncClose.DataBuff,
|
|
PreviousBuffer,
|
|
PreviousDataLength);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavReadRawCallback: allocate a bigger buffer %x %x\n",
|
|
DavWorkItem->AsyncClose.DataBuff,
|
|
DavWorkItem->AsyncClose.DataBuffAllocationSize));
|
|
}
|
|
|
|
RtlCopyMemory((PBYTE)(DavWorkItem->AsyncClose.DataBuff + PreviousDataLength),
|
|
DataBuffer,
|
|
DataLength);
|
|
|
|
DavWorkItem->AsyncClose.DataBuffSizeInBytes += DataLength;
|
|
|
|
DavPrint((DEBUG_MISC, "DavReadRawCallback: Buffer %x DataLength %d\n",
|
|
DavWorkItem->AsyncClose.DataBuff,DavWorkItem->AsyncClose.DataBuffSizeInBytes));
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (PreviousBuffer) {
|
|
LocalFree(PreviousBuffer);
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavSetBasicInformation(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET hDavConnect,
|
|
LPWSTR PathName,
|
|
BOOL fCreationTimeChanged,
|
|
BOOL fLastAccessTimeChanged,
|
|
BOOL fLastModifiedTimeChanged,
|
|
BOOL fFileAttributesChanged,
|
|
IN LARGE_INTEGER *lpCreationTime,
|
|
IN LARGE_INTEGER *lpLastAccessTime,
|
|
IN LARGE_INTEGER *lpLastModifiedTime,
|
|
DWORD dwFileAttributes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets DAV properties on a file or a directory. It formats an XML requests and sends it
|
|
to the server.
|
|
|
|
Arguments:
|
|
|
|
DavConnectHandle - Server connection.
|
|
|
|
CloseRequest - Usemode close request corresponding to the kernelmode close.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
CHAR *lpTemp = NULL, Buffer[1024];
|
|
DWORD dwError = ERROR_SUCCESS, dwSizeRemaining, dwTemp;
|
|
BOOL fRet = FALSE;
|
|
BOOL fInfoChange = TRUE;
|
|
DWORD dwOverrideAttribMask = (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_ENCRYPTED |
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
|
|
FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_TEMPORARY |
|
|
FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
fInfoChange = (fCreationTimeChanged | fLastAccessTimeChanged |
|
|
fLastModifiedTimeChanged | fFileAttributesChanged);
|
|
|
|
DavPrint((DEBUG_MISC, "DavSetBasicInformation: Attributes = %x %x\n", dwFileAttributes,fInfoChange));
|
|
|
|
//
|
|
// We do not proceed further since there is no information to change. Also,
|
|
// in this case we return SUCCESS back to the caller.
|
|
//
|
|
if(fInfoChange == FALSE) {
|
|
fRet = TRUE;
|
|
dwError = ERROR_SUCCESS;
|
|
goto bailout;
|
|
}
|
|
|
|
//
|
|
// If attributes have changed, then verify that the new attributes are in
|
|
// valid combination i.e. If either of following attributes is present:
|
|
// FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ENCRYPTED,
|
|
// FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
|
|
// FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_READONLY,
|
|
// FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY, FILE_ATTRIBUTE_DIRECTORY
|
|
// and if FILE_ATTRIBUTE_NORMAL is present, then FILE_ATTRIBUTE_NORMAL
|
|
// should be filtered.
|
|
//
|
|
|
|
if (fFileAttributesChanged == TRUE && (dwOverrideAttribMask & dwFileAttributes)) {
|
|
dwFileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
//
|
|
// If this is a directoy and the attributes being set include
|
|
// FILE_TEMPORARY_FILE then we return ERROR_INVALID_PARAMETER since a
|
|
// directory cannot have this attribute.
|
|
//
|
|
if ( (fFileAttributesChanged) &&
|
|
(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
(dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ) {
|
|
fRet = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto bailout;
|
|
}
|
|
|
|
dwSizeRemaining = sizeof(rgPropPatchHeader) + sizeof(rgPropPatchTrailer) + 8 +
|
|
((fCreationTimeChanged)?(INTERNET_RFC1123_BUFSIZE+
|
|
sizeof(rgCreationTimeTagHeader)+
|
|
sizeof(rgCreationTimeTagTrailer)):0)+
|
|
((fLastAccessTimeChanged)?(INTERNET_RFC1123_BUFSIZE+
|
|
sizeof(rgLastAccessTimeTagHeader)+
|
|
sizeof(rgLastAccessTimeTagTrailer)):0)+
|
|
((fLastModifiedTimeChanged)?(INTERNET_RFC1123_BUFSIZE+
|
|
sizeof(rgLastModifiedTimeTagHeader)+
|
|
sizeof(rgLastModifiedTimeTagTrailer)):0)+
|
|
((fFileAttributesChanged)?(8+sizeof(rgFileAttributesTagHeader)+
|
|
sizeof(rgFileAttributesTagTrailer)):0);
|
|
|
|
|
|
if (dwSizeRemaining > sizeof(Buffer)) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
DavPrint((DEBUG_ERRORS, "DavSetBasicInformation: Insufficient buffer %d %d\n", dwSizeRemaining, sizeof(Buffer)));
|
|
}
|
|
|
|
memset(Buffer, 0, sizeof(Buffer));
|
|
|
|
dwSizeRemaining = sizeof(Buffer);
|
|
|
|
lpTemp = Buffer;
|
|
|
|
memcpy(lpTemp, rgPropPatchHeader, (sizeof(rgPropPatchHeader)-1));
|
|
|
|
lpTemp += (sizeof(rgPropPatchHeader)-1);
|
|
|
|
dwSizeRemaining -= (sizeof(rgPropPatchHeader)-1);
|
|
|
|
dwTemp = dwSizeRemaining;
|
|
|
|
if (fCreationTimeChanged) {
|
|
if (!DavConvertTimeToXml(rgCreationTimeTagHeader, (sizeof(rgCreationTimeTagHeader)-1),
|
|
rgCreationTimeTagTrailer, (sizeof(rgCreationTimeTagTrailer)-1),
|
|
lpCreationTime,
|
|
&lpTemp,
|
|
&dwTemp)) {
|
|
DavPrint((DEBUG_ERRORS, "DavSetBasicInformation: Failed to convert creationtime\n"));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
if (fLastAccessTimeChanged)
|
|
{
|
|
if (!DavConvertTimeToXml(rgLastAccessTimeTagHeader, (sizeof(rgLastAccessTimeTagHeader)-1),
|
|
rgLastAccessTimeTagTrailer, (sizeof(rgLastAccessTimeTagTrailer)-1),
|
|
lpLastAccessTime,
|
|
&lpTemp,
|
|
&dwTemp))
|
|
{
|
|
DavPrint((DEBUG_ERRORS, "DavSetBasicInformation: Failed to convert lastaccesstime\n"));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
if (fLastModifiedTimeChanged)
|
|
{
|
|
if (!DavConvertTimeToXml(rgLastModifiedTimeTagHeader, (sizeof(rgLastModifiedTimeTagHeader)-1),
|
|
rgLastModifiedTimeTagTrailer, (sizeof(rgLastModifiedTimeTagTrailer)-1),
|
|
lpLastModifiedTime,
|
|
&lpTemp,
|
|
&dwTemp))
|
|
{
|
|
DavPrint((DEBUG_ERRORS, "DavSetBasicInformation: Failed to convert lastmodifiedtime\n"));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
if (fFileAttributesChanged)
|
|
{
|
|
memcpy(lpTemp, rgFileAttributesTagHeader, sizeof(rgFileAttributesTagHeader)-1);
|
|
lpTemp += (sizeof(rgFileAttributesTagHeader)-1);
|
|
|
|
sprintf(lpTemp, "%8.8x", dwFileAttributes);
|
|
lpTemp += 8;
|
|
|
|
memcpy(lpTemp, rgFileAttributesTagTrailer, sizeof(rgFileAttributesTagTrailer)-1);
|
|
lpTemp += (sizeof(rgFileAttributesTagTrailer)-1);
|
|
}
|
|
|
|
memcpy(lpTemp, rgPropPatchTrailer, sizeof(rgPropPatchTrailer)-1);
|
|
|
|
dwError = DavSetProperties(DavWorkItem, hDavConnect, PathName, Buffer);
|
|
|
|
fRet = (dwError == ERROR_SUCCESS);
|
|
|
|
if (!fRet) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetBasicInformation/DavSetProperties: dwError = %d\n",
|
|
dwError));
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
bailout:
|
|
|
|
if (!fRet) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetBasicInformation: dwError = %x\n", GetLastError()));
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavConvertTimeToXml(
|
|
IN PCHAR lpTagHeader,
|
|
IN DWORD dwHeaderSize,
|
|
IN PCHAR lpTagTrailer,
|
|
IN DWORD dwTrailerSize,
|
|
IN LARGE_INTEGER *lpTime,
|
|
OUT PCHAR *lplpBuffer,
|
|
IN OUT DWORD *lpdwBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates an xml piece for setting a time property. The format is
|
|
<TagHeader>TimeString in RFC 1123 format<TagTrailer>
|
|
|
|
Arguments:
|
|
|
|
lpTagHeader tag beginning e.g. <Z:Win32CreationTime>
|
|
|
|
dwHeaderSize size of the above header in bytes
|
|
|
|
lpTagTrailer tag end e.g. </Z:Win32CreationTime>
|
|
|
|
dwTrailerSize size of the trailer in bytes
|
|
|
|
lplpBuffer pointer to a buffer pointer. On successful return the pointer is moved ahead.
|
|
|
|
lpdwBufferSize contains the passed in buffersize. On successful return, this value
|
|
is reduced by the amount of space consumed in this routine.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code. If the buffersize is not enough, the
|
|
error code is ERROR_INSUFFICIENT_BUFFER and lpdwBufferSize contains the
|
|
amount necessary to succeed.
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sSystemTime;
|
|
DWORD cbTimeSize;
|
|
CHAR chTimeBuff[INTERNET_RFC1123_BUFSIZE+4], *lpTemp;
|
|
|
|
|
|
if(!FileTimeToSystemTime((FILETIME *)lpTime, &sSystemTime))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(!InternetTimeFromSystemTimeA(&sSystemTime, INTERNET_RFC1123_FORMAT, chTimeBuff, sizeof(chTimeBuff)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
cbTimeSize = strlen(chTimeBuff);
|
|
|
|
if (*lpdwBufferSize < (cbTimeSize + dwHeaderSize + dwTrailerSize))
|
|
{
|
|
*lpdwBufferSize = (cbTimeSize + dwHeaderSize + dwTrailerSize);
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// all is well, start copying
|
|
|
|
lpTemp = *lplpBuffer;
|
|
|
|
// header tag eg: <Z:Win32CreationTime>
|
|
memcpy(lpTemp, lpTagHeader, dwHeaderSize);
|
|
|
|
lpTemp += dwHeaderSize;
|
|
|
|
|
|
// Time in the RFC_1123 format
|
|
memcpy(lpTemp, chTimeBuff, cbTimeSize);
|
|
|
|
lpTemp += cbTimeSize;
|
|
|
|
// trailer tag eg: </Z:Win32CreationTime>
|
|
memcpy(lpTemp, lpTagTrailer, dwTrailerSize);
|
|
|
|
lpTemp += dwTrailerSize;
|
|
|
|
|
|
// adjust the remainign size and the pointers
|
|
*lpdwBufferSize -= (cbTimeSize + dwHeaderSize + dwTrailerSize);
|
|
*lplpBuffer = lpTemp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavParseXmlResponse(
|
|
HINTERNET DavOpenHandle,
|
|
DAV_FILE_ATTRIBUTES *pDavFileAttributesIn,
|
|
DWORD *pNumFileEntries
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the xml response. This is mainly useful for verbs which
|
|
may get back XML response.
|
|
|
|
Arguments:
|
|
|
|
DavOpenHandle - Handle obtained from HttpOpenRequest. A send is already
|
|
issued on this handle.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL ReturnVal, readDone;
|
|
PCHAR DataBuff = NULL;
|
|
DWORD NumRead = 0, NumOfFileEntries = 0, TotalDataBytesRead = 0;
|
|
PVOID Ctx1 = NULL, Ctx2 = NULL;
|
|
DAV_FILE_ATTRIBUTES DavFileAttributes, *pDavFileAttributesLocal = NULL;
|
|
|
|
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, NUM_OF_BYTES_TO_READ);
|
|
if (DataBuff == NULL) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavParseXmlResponse/LocalAlloc: dwError = %08lx\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Read the response and parse it.
|
|
//
|
|
do {
|
|
|
|
ReturnVal = InternetReadFile(DavOpenHandle,
|
|
(LPVOID)DataBuff,
|
|
NUM_OF_BYTES_TO_READ,
|
|
&(NumRead));
|
|
if (!ReturnVal) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavParseXmlResponse/InternetReadFile: dwError = "
|
|
"%08lx\n", dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC, "DavParseXmlResponse: 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) {
|
|
dwError = ERROR_BAD_NET_RESP;
|
|
DavPrint((DEBUG_ERRORS, "DavParseXmlResponse. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
readDone = (NumRead == 0) ? TRUE : FALSE;
|
|
|
|
dwError = DavPushData(DataBuff, &Ctx1, &Ctx2, NumRead, readDone);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavParseXmlResponse/DavPushData."
|
|
" Error Val = %d\n", dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (readDone) {
|
|
break;
|
|
}
|
|
|
|
} while ( TRUE );
|
|
|
|
if (Ctx2) {
|
|
if (pDavFileAttributesIn) {
|
|
pDavFileAttributesLocal = pDavFileAttributesIn;
|
|
} else {
|
|
pDavFileAttributesLocal = &DavFileAttributes;
|
|
}
|
|
memset(pDavFileAttributesLocal, 0, sizeof(DavFileAttributes));
|
|
InitializeListHead(&(pDavFileAttributesLocal->NextEntry));
|
|
dwError = DavParseData(pDavFileAttributesLocal, Ctx1, Ctx2, &NumOfFileEntries);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavParseXmlResponse/DavParseData. "
|
|
"Error Val = %d\n", dwError));
|
|
DavFinalizeFileAttributesList(pDavFileAttributesLocal, FALSE);
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
if (!pDavFileAttributesIn){
|
|
DavFinalizeFileAttributesList(pDavFileAttributesLocal, FALSE);
|
|
}
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
}
|
|
|
|
if (pNumFileEntries){
|
|
*pNumFileEntries = NumOfFileEntries;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (DataBuff) {
|
|
LocalFree(DataBuff);
|
|
DataBuff = NULL;
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavSetProperties(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET hDavConnect,
|
|
LPWSTR lpPathName,
|
|
LPSTR lpPropertiesBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets DAV properties on a file or a directory. It formats an XML requests and sends it
|
|
to the server.
|
|
|
|
Arguments:
|
|
|
|
DavConnectHandle - Server connection.
|
|
|
|
CloseRequest - Usemode close request corresponding to the kernelmode close.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HINTERNET hRequest = NULL;
|
|
BOOL BStatus = FALSE, ReturnVal = FALSE;
|
|
PWCHAR PassportCookie = NULL;
|
|
|
|
//
|
|
// Convert the unicode object name to UTF-8 URL format.
|
|
// Space and other white characters will remain untouched - these should
|
|
// be taken care of by wininet calls.
|
|
// This has to be a W API as the name in CloseRequest is unicode.
|
|
//
|
|
BStatus = DavHttpOpenRequestW(hDavConnect,
|
|
L"PROPPATCH",
|
|
lpPathName,
|
|
L"HTTP/1.1",
|
|
NULL,
|
|
NULL,
|
|
INTERNET_FLAG_KEEP_CONNECTION |
|
|
INTERNET_FLAG_NO_COOKIES |
|
|
INTERNET_FLAG_RELOAD,
|
|
0,
|
|
L"DavSetProperties",
|
|
&hRequest);
|
|
if(BStatus == FALSE) {
|
|
dwError = GetLastError();
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
if (hRequest == NULL) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/DavHttpOpenRequestW. Error Val = %d\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// If OpaqueLockToken is non-NULL, then we need to add this header
|
|
// to the PROPPATCH request being sent out.
|
|
//
|
|
|
|
if (DavWorkItem->WorkItemType == UserModeClose) {
|
|
|
|
PDAV_USERMODE_CLOSE_REQUEST CloseRequest = &(DavWorkItem->CloseRequest);
|
|
|
|
if (CloseRequest->OpaqueLockToken != NULL) {
|
|
ReturnVal = HttpAddRequestHeadersW(hRequest,
|
|
CloseRequest->OpaqueLockToken,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
} else if (DavWorkItem->WorkItemType == UserModeSetFileInformation) {
|
|
|
|
PDAV_USERMODE_SETFILEINFORMATION_REQUEST SetFileInformationRequest = &(DavWorkItem->SetFileInformationRequest);
|
|
|
|
if (SetFileInformationRequest->OpaqueLockToken != NULL) {
|
|
ReturnVal = HttpAddRequestHeadersW(hRequest,
|
|
SetFileInformationRequest->OpaqueLockToken,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We need to add the header "translate:f" to tell IIS that it should
|
|
// allow the user to excecute this VERB on the specified path which it
|
|
// would not allow (in some cases) otherwise. Finally, there is a special
|
|
// flag in the metabase to allow for uploading of "dangerous" content
|
|
// (anything that can be run on the server). This is the ScriptSourceAccess
|
|
// flag in the UI or the AccessSource flag in the metabase. You will need
|
|
// to set this bit to true as well as correct NT ACLs in order to be able
|
|
// to upload .exes or anything executable.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersA(hRequest,
|
|
"translate: f\n",
|
|
-1,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/HttpAddRequestHeadersA. Error Val = %d\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
dwError = DavAttachPassportCookie(DavWorkItem,hRequest,&PassportCookie);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
dwError = DavInternetSetOption(DavWorkItem,hRequest);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
ReturnVal = HttpSendRequestA(hRequest,
|
|
rgXmlHeader,
|
|
strlen(rgXmlHeader),
|
|
(LPVOID)lpPropertiesBuffer,
|
|
strlen(lpPropertiesBuffer));
|
|
if (!ReturnVal) {
|
|
dwError = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/HttpSendRequestA: Error Val = %d\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
dwError = DavQueryAndParseResponse(hRequest);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
SetLastError(dwError);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/DavQueryAndParseResponse: Error Val = %d\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
dwError = DavParseXmlResponse(hRequest, NULL, NULL);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
SetLastError(dwError);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavSetProperties/DavParseXmlResponse: dwError = %d\n",
|
|
dwError));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (hRequest) {
|
|
InternetCloseHandle(hRequest);
|
|
}
|
|
|
|
if (PassportCookie) {
|
|
LocalFree(PassportCookie);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavTestProppatch(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET hDavConnect,
|
|
LPWSTR lpPathName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tests whether DAV properties can be set on this root directory.
|
|
|
|
Arguments:
|
|
|
|
DavConnectHandle - Server connection.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 Error Code.
|
|
|
|
--*/
|
|
{
|
|
CHAR *lpTemp = NULL, Buffer[1024];
|
|
DWORD dwError = ERROR_SUCCESS, dwSizeRemaining, dwTemp;
|
|
|
|
memset(Buffer, 0, sizeof(Buffer));
|
|
|
|
dwSizeRemaining = sizeof(Buffer);
|
|
|
|
lpTemp = Buffer;
|
|
|
|
memcpy(lpTemp, rgPropPatchHeader, (sizeof(rgPropPatchHeader)-1));
|
|
|
|
lpTemp += (sizeof(rgPropPatchHeader)-1);
|
|
|
|
dwSizeRemaining -= (sizeof(rgPropPatchHeader)-1);
|
|
|
|
dwTemp = dwSizeRemaining;
|
|
|
|
memcpy(lpTemp, rgDummyAttributes, sizeof(rgDummyAttributes)-1);
|
|
lpTemp += (sizeof(rgDummyAttributes)-1);
|
|
|
|
memcpy(lpTemp, rgPropPatchTrailer, sizeof(rgPropPatchTrailer)-1);
|
|
|
|
dwError = DavSetProperties(DavWorkItem, hDavConnect, lpPathName, Buffer);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavUnLockTheFileOnTheServer(
|
|
IN PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during create or close when we need to UNLOCK the
|
|
file on the server. We unlock the file when the handle whose create LOCKed
|
|
the file is closed or if the Create failed after LOCKing the file.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The buffer that contains the request parameters and options.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error code.
|
|
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
|
|
PWCHAR LockTokenHeader = NULL, PassportCookie = NULL, UnLockPathName = NULL;
|
|
PWCHAR OpaqueLockToken = NULL;
|
|
ULONG LockTokenHeaderLengthInBytes = 0;
|
|
BOOL BStatus = FALSE, ReturnVal = FALSE;
|
|
PDAV_USERMODE_CLOSE_REQUEST CloseRequest = NULL;
|
|
PDAV_USERMODE_CREATE_RESPONSE CreateResponse = NULL;
|
|
|
|
//
|
|
// An UNLOCK request can come from Create or Close Paths.
|
|
// 1. If the Create call in the usermode fails after the file has been
|
|
// LOCKed, it is UNLOCKed before the call is completed.
|
|
// 2. A file that was LOCKed on Create is UNLOCKed when the handle is
|
|
// closed.
|
|
//
|
|
|
|
if (DavWorkItem->WorkItemType == UserModeCreate) {
|
|
CreateResponse = &(DavWorkItem->CreateResponse);
|
|
DavConnHandle = DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle;
|
|
UnLockPathName = DavWorkItem->AsyncCreate.RemPathName;
|
|
OpaqueLockToken = CreateResponse->OpaqueLockToken;
|
|
} else {
|
|
CloseRequest = &(DavWorkItem->CloseRequest);
|
|
ASSERT(DavWorkItem->WorkItemType == UserModeClose);
|
|
ASSERT(CloseRequest->OpaqueLockToken != NULL);
|
|
DavConnHandle = DavWorkItem->AsyncClose.PerUserEntry->DavConnHandle;
|
|
UnLockPathName = &(CloseRequest->PathName[1]);
|
|
OpaqueLockToken = CloseRequest->OpaqueLockToken;
|
|
}
|
|
|
|
//
|
|
// Convert the unicode object name to UTF-8 URL format.
|
|
// Space and other white characters will remain untouched - these should
|
|
// be taken care of by wininet calls.
|
|
// This has to be a W API as the name in CloseRequest is unicode.
|
|
//
|
|
BStatus = DavHttpOpenRequestW(DavConnHandle,
|
|
L"UNLOCK",
|
|
UnLockPathName,
|
|
L"HTTP/1.1",
|
|
NULL,
|
|
NULL,
|
|
INTERNET_FLAG_KEEP_CONNECTION |
|
|
INTERNET_FLAG_NO_COOKIES |
|
|
INTERNET_FLAG_RELOAD,
|
|
0,
|
|
L"DavUnLockTheFileOnTheServer",
|
|
&DavOpenHandle);
|
|
if(BStatus == FALSE) {
|
|
WStatus = GetLastError();
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
if (DavOpenHandle == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/DavHttpOpenRequestW. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// We need to add the header "translate:f" to tell IIS that it should
|
|
// allow the user to excecute this VERB on the specified path which it
|
|
// would not allow (in some cases) otherwise. Finally, there is a special
|
|
// flag in the metabase to allow for uploading of "dangerous" content
|
|
// (anything that can be run on the server). This is the ScriptSourceAccess
|
|
// flag in the UI or the AccessSource flag in the metabase. You will need
|
|
// to set this bit to true as well as correct NT ACLs in order to be able
|
|
// to upload .exes or anything executable. We set this header on all requests
|
|
// that are sent to the server including the UNLOCK request.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"translate: f\n",
|
|
-1,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/HttpAddRequestHeadersW. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
WStatus = DavAttachPassportCookie(DavWorkItem, DavOpenHandle, &PassportCookie);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/DavAttachPassportCookie. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
WStatus = DavInternetSetOption(DavWorkItem, DavOpenHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/DavInternetSetOption. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
LockTokenHeaderLengthInBytes = (1 + wcslen(L"Lock-Token: ")) * sizeof(WCHAR);
|
|
|
|
if (DavWorkItem->WorkItemType == UserModeCreate) {
|
|
|
|
//
|
|
// CreateResponse->OpaqueLockToken has the following format.
|
|
// <opaquelocktoken:sdfsadfsdfdsfd.....>
|
|
// and we need to create a header of the following format.
|
|
// Lock-Token: <opaquelocktoken:sdfsadfsdfdsfd.....>
|
|
//
|
|
|
|
LockTokenHeaderLengthInBytes += (wcslen(OpaqueLockToken)) * sizeof(WCHAR);
|
|
|
|
LockTokenHeader = LocalAlloc(LPTR, LockTokenHeaderLengthInBytes);
|
|
if (LockTokenHeader == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
wcsncpy(LockTokenHeader, L"Lock-Token: ", wcslen(L"Lock-Token: "));
|
|
|
|
wcsncpy((LockTokenHeader + wcslen(L"Lock-Token: ")),
|
|
OpaqueLockToken,
|
|
wcslen(OpaqueLockToken));
|
|
|
|
} else {
|
|
|
|
//
|
|
// CloseRequest->OpaqueLockToken has the following format.
|
|
// If: (<opaquelocktoken:sdfsadfsdfdsfd.....>)
|
|
// and we need to create a header of the following format.
|
|
// Lock-Token: <opaquelocktoken:sdfsadfsdfdsfd.....>
|
|
//
|
|
|
|
//
|
|
// We don't need first 5 chars "If: (" and the last ")" char. So we subtract
|
|
// 6 from the total length of CloseRequest->OpaqueLockToken.
|
|
//
|
|
LockTokenHeaderLengthInBytes += (wcslen(OpaqueLockToken) - 6) * sizeof(WCHAR);
|
|
|
|
LockTokenHeader = LocalAlloc(LPTR, LockTokenHeaderLengthInBytes);
|
|
if (LockTokenHeader == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
wcsncpy(LockTokenHeader, L"Lock-Token: ", wcslen(L"Lock-Token: "));
|
|
|
|
wcsncpy((LockTokenHeader + wcslen(L"Lock-Token: ")),
|
|
(OpaqueLockToken + wcslen(L"If: (")),
|
|
(wcslen(OpaqueLockToken) - 6));
|
|
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavUnLockTheFileOnTheServer: LockTokenHeader = %ws\n",
|
|
LockTokenHeader));
|
|
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
LockTokenHeader,
|
|
-1,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/HttpAddRequestHeadersW. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RESEND_THE_REQUEST:
|
|
|
|
ReturnVal = HttpSendRequestExW(DavOpenHandle,
|
|
NULL,
|
|
NULL,
|
|
HSR_SYNC,
|
|
(ULONG_PTR)0);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/HttpSendRequestExW. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
ReturnVal = HttpEndRequestW(DavOpenHandle,
|
|
NULL,
|
|
HSR_SYNC,
|
|
(ULONG_PTR)0);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
//
|
|
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then WinInet
|
|
// is trying to authenticate itself with the server. If we get back
|
|
// ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, WinInet is expecting us to
|
|
// confirm that the redirect needs to be followed. In these scenarios,
|
|
// we need to repeat the HttpSend and HttpEnd request calls.
|
|
//
|
|
if (WStatus == ERROR_INTERNET_FORCE_RETRY || WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) {
|
|
goto RESEND_THE_REQUEST;
|
|
}
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/HttpEndRequestW. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
WStatus = DavQueryAndParseResponse(DavOpenHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavUnLockTheFileOnTheServer/DavQueryAndParseResponse. WStatus = %d\n",
|
|
WStatus));
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (DavOpenHandle != NULL) {
|
|
InternetCloseHandle(DavOpenHandle);
|
|
DavOpenHandle = NULL;
|
|
}
|
|
|
|
if (PassportCookie) {
|
|
LocalFree(PassportCookie);
|
|
PassportCookie = NULL;
|
|
}
|
|
|
|
if (LockTokenHeader) {
|
|
LocalFree(LockTokenHeader);
|
|
LockTokenHeader = NULL;
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|