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.
1489 lines
43 KiB
1489 lines
43 KiB
/*++
|
|
|
|
Copyright (c) 1996-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Qfs.c
|
|
|
|
Abstract:
|
|
|
|
Redirection layer for quorum access
|
|
|
|
Author:
|
|
|
|
GorN 19-Sep-2001
|
|
|
|
Revision History:
|
|
|
|
TODO:
|
|
Support more than one Qfs provider
|
|
|
|
--*/
|
|
|
|
#define QFS_DO_NOT_UNMAP_WIN32 // get access to regular CreateFile, etc
|
|
|
|
#ifndef DUMB_CLIENT
|
|
#include "service.h"
|
|
#endif
|
|
#include "QfsTrans.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <Imagehlp.h>
|
|
|
|
#ifndef min
|
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
////////////////// Debug Junk //////////////////
|
|
|
|
int QfsLogLevel = 0;
|
|
|
|
void
|
|
debug_log(char *format, ...)
|
|
{
|
|
va_list marker;
|
|
|
|
va_start(marker, format);
|
|
|
|
#ifdef DUMB_CLIENT
|
|
if (QfsLogLevel > 2) {
|
|
printf("%d:%x:",GetTickCount(), GetCurrentThreadId());
|
|
vprintf(format, marker);
|
|
}
|
|
#else
|
|
{
|
|
char buf[1024];
|
|
vsprintf(buf, format, marker);
|
|
ClRtlLogPrint(LOG_NOISE, "%1!hs!\r\n",buf);
|
|
}
|
|
#endif
|
|
|
|
va_end(marker);
|
|
|
|
}
|
|
|
|
void
|
|
error_log(char *format, ...)
|
|
{
|
|
va_list marker;
|
|
|
|
va_start(marker, format);
|
|
|
|
#ifdef DUMB_CLIENT
|
|
if (QfsLogLevel > 0) {
|
|
printf("*E %d:%x:",GetTickCount(), GetCurrentThreadId());
|
|
vprintf(format, marker);
|
|
}
|
|
#else
|
|
{
|
|
char buf[1024];
|
|
vsprintf(buf, format, marker);
|
|
ClRtlLogPrint(LOG_ERROR, "%1!hs!\r\n",buf);
|
|
}
|
|
#endif
|
|
|
|
va_end(marker);
|
|
|
|
}
|
|
|
|
#ifndef QfsError
|
|
# define QfsError(x) error_log x
|
|
#endif
|
|
#ifndef QfsNoise
|
|
# define QfsNoise(x) debug_log x
|
|
#endif
|
|
|
|
// When give a UNS path that looks like a Qfs path
|
|
// we contact the Qfs server and query whether it
|
|
// recognizes that path. If it is, we cache this recognized
|
|
// path in QfsPath veriable, so that next time we
|
|
// can immediately pass the request coming to this path to Qfs
|
|
|
|
WCHAR QfsPath[MAX_PATH];
|
|
UINT ccQfsPath = 0;
|
|
CRITICAL_SECTION QfsCriticalSection;
|
|
SHARED_MEM_SERVER Client;
|
|
|
|
VOID QfsInitialize()
|
|
{
|
|
InitializeCriticalSection(&QfsCriticalSection);
|
|
MemClient_Init(&Client);
|
|
}
|
|
|
|
VOID QfsCleanup()
|
|
{
|
|
MemClient_Cleanup(&Client);
|
|
DeleteCriticalSection(&QfsCriticalSection);
|
|
}
|
|
|
|
#define AcquireExclusive() EnterCriticalSection(&QfsCriticalSection)
|
|
#define ReleaseExclusive() LeaveCriticalSection(&QfsCriticalSection)
|
|
|
|
#define UpgradeToExclusive() (0)
|
|
|
|
#define AcquireShared() EnterCriticalSection(&QfsCriticalSection)
|
|
#define ReleaseShared() LeaveCriticalSection(&QfsCriticalSection)
|
|
|
|
// the whole transport interface is incapsulated in
|
|
// three functions
|
|
// ReserveBuffer, DeliverBuffer and RelaseBuffer
|
|
// The pattern of usage is
|
|
//
|
|
// ReserveBuffer(Operation, Path or Handle)
|
|
// [gets a pointer to a job buffer if Path or Handle belong to Qfs]
|
|
// Copy in parameters to a buffer
|
|
// DeliverBuffer
|
|
// Copy out parameters from a buffer
|
|
// ReleaseBuffer
|
|
|
|
DWORD QfspReserveBuffer(
|
|
DWORD OpCode,
|
|
LPCWSTR FileName,
|
|
QfsHANDLE* HandlePtr,
|
|
PJOB_BUF *pj);
|
|
|
|
BOOL QfspDeliverBuffer(
|
|
PJOB_BUF j,
|
|
DWORD* Status);
|
|
|
|
void QfspReleaseBuffer(
|
|
PJOB_BUF j);
|
|
|
|
BOOL QfspDeliverBufferInternal(
|
|
LPWSTR PipeName,
|
|
LPVOID buf,
|
|
DWORD len,
|
|
DWORD timeout,
|
|
DWORD* Status
|
|
);
|
|
|
|
DWORD QfspReserveBufferNoChecks(
|
|
DWORD OpCode,
|
|
LPCWSTR FileName,
|
|
QfsHANDLE* HandlePtr,
|
|
PJOB_BUF *pj);
|
|
|
|
LPCWSTR SkipUncPrefix(
|
|
IN LPCWSTR p,
|
|
OUT LPBOOL bIsShareName)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the passed string looks like \\?\unc, or \\ strip the prefix and set pIsShareName to true
|
|
|
|
Outputs:
|
|
|
|
bIsShareName - set to TRUE if the path looks like UNC path and to FALSE otherwise
|
|
|
|
Returns:
|
|
|
|
The path without \\?\unc or \\ prefix, if it is a UNC path, otherwise returns p
|
|
|
|
--*/
|
|
{
|
|
if (p[0] == '\\' && p[1] == '\\') {
|
|
*bIsShareName = TRUE;
|
|
p += 2;
|
|
if (p[0]=='?' && p[1]=='\\' && p[2]=='U' && p[3]=='N' && p[4]=='C' && p[5] == '\\') {
|
|
p += 6;
|
|
if (p[0] != 0 && p[1]==':') {
|
|
p += 2;
|
|
if (p[0] == '\\') {
|
|
++p;
|
|
}
|
|
*bIsShareName = FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
*bIsShareName = FALSE;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
BOOL IsQfsPath(LPCWSTR Path)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks whether the path looks like a QfsPath
|
|
|
|
This routine has fast and slow path.
|
|
If QfsPath is set and is a valid prefix of Path, the function immediately returns
|
|
Otherwise, it delivers opConnect request to the QfsServer to verify that it can
|
|
handle this path.
|
|
|
|
If QfsServer is not running, it will get a connection failure
|
|
If QfsServer is up and recognizes the path, we set QfsPath, so that we don't
|
|
have to do all this when we called next time for a similar path
|
|
|
|
Inputs:
|
|
|
|
Path,
|
|
QfsPath global
|
|
|
|
Side effects:
|
|
|
|
Sets QfsPath if succesfully talked to Qfs server.
|
|
|
|
--*/
|
|
{
|
|
BOOL IsShare;
|
|
PWCHAR p;
|
|
WCHAR shareName[MAX_PATH];
|
|
JobBuf_t *j;
|
|
SIZE_T len;
|
|
DWORD Status=ERROR_NO_MATCH;
|
|
|
|
Path = SkipUncPrefix(Path, &IsShare);
|
|
if (!IsShare) {
|
|
SetLastError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
AcquireShared();
|
|
if (ccQfsPath) {
|
|
if (wcsncmp(Path, QfsPath, ccQfsPath) == 0) {
|
|
ReleaseShared();
|
|
return TRUE;
|
|
}
|
|
}
|
|
ReleaseShared();
|
|
|
|
p = wcschr(Path, '$');
|
|
if (p == NULL || p[1] !='\\' ) {
|
|
SetLastError(Status);
|
|
return FALSE;
|
|
}
|
|
len = p - Path + 2;
|
|
if (len+10 >= MAX_PATH) {
|
|
SetLastError(Status);
|
|
return FALSE;
|
|
}
|
|
CopyMemory(shareName, Path, (len) * sizeof(WCHAR));
|
|
shareName[len] = 0;
|
|
|
|
// We are trying to connect to MNS now to verify the share path. We need to zero
|
|
// out the length field (ccQfsPath). Look at QfspCopyPath().
|
|
//
|
|
AcquireExclusive();
|
|
ccQfsPath = 0;
|
|
ReleaseExclusive();
|
|
|
|
Status = QfspReserveBufferNoChecks(opConnect, shareName, 0, &j);
|
|
if (Status != ERROR_SUCCESS) {
|
|
SetLastError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the clussvc process ID.
|
|
j->ClussvcProcessId = GetCurrentProcessId();
|
|
QfspDeliverBuffer(j,&Status);
|
|
QfspReleaseBuffer(j);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
AcquireExclusive();
|
|
wcscpy(QfsPath, shareName);
|
|
ccQfsPath = (DWORD)len;
|
|
ReleaseExclusive();
|
|
|
|
QfsNoise(("[Qfs] QfsPath %ws", QfsPath));
|
|
// need to update
|
|
} else {
|
|
SetLastError(Status);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// QfsINVALID_HANDLE_VALUE is to be used in place of INVALID_HANDLE_VALUE
|
|
// to initalize hadle of QfsHANDLE type
|
|
|
|
QfsHANDLE QfsINVALID_HANDLE_VALUE = {INVALID_HANDLE_VALUE, 0};
|
|
|
|
#define HandlePtr(x) (&(x))
|
|
|
|
BOOL IsQfsHandle(QfsHANDLE handle)
|
|
{
|
|
return handle.IsQfs;
|
|
}
|
|
|
|
HANDLE GetRealHandle(QfsHANDLE QfsHandle)
|
|
{
|
|
return QfsHandle.realHandle;
|
|
}
|
|
|
|
QfsHANDLE MakeQfsHandle(HANDLE handle)
|
|
{
|
|
QfsHANDLE result;
|
|
result.IsQfs = 1;
|
|
result.realHandle = handle;
|
|
return result;
|
|
}
|
|
|
|
QfsHANDLE MakeWin32Handle(HANDLE handle)
|
|
{
|
|
QfsHANDLE result;
|
|
result.IsQfs = 0;
|
|
result.realHandle = handle;
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
#undef malloc
|
|
#undef free
|
|
|
|
#define malloc(dwBytes) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytes)
|
|
#define free(hHeap) HeapFree(GetProcessHeap(), 0, hHeap)
|
|
|
|
DWORD QfspCopyPath(
|
|
OUT LPVOID Buf,
|
|
IN DWORD BufSize,
|
|
IN LPCWSTR FileName)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy path without QfsPath prefix.
|
|
Ie it will take:
|
|
|
|
\\.\UNC\12378\234-79879-87987$\a\b and transforms it into \a\b
|
|
|
|
Sets QfsPath if succesfully talked to Qfs server.
|
|
|
|
WARNING: It is a coller responsibility to make sure that the buffer
|
|
is large enough to fit the filename
|
|
|
|
--*/
|
|
{
|
|
BOOL bIsShare;
|
|
DWORD cbLen;
|
|
|
|
FileName = SkipUncPrefix(FileName, &bIsShare) + ccQfsPath;
|
|
cbLen = sizeof(WCHAR) * (wcslen(FileName)+1);
|
|
|
|
if (cbLen > BufSize) {
|
|
return ERROR_BAD_PATHNAME;
|
|
}
|
|
CopyMemory(Buf, FileName, cbLen);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD QfspReserveBufferNoChecks(
|
|
DWORD OpCode,
|
|
LPCWSTR FileName,
|
|
QfsHANDLE* HandlePtr,
|
|
PJOB_BUF *pj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares a job buffer.
|
|
|
|
Sets the OpCode, copies FileName and Handle if present
|
|
|
|
Output:
|
|
|
|
If the operation is successful, the pointer to a job buffer is returned in *pj
|
|
|
|
--*/
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD status;
|
|
|
|
status = MemClient_ReserveBuffer(&Client, &j);
|
|
if (status != ERROR_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
j->hdr.OpCode = OpCode;
|
|
if (HandlePtr) {
|
|
j->Handle = GetRealHandle(*HandlePtr);
|
|
}
|
|
if (FileName) {
|
|
status = QfspCopyPath(j->Buffer, sizeof(j->Buffer), FileName);
|
|
if (status != ERROR_SUCCESS) {
|
|
free(j);
|
|
return status;
|
|
}
|
|
}
|
|
*pj = j;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD QfspReserveBuffer(
|
|
DWORD OpCode,
|
|
LPCWSTR FileName,
|
|
QfsHANDLE* HandlePtr,
|
|
PJOB_BUF *pj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares a job buffer.
|
|
|
|
Sets the OpCode, copies FileName and Handle if present
|
|
|
|
Return codes:
|
|
|
|
ERROR_NO_MATCH: the handle or path do not belong to Qfs,
|
|
the caller needs to use regular Win32 i/o APIs
|
|
|
|
--*/
|
|
{
|
|
if(HandlePtr && GetRealHandle(*HandlePtr) == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
|
|
if(FileName && !IsQfsPath(FileName)) return ERROR_NO_MATCH;
|
|
if(HandlePtr && !IsQfsHandle(*HandlePtr)) return ERROR_NO_MATCH;
|
|
|
|
return QfspReserveBufferNoChecks(OpCode, FileName, HandlePtr, pj);
|
|
}
|
|
|
|
BOOL QfspDeliverBuffer(
|
|
PJOB_BUF j,
|
|
DWORD* Status)
|
|
{
|
|
*Status = MemClient_DeliverBuffer(j);
|
|
if (*Status == ERROR_SUCCESS) {
|
|
*Status = j->hdr.Status;
|
|
}
|
|
return *Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
void QfspReleaseBuffer(
|
|
PJOB_BUF j)
|
|
{
|
|
MemClient_Release(j);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Redirection shims, most of them follow the following pattern
|
|
//
|
|
// ReserveBuffer(Operation, Path or Handle)
|
|
// [gets a pointer to a job buffer if Path or Handle belong to Qfs]
|
|
// Copy in parameters to a buffer
|
|
// DeliverBuffer
|
|
// Copy out parameters from a buffer
|
|
// ReleaseBuffer
|
|
//
|
|
// If ReserveBuffer failed with error NO_MATCH, calls regular Win32 API
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#define StatusFromBool(expr) (Status = (expr)?ERROR_SUCCESS:GetLastError())
|
|
#define BoolToStatus(expr) StatusFromBool(expr)
|
|
#define StatusFromHandle(expr) (Status = ((expr) != INVALID_HANDLE_VALUE)?ERROR_SUCCESS:GetLastError())
|
|
|
|
BOOL QfsCloseHandle(
|
|
QfsHANDLE hObject // handle to object
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opCloseFile, NULL, HandlePtr(hObject), &j);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Status = CloseHandle(GetRealHandle(hObject))?ERROR_SUCCESS:GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsCloseHandle %x, status %d", GetRealHandle(hObject), Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD QfspRemapCreateFileStatus(DWORD Status, DWORD DispReq, DWORD DispAct)
|
|
{
|
|
if (Status == ERROR_ALREADY_EXISTS && DispReq == CREATE_NEW)
|
|
return ERROR_FILE_EXISTS;
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
return Status;
|
|
|
|
if (DispAct != OPEN_EXISTING)
|
|
return Status;
|
|
|
|
if (DispReq == CREATE_ALWAYS || DispReq == OPEN_ALWAYS)
|
|
return ERROR_ALREADY_EXISTS;
|
|
|
|
return Status;
|
|
}
|
|
|
|
QfsHANDLE QfsCreateFile(
|
|
LPCWSTR lpFileName, // file name
|
|
DWORD dwDesiredAccess, // access mode
|
|
DWORD dwShareMode, // share mode
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
|
|
DWORD dwCreationDisposition, // how to create
|
|
DWORD dwFlagsAndAttributes, // file attributes
|
|
HANDLE hTemplateFile // handle to template file
|
|
)
|
|
{
|
|
QfsHANDLE Result=QfsINVALID_HANDLE_VALUE;
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opCreateFile, lpFileName, NULL, &j);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
j->dwDesiredAccess = dwDesiredAccess;
|
|
j->dwShareMode = dwShareMode;
|
|
j->dwCreationDisposition = dwCreationDisposition;
|
|
j->dwFlagsAndAttributes = dwFlagsAndAttributes;
|
|
|
|
if( QfspDeliverBuffer(j, &Status) ) {
|
|
Result = MakeQfsHandle(j->Handle);
|
|
} else {
|
|
Result = QfsINVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
Status = QfspRemapCreateFileStatus(Status,
|
|
dwCreationDisposition, j->dwCreationDisposition);
|
|
|
|
dwCreationDisposition = j->dwCreationDisposition;
|
|
QfspReleaseBuffer(j);
|
|
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
|
|
Result = MakeWin32Handle(
|
|
CreateFile(
|
|
lpFileName, dwDesiredAccess, dwShareMode,
|
|
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile)
|
|
);
|
|
|
|
Status = GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsOpenFile %ws => %x, %x status %d", lpFileName, dwCreationDisposition, Result, Status));
|
|
SetLastError(Status);
|
|
return Result;
|
|
}
|
|
|
|
|
|
// little helper structure that simplifies printing a sample of
|
|
// a buffer data for debugging
|
|
|
|
typedef struct _sig { char sig[5]; } SIG;
|
|
|
|
SIG Prefix(LPCVOID lpBuffer) {
|
|
char* p = (char*)lpBuffer;
|
|
SIG Result = {"...."};
|
|
if (isalpha(p[0])) Result.sig[0] = p[0];
|
|
if (isalpha(p[1])) Result.sig[1] = p[1];
|
|
if (isalpha(p[2])) Result.sig[2] = p[2];
|
|
if (isalpha(p[3])) Result.sig[3] = p[3];
|
|
return Result;
|
|
}
|
|
|
|
|
|
// NOWHOW MNS interprets WriteFile with Size zero as SetEndOfFile //
|
|
BOOL QfsWriteFile(
|
|
QfsHANDLE hFile, // handle to file
|
|
LPCVOID lpBuffer, // data buffer
|
|
DWORD nNumberOfBytesToWrite, // number of bytes to write
|
|
LPDWORD lpNumberOfBytesWritten, // number of bytes written
|
|
LPOVERLAPPED lpOverlapped // overlapped buffer
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
ULONG PreOffset = 0, PostOffset = 0;
|
|
DWORD Status = QfspReserveBuffer(opWriteFile, NULL, HandlePtr(hFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
DWORD nRemainingBytes = nNumberOfBytesToWrite;
|
|
const char* BufferWalker = lpBuffer;
|
|
|
|
if (lpOverlapped) {
|
|
j->Offset = lpOverlapped->Offset;
|
|
} else {
|
|
j->Offset = ~0; // use file pointer
|
|
}
|
|
PreOffset = (ULONG)j->Offset;
|
|
do {
|
|
j->cbSize = (USHORT)min(JOB_BUF_MAX_BUFFER, nRemainingBytes) ;
|
|
CopyMemory(j->Buffer, BufferWalker, j->cbSize);
|
|
if (QfspDeliverBuffer(j, &Status)) {
|
|
nRemainingBytes -= j->cbSize;
|
|
BufferWalker += j->cbSize;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (nRemainingBytes > 0 && j->cbSize > 0);
|
|
if (lpNumberOfBytesWritten) {
|
|
*lpNumberOfBytesWritten = nNumberOfBytesToWrite - nRemainingBytes;
|
|
}
|
|
PostOffset = (ULONG)j->Offset;
|
|
QfspReleaseBuffer(j);
|
|
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
if(WriteFile(GetRealHandle(hFile), lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped)) {
|
|
Status = ERROR_SUCCESS;
|
|
} else {
|
|
Status = GetLastError();
|
|
}
|
|
}
|
|
QfsNoise(("[Qfs] WriteFile %x (%s) %d, status %d (%d=>%d)", GetRealHandle(hFile), Prefix(lpBuffer).sig,
|
|
nNumberOfBytesToWrite, Status, PreOffset, PostOffset));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsReadFile(
|
|
QfsHANDLE hFile, // handle to file
|
|
LPVOID lpBuffer, // data buffer
|
|
DWORD nNumberOfBytesToRead, // number of bytes to read
|
|
LPDWORD lpNumberOfBytesRead, // number of bytes read
|
|
LPOVERLAPPED lpOverlapped // overlapped buffer
|
|
)
|
|
{
|
|
PJOB_BUF j; ULONG PreOffset = 0, PostOffset = 0;
|
|
DWORD Status = QfspReserveBuffer(opReadFile, NULL, HandlePtr(hFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
DWORD nRemainingBytes = nNumberOfBytesToRead;
|
|
PCHAR BufferWalker = lpBuffer;
|
|
|
|
if (lpOverlapped) {
|
|
j->Offset = lpOverlapped->Offset;
|
|
} else {
|
|
j->Offset = (ULONGLONG)-1; // use file pointer
|
|
}
|
|
|
|
PreOffset = (ULONG)j->Offset;
|
|
do {
|
|
j->cbSize = (USHORT)min(JOB_BUF_MAX_BUFFER, nRemainingBytes);
|
|
if (QfspDeliverBuffer(j, &Status)) {
|
|
CopyMemory(BufferWalker, j->Buffer, j->cbSize);
|
|
nRemainingBytes -= j->cbSize;
|
|
BufferWalker += j->cbSize;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (nRemainingBytes > 0 && j->cbSize > 0);
|
|
if (lpNumberOfBytesRead) {
|
|
*lpNumberOfBytesRead = nNumberOfBytesToRead - nRemainingBytes;
|
|
}
|
|
PostOffset = (ULONG)j->Offset;
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
if(ReadFile(GetRealHandle(hFile), lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped)) {
|
|
Status = ERROR_SUCCESS;
|
|
} else {
|
|
Status = GetLastError();
|
|
}
|
|
}
|
|
QfsNoise(("[Qfs] ReadFile %x (%s) %d %d, (%d=>%d) %x status %d",
|
|
GetRealHandle(hFile), &Prefix(lpBuffer).sig,
|
|
nNumberOfBytesToRead, *lpNumberOfBytesRead,
|
|
PreOffset, PostOffset, lpOverlapped, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsFlushFileBuffers(
|
|
QfsHANDLE hFile // handle to file
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opFlushFile, NULL, HandlePtr(hFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Status = FlushFileBuffers(GetRealHandle(hFile))?ERROR_SUCCESS:GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsFlushBuffers %x, status %d", GetRealHandle(hFile), Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsDeleteFile(
|
|
LPCTSTR lpFileName
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opDeleteFile, lpFileName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Status = DeleteFile(lpFileName)?ERROR_SUCCESS:GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsDeleteFile %ws, status %d", lpFileName, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsRemoveDirectory(
|
|
LPCTSTR lpFileName
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opDeleteFile, lpFileName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Status = RemoveDirectory(lpFileName)?ERROR_SUCCESS:GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsRemoveDirectory %ws, status %d", lpFileName, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
QfsHANDLE QfsFindFirstFile(
|
|
LPCWSTR lpFileName, // file name
|
|
LPWIN32_FIND_DATA lpFindFileData // data buffer
|
|
)
|
|
{
|
|
QfsHANDLE Result=QfsINVALID_HANDLE_VALUE;
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opFindFirstFile, lpFileName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
if(QfspDeliverBuffer(j, &Status)) {
|
|
Result = MakeQfsHandle(j->Handle);
|
|
*lpFindFileData = j->FindFileData;
|
|
} else {
|
|
Result = QfsINVALID_HANDLE_VALUE;
|
|
}
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Result = MakeWin32Handle(
|
|
FindFirstFile(lpFileName, lpFindFileData)
|
|
);
|
|
Status = QfsIsHandleValid(Result)?ERROR_SUCCESS:GetLastError();
|
|
}
|
|
QfsNoise(("[Qfs] QfsFindFirstFile %ws => %x, error %d", lpFileName, GetRealHandle(Result), Status));
|
|
SetLastError(Status);
|
|
return Result;
|
|
}
|
|
|
|
BOOL QfsFindNextFile(
|
|
QfsHANDLE hFindFile, // search handle
|
|
LPWIN32_FIND_DATA lpFindFileData // data buffer
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opFindNextFile, NULL, HandlePtr(hFindFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
if(QfspDeliverBuffer(j, &Status)) {
|
|
*lpFindFileData = j->FindFileData;
|
|
}
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( FindNextFile(GetRealHandle(hFindFile), lpFindFileData) );
|
|
}
|
|
QfsNoise(("[Qfs] QfsFindNextFile %x", GetRealHandle(hFindFile)));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsFindClose(
|
|
QfsHANDLE hFindFile // file search handle
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opFindClose, NULL, HandlePtr(hFindFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( FindClose(GetRealHandle(hFindFile)) );
|
|
}
|
|
QfsNoise(("[Qfs] QfsFindClose %x", GetRealHandle(hFindFile)));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsCreateDirectory(
|
|
LPCWSTR lpPathName, // directory name
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opCreateDir, lpPathName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( CreateDirectory(lpPathName, lpSecurityAttributes) );
|
|
}
|
|
QfsNoise(("[Qfs] QfsCreateDirectory %ws, status %d", lpPathName, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsGetDiskFreeSpaceEx(
|
|
LPCTSTR lpDirectoryName, // directory name
|
|
PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
|
|
PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opGetDiskFreeSpace, lpDirectoryName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
if(QfspDeliverBuffer(j, &Status)) {
|
|
lpFreeBytesAvailable->QuadPart = j->FreeBytesAvailable;
|
|
lpTotalNumberOfBytes->QuadPart = j->TotalNumberOfBytes;
|
|
if (lpTotalNumberOfFreeBytes) {
|
|
lpTotalNumberOfFreeBytes->QuadPart = j->TotalNumberOfFreeBytes;
|
|
}
|
|
}
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( GetDiskFreeSpaceEx(lpDirectoryName, lpFreeBytesAvailable,
|
|
lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes) );
|
|
}
|
|
QfsNoise(("[Qfs] GetDiskFreeSpaceEx %ws, status %d", lpDirectoryName, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsGetFileSizeEx(
|
|
QfsHANDLE hFile, // handle to file
|
|
PLARGE_INTEGER lpFileSize) // file size
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opGetAttr, NULL, HandlePtr(hFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
if(QfspDeliverBuffer(j, &Status)) {
|
|
lpFileSize->QuadPart = j->EndOfFile;
|
|
}
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( GetFileSizeEx(GetRealHandle(hFile), lpFileSize) );
|
|
}
|
|
QfsNoise(("[Qfs] QfsGetFileSize %x %I64d", GetRealHandle(hFile), lpFileSize->QuadPart));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD QfsGetFileSize(
|
|
QfsHANDLE hFile, // handle to file
|
|
LPDWORD lpFileSizeHigh) // high-order word of file size
|
|
{
|
|
LARGE_INTEGER Li;
|
|
if ( QfsGetFileSizeEx(hFile,&Li) ) {
|
|
if ( lpFileSizeHigh ) {
|
|
*lpFileSizeHigh = (DWORD)Li.HighPart;
|
|
}
|
|
if (Li.LowPart == -1 ) {
|
|
SetLastError(0);
|
|
}
|
|
} else {
|
|
Li.LowPart = -1;
|
|
}
|
|
return Li.LowPart;
|
|
}
|
|
|
|
// NOWHOW MNS interprets WriteFile with Size zero as SetEndOfFile //
|
|
DWORD QfsSetEndOfFile(
|
|
QfsHANDLE hFile,
|
|
LONGLONG Offset
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opWriteFile, NULL, HandlePtr(hFile), &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
j->Offset = Offset;
|
|
j->cbSize = 0;
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
Status = SetFilePointerEx(GetRealHandle(hFile), *(PLARGE_INTEGER)&Offset, NULL, FILE_BEGIN);
|
|
if (Status != 0xFFFFFFFF && SetEndOfFile(GetRealHandle(hFile))) {
|
|
Status = ERROR_SUCCESS;
|
|
} else {
|
|
Status = GetLastError();
|
|
}
|
|
}
|
|
QfsNoise(("[Qfs] QfsSetEndOfFile %x %d, Status %d", GetRealHandle(hFile), Offset, Status));
|
|
return Status;
|
|
}
|
|
|
|
DWORD QfsIsOnline(
|
|
IN LPCWSTR Path,
|
|
OUT BOOL *pfOnline
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status=ERROR_NO_MATCH;
|
|
|
|
if (IsQfsPath(Path)) {
|
|
// This is a MNS path.
|
|
// Try to perform null operation on the server.
|
|
//
|
|
Status = QfspReserveBufferNoChecks(opNone, NULL, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
*pfOnline = QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
}
|
|
else {
|
|
*pfOnline = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
// Cases:
|
|
// 1. If this is not MNS path, should return ERROR_NO_MATCH.
|
|
// 2. If this is MNS path, but MNS not online, should return some other error value.
|
|
//
|
|
// Soln: IsQfsPath() now returns the error value through SetLastError().
|
|
//
|
|
Status = GetLastError();
|
|
*pfOnline = FALSE;
|
|
}
|
|
|
|
QfsNoise(("[Qfs] QfsIsOnline => %d, Status %d", *pfOnline, Status));
|
|
return Status;
|
|
}
|
|
|
|
HANDLE QfsCreateFileMapping(
|
|
QfsHANDLE hFile, // handle to file
|
|
LPSECURITY_ATTRIBUTES lpAttributes, // security
|
|
DWORD flProtect, // protection
|
|
DWORD dwMaximumSizeHigh, // high-order DWORD of size
|
|
DWORD dwMaximumSizeLow, // low-order DWORD of size
|
|
LPCTSTR lpName // object name
|
|
)
|
|
{
|
|
if (IsQfsHandle(hFile)) {
|
|
QfsError(("[Qfs] !!!!! CreateFileMapping for qfs handle !!!!! %x", hFile));
|
|
return INVALID_HANDLE_VALUE;
|
|
} else {
|
|
return CreateFileMapping(GetRealHandle(hFile), lpAttributes, flProtect,
|
|
dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
|
|
}
|
|
}
|
|
|
|
BOOL QfsGetOverlappedResult(
|
|
QfsHANDLE hFile, // handle to file, pipe, or device
|
|
LPOVERLAPPED lpOverlapped, // overlapped structure
|
|
LPDWORD lpNumberOfBytesTransferred, // bytes transferred
|
|
BOOL bWait // wait option
|
|
)
|
|
{
|
|
if (IsQfsHandle(hFile)) {
|
|
QfsError(("[Qfs] GetOverlappedResults for qfs handle !!!%x", hFile));
|
|
return FALSE;
|
|
} else {
|
|
return GetOverlappedResult(GetRealHandle(hFile), lpOverlapped,
|
|
lpNumberOfBytesTransferred, bWait);
|
|
}
|
|
}
|
|
|
|
BOOL QfsSetFileAttributes(
|
|
LPCWSTR lpFileName, // file name
|
|
DWORD dwFileAttributes // attributes
|
|
)
|
|
{
|
|
PJOB_BUF j;
|
|
DWORD Status = QfspReserveBuffer(opSetAttr2, lpFileName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
j->EndOfFile = 0;
|
|
j->AllocationSize = 0;
|
|
j->CreationTime = 0;
|
|
j->LastAccessTime = 0;
|
|
j->LastWriteTime = 0;
|
|
j->FileAttributes = dwFileAttributes;
|
|
QfspDeliverBuffer(j, &Status);
|
|
QfspReleaseBuffer(j);
|
|
} else if (Status == ERROR_NO_MATCH) {
|
|
StatusFromBool( SetFileAttributes(lpFileName, dwFileAttributes) );
|
|
}
|
|
QfsNoise(("[Qfs] QfsSetFileAttributes %ws %x, status %d", lpFileName, dwFileAttributes, Status));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsCopyFile(
|
|
LPCWSTR lpExistingFileName,
|
|
LPCWSTR lpNewFileName,
|
|
BOOL bFailIfExists
|
|
)
|
|
{
|
|
return QfsCopyFileEx(
|
|
lpExistingFileName,
|
|
lpNewFileName,
|
|
(LPPROGRESS_ROUTINE)NULL,
|
|
(LPVOID)NULL,
|
|
(LPBOOL)NULL,
|
|
bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0
|
|
);
|
|
}
|
|
|
|
// We have to implement our own version of CopyFile,
|
|
// using Qfs apis, if either a source or destination contain QfsPath
|
|
|
|
#define BUF_SIZE (32 * 1024)
|
|
|
|
#define COPY_FILE_FLUSH_BUFFERS 1
|
|
|
|
DWORD QfspCopyFileInternal(
|
|
LPCWSTR lpSrc,
|
|
LPCWSTR lpDst,
|
|
LPBOOL pbCancel,
|
|
DWORD dwCopyFlags,
|
|
DWORD ExtraFlags)
|
|
{
|
|
QfsHANDLE src = QfsINVALID_HANDLE_VALUE;
|
|
QfsHANDLE dst = QfsINVALID_HANDLE_VALUE;
|
|
DWORD dstDisp;
|
|
char* buf = malloc(65536);
|
|
DWORD Status=ERROR_SUCCESS;
|
|
|
|
if (buf == NULL) {
|
|
Status = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
if (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) {
|
|
dstDisp = CREATE_NEW;
|
|
} else {
|
|
dstDisp = CREATE_ALWAYS;
|
|
}
|
|
|
|
src = QfsCreateFile(lpSrc,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (!QfsIsHandleValid(src)) {
|
|
Status = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
dst = QfsCreateFile(lpDst,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
|
|
dstDisp, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (!QfsIsHandleValid(dst)) {
|
|
Status = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
for(;;) {
|
|
DWORD dwSize;
|
|
if (pbCancel && *pbCancel) {
|
|
Status = ERROR_OPERATION_ABORTED;
|
|
goto exit;
|
|
}
|
|
if ( !QfsReadFile(src, buf, BUF_SIZE, &dwSize, NULL) ) {
|
|
Status = GetLastError();
|
|
goto exit;
|
|
}
|
|
if (dwSize == 0) {
|
|
break;
|
|
}
|
|
if (pbCancel && *pbCancel) {
|
|
Status = ERROR_OPERATION_ABORTED;
|
|
goto exit;
|
|
}
|
|
if (!QfsWriteFile(dst, buf, dwSize, &dwSize, NULL) ) {
|
|
Status = GetLastError();
|
|
goto exit;
|
|
}
|
|
}
|
|
if (ExtraFlags & COPY_FILE_FLUSH_BUFFERS) {
|
|
QfsFlushFileBuffers(dst);
|
|
}
|
|
|
|
exit:
|
|
QfsCloseHandleIfValid(src);
|
|
QfsCloseHandleIfValid(dst);
|
|
if (buf) { free(buf); }
|
|
return Status;
|
|
}
|
|
|
|
BOOL QfsCopyFileEx(
|
|
LPCWSTR lpExistingFileName, // name of existing file
|
|
LPCWSTR lpNewFileName, // name of new file
|
|
LPPROGRESS_ROUTINE lpProgressRoutine, // callback function
|
|
LPVOID lpData, // callback parameter
|
|
LPBOOL pbCancel, // cancel status
|
|
DWORD dwCopyFlags // copy options
|
|
)
|
|
{
|
|
DWORD Status;
|
|
if (!IsQfsPath(lpExistingFileName) && !IsQfsPath(lpNewFileName)) {
|
|
BoolToStatus( CopyFileEx(lpExistingFileName, lpNewFileName, lpProgressRoutine,
|
|
lpData, pbCancel, dwCopyFlags) );
|
|
} else if (lpProgressRoutine || (dwCopyFlags & COPY_FILE_RESTARTABLE)) {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
Status = QfspCopyFileInternal(
|
|
lpExistingFileName, lpNewFileName, pbCancel, dwCopyFlags, 0);
|
|
}
|
|
QfsNoise(("[Qfs] QfsCopyFileEx %ws=>%ws, status %d", lpExistingFileName, lpNewFileName, Status));
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsMoveFileEx(
|
|
LPCWSTR lpExistingFileName, // file name
|
|
LPCWSTR lpNewFileName, // new file name
|
|
DWORD dwFlags // move options
|
|
)
|
|
{
|
|
BOOL bSrcQfs = IsQfsPath(lpExistingFileName);
|
|
BOOL bDstQfs = IsQfsPath(lpNewFileName);
|
|
DWORD Status;
|
|
if (!bSrcQfs && !bDstQfs) {
|
|
BoolToStatus( MoveFileEx(lpExistingFileName, lpNewFileName, dwFlags) );
|
|
} else if (bSrcQfs && bDstQfs) {
|
|
PJOB_BUF j;
|
|
Status = QfspReserveBuffer(opRename, lpExistingFileName, NULL, &j);
|
|
if (Status == ERROR_SUCCESS) {
|
|
Status = QfspCopyPath(j->FileNameDest, sizeof(j->FileNameDest), lpNewFileName);
|
|
if (Status == ERROR_SUCCESS) {
|
|
QfspDeliverBuffer(j, &Status);
|
|
}
|
|
QfspReleaseBuffer(j);
|
|
}
|
|
} else {
|
|
BoolToStatus(
|
|
QfsClRtlCopyFileAndFlushBuffers(lpExistingFileName, lpNewFileName) );
|
|
if (Status == ERROR_SUCCESS) {
|
|
BoolToStatus(QfsDeleteFile(lpExistingFileName));
|
|
}
|
|
}
|
|
QfsNoise(("[Qfs] QfsMoveFileEx %ws=>%ws", lpExistingFileName, lpNewFileName));
|
|
SetLastError(Status);
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
// GetTempFileName had to be shimmed so that it can work over Qfs path
|
|
|
|
UINT QfsGetTempFileName(
|
|
LPCWSTR lpPathName, // directory name
|
|
LPCWSTR lpPrefixString, // file name prefix
|
|
UINT uUnique, // integer
|
|
LPWSTR lpTempFileName // file name buffer
|
|
)
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
if ( IsQfsPath(lpPathName) ) {
|
|
int len;
|
|
|
|
wcscpy(lpTempFileName, lpPathName);
|
|
wcscat(lpTempFileName, lpPrefixString);
|
|
len = wcslen(lpTempFileName);
|
|
|
|
uUnique = uUnique & 0x0000ffff;
|
|
if (uUnique) {
|
|
wsprintf(lpTempFileName+len, L"%04x.tmp", uUnique);
|
|
} else {
|
|
DWORD uStartPoint = GetTickCount() & 0x0000ffff | 1;
|
|
uUnique = uStartPoint;
|
|
|
|
for(;;) {
|
|
QfsHANDLE hdl;
|
|
|
|
wsprintf(lpTempFileName+len, L"%04x.tmp", uUnique);
|
|
|
|
hdl = QfsCreateFile(lpTempFileName, GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_NEW, 0, 0);
|
|
if (QfsIsHandleValid(hdl)) {
|
|
QfsCloseHandle(hdl);
|
|
break;
|
|
}
|
|
Status = GetLastError();
|
|
if (Status == ERROR_ALREADY_EXISTS
|
|
||Status == ERROR_FILE_EXISTS
|
|
||Status == ERROR_SHARING_VIOLATION
|
|
||Status == ERROR_ACCESS_DENIED)
|
|
{
|
|
uUnique = (uUnique + 1) & 0xFFFF;
|
|
if (uUnique == 0) { ++uUnique; }
|
|
if (uUnique == uStartPoint) {
|
|
SetLastError(Status = ERROR_NO_MORE_FILES);
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { // not QfsPath
|
|
uUnique = GetTempFileName(
|
|
lpPathName, lpPrefixString, uUnique, lpTempFileName);
|
|
if (uUnique == 0) {
|
|
Status = GetLastError();
|
|
lpTempFileName[0] = 0;
|
|
}
|
|
}
|
|
|
|
QfsNoise(("[Qfs] QfsGetTempFileName %ws, %ws, %d => %ws, status %d",
|
|
lpPathName, lpPrefixString, uUnique, lpTempFileName, Status));
|
|
|
|
return uUnique;
|
|
}
|
|
|
|
// Helper routine for QfsRegSaveKey and QfsMapFileAndCheckSum
|
|
// creates thread specific TempFile name
|
|
|
|
DWORD QfspThreadTempFileName(
|
|
OUT LPWSTR Path // assumes MAX_PATH size
|
|
)
|
|
{
|
|
DWORD Status = GetModuleFileName(NULL, Path, MAX_PATH);
|
|
PWCHAR p;
|
|
if (Status == 0) {
|
|
return GetLastError();
|
|
}
|
|
if (Status == MAX_PATH) {
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
p = wcsrchr(Path, '\\');
|
|
if (p == NULL) {
|
|
return ERROR_PATH_NOT_FOUND;
|
|
}
|
|
wsprintf(p+1, L"Qfs%x.tmp", GetCurrentThreadId());
|
|
QfsNoise(("[Qfs] TempName generated %ws", p));
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Saves registry key in a temporary file on the system disk
|
|
// then copies it onto the quorum
|
|
|
|
LONG QfsRegSaveKey(
|
|
HKEY hKey, // handle to key
|
|
LPCWSTR lpFile, // data file
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
|
|
)
|
|
{
|
|
DWORD Status;
|
|
if (IsQfsPath(lpFile)) {
|
|
WCHAR TempName[MAX_PATH];
|
|
Status = QfspThreadTempFileName(TempName);
|
|
if (Status == ERROR_SUCCESS) {
|
|
DeleteFile(TempName);
|
|
Status = RegSaveKey(hKey, TempName, lpSecurityAttributes);
|
|
if (Status == ERROR_SUCCESS) {
|
|
BoolToStatus( QfsMoveFile(TempName, lpFile) );
|
|
}
|
|
}
|
|
} else {
|
|
Status = RegSaveKey(hKey, lpFile, lpSecurityAttributes);
|
|
}
|
|
QfsNoise(("[Qfs] QfsRegSaveKey %ws, status %d",
|
|
lpFile, Status));
|
|
return Status;
|
|
}
|
|
|
|
#ifndef DUMB_CLIENT
|
|
|
|
// Computes a checksome for the file on the quorum disk,
|
|
// by copying it to the temp file on the system disk and invoking regular MapFileAndChecksum API
|
|
|
|
DWORD QfsMapFileAndCheckSum(
|
|
LPCWSTR Filename,
|
|
PDWORD HeaderSum,
|
|
PDWORD CheckSum
|
|
)
|
|
{
|
|
DWORD RetCode = 1, Status;
|
|
if (IsQfsPath(Filename)) {
|
|
WCHAR TempName[MAX_PATH];
|
|
Status = QfspThreadTempFileName(TempName);
|
|
if (Status == ERROR_SUCCESS) {
|
|
DeleteFile(TempName);
|
|
if (QfsCopyFile(Filename, TempName, 0)) {
|
|
RetCode = MapFileAndCheckSum((LPWSTR)TempName, HeaderSum, CheckSum);
|
|
DeleteFile(TempName);
|
|
}
|
|
}
|
|
} else {
|
|
RetCode = MapFileAndCheckSum((LPWSTR)Filename, HeaderSum, CheckSum);
|
|
}
|
|
Status = RetCode ? GetLastError() : ERROR_SUCCESS;
|
|
QfsNoise(("[Qfs] QfsMapFileAndCheckSum %ws, ret %d status %d",
|
|
Filename, RetCode, Status));
|
|
return RetCode;
|
|
}
|
|
|
|
// Some of the ClRtl function has to be redone here.
|
|
// The reason is that ClRtl is not Qfs aware and cannot call Qfs shims directly
|
|
|
|
DWORD
|
|
QfsSetFileSecurityInfo(
|
|
IN LPCWSTR lpszFile,
|
|
IN DWORD dwAdminMask,
|
|
IN DWORD dwOwnerMask,
|
|
IN DWORD dwEveryoneMask
|
|
)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD dwError;
|
|
|
|
if (IsQfsPath(lpszFile)) {
|
|
// don't do this for QFS shares
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
hFile = CreateFile(lpszFile,
|
|
GENERIC_READ|WRITE_DAC|READ_CONTROL,
|
|
0,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
dwError = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[Qfs] QfsSetFileSecurityInfo - Failed to open file %1!ws!, Status=%2!u!\n",
|
|
lpszFile,
|
|
dwError);
|
|
return dwError;
|
|
}
|
|
|
|
dwError = ClRtlSetObjSecurityInfo(hFile,
|
|
SE_FILE_OBJECT,
|
|
dwAdminMask,
|
|
dwOwnerMask,
|
|
dwEveryoneMask);
|
|
CloseHandle(hFile);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
QfsClRtlCopyFileAndFlushBuffers(
|
|
IN LPCWSTR lpszSourceFile,
|
|
IN LPCWSTR lpszDestinationFile
|
|
)
|
|
{
|
|
DWORD Status = QfspCopyFileInternal(
|
|
lpszSourceFile, lpszDestinationFile,
|
|
NULL, 0, COPY_FILE_FLUSH_BUFFERS);
|
|
|
|
return Status == ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL QfsClRtlCreateDirectory(
|
|
IN LPCWSTR lpszPath
|
|
)
|
|
{
|
|
WCHAR cSlash = L'\\';
|
|
DWORD dwLen;
|
|
LPCWSTR pszNext;
|
|
WCHAR lpszDir[MAX_PATH];
|
|
LPWSTR pszDirPath=NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
if (!lpszPath || ((dwLen=lstrlenW(lpszPath)) < 1))
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
pszDirPath = (LPWSTR)LocalAlloc(LMEM_FIXED, ((dwLen + 2) * sizeof(WCHAR)));
|
|
if (pszDirPath == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto FnExit;
|
|
}
|
|
lstrcpyW(pszDirPath, lpszPath);
|
|
|
|
//if it doesnt terminate with \, terminate it
|
|
if (pszDirPath[dwLen-1] != cSlash)
|
|
{
|
|
pszDirPath[dwLen] = cSlash;
|
|
pszDirPath[dwLen+1] = L'\0';
|
|
}
|
|
|
|
dwLen = lstrlenW(pszDirPath);
|
|
//handle SMB Path names e.g \\xyz\abc\lmn
|
|
if ((dwLen > 2) && (pszDirPath[0]== L'\\') && (pszDirPath[1] == L'\\'))
|
|
{
|
|
//check if the name if of format \\?\UNC\XYZ\ABC\LMN
|
|
// or if the format \\?\C:\xyz\abz
|
|
if ((dwLen >3) && (pszDirPath[2] == L'?'))
|
|
{
|
|
//search for the \ after ?
|
|
pszNext = wcschr(pszDirPath + 2, cSlash);
|
|
//check if it is followed by UNC
|
|
if (pszNext)
|
|
{
|
|
if (!wcsncmp(pszNext+1, L"UNC", lstrlenW(L"UNC")))
|
|
{
|
|
//it is a UNC Path name
|
|
//move past the third slash from here
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
if (pszNext)
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
if (pszNext)
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
}
|
|
else
|
|
{
|
|
//it is a volume name, move to the next slash
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//it is of type \\xyz\abc\lmn
|
|
pszNext = wcschr(pszDirPath + 2, cSlash);
|
|
if (pszNext)
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszNext = pszDirPath;
|
|
pszNext = wcschr(pszNext, cSlash);
|
|
// if the character before the first \ is :, skip the creation
|
|
// of the c:\ level directory
|
|
if (pszNext && pszNext > pszDirPath)
|
|
{
|
|
pszNext--;
|
|
if (*pszNext == L':')
|
|
{
|
|
pszNext++;
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
}
|
|
else
|
|
pszNext++;
|
|
}
|
|
}
|
|
|
|
while ( pszNext)
|
|
{
|
|
DWORD_PTR dwptrLen;
|
|
|
|
dwptrLen = pszNext - pszDirPath + 1;
|
|
|
|
dwLen=(DWORD)dwptrLen;
|
|
lstrcpynW(lpszDir, pszDirPath, dwLen+1);
|
|
|
|
if (!QfsCreateDirectory(lpszDir, NULL))
|
|
{
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_ALREADY_EXISTS)
|
|
{
|
|
//this is not a problem,continue
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
QfsError(("[ClRtl] CreateDirectory Failed on %ws. Error %u",
|
|
lpszDir, dwError));
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
pszNext = wcschr(pszNext+1, cSlash);
|
|
}
|
|
|
|
FnExit:
|
|
if (pszDirPath) LocalFree(pszDirPath);
|
|
return(dwError);
|
|
}
|
|
|
|
|