1
0
This repository has been archived on 2025-07-27. You can view files and clone it, but cannot push or open issues or pull requests.
Files
siadrive/3rd-party/Dokan Library-1.0.2/x86/sample/mirror/mirror.c
Scott E. Graves 99da074b21 Dokan API
2017-02-10 15:09:15 -06:00

1558 lines
51 KiB
C

/*
Dokan : user-mode file system library for Windows
Copyright (C) 2015 - 2017 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com>
Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net>
http://dokan-dev.github.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "../../dokan/dokan.h"
#include "../../dokan/fileinfo.h"
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <winbase.h>
BOOL g_UseStdErr;
BOOL g_DebugMode;
BOOL g_HasSeSecurityPrivilege;
static void DbgPrint(LPCWSTR format, ...) {
if (g_DebugMode) {
const WCHAR *outputString;
WCHAR *buffer = NULL;
size_t length;
va_list argp;
va_start(argp, format);
length = _vscwprintf(format, argp) + 1;
buffer = _malloca(length * sizeof(WCHAR));
if (buffer) {
vswprintf_s(buffer, length, format, argp);
outputString = buffer;
} else {
outputString = format;
}
if (g_UseStdErr)
fputws(outputString, stderr);
else
OutputDebugStringW(outputString);
if (buffer)
_freea(buffer);
va_end(argp);
if (g_UseStdErr)
fflush(stderr);
}
}
static WCHAR RootDirectory[MAX_PATH] = L"C:";
static WCHAR MountPoint[MAX_PATH] = L"M:\\";
static WCHAR UNCName[MAX_PATH] = L"";
static void GetFilePath(PWCHAR filePath, ULONG numberOfElements,
LPCWSTR FileName) {
wcsncpy_s(filePath, numberOfElements, RootDirectory, wcslen(RootDirectory));
size_t unclen = wcslen(UNCName);
if (unclen > 0 && _wcsnicmp(FileName, UNCName, unclen) == 0) {
if (_wcsnicmp(FileName + unclen, L".", 1) != 0) {
wcsncat_s(filePath, numberOfElements, FileName + unclen,
wcslen(FileName) - unclen);
}
} else {
wcsncat_s(filePath, numberOfElements, FileName, wcslen(FileName));
}
}
static void PrintUserName(PDOKAN_FILE_INFO DokanFileInfo) {
HANDLE handle;
UCHAR buffer[1024];
DWORD returnLength;
WCHAR accountName[256];
WCHAR domainName[256];
DWORD accountLength = sizeof(accountName) / sizeof(WCHAR);
DWORD domainLength = sizeof(domainName) / sizeof(WCHAR);
PTOKEN_USER tokenUser;
SID_NAME_USE snu;
handle = DokanOpenRequestorToken(DokanFileInfo);
if (handle == INVALID_HANDLE_VALUE) {
DbgPrint(L" DokanOpenRequestorToken failed\n");
return;
}
if (!GetTokenInformation(handle, TokenUser, buffer, sizeof(buffer),
&returnLength)) {
DbgPrint(L" GetTokenInformaiton failed: %d\n", GetLastError());
CloseHandle(handle);
return;
}
CloseHandle(handle);
tokenUser = (PTOKEN_USER)buffer;
if (!LookupAccountSid(NULL, tokenUser->User.Sid, accountName, &accountLength,
domainName, &domainLength, &snu)) {
DbgPrint(L" LookupAccountSid failed: %d\n", GetLastError());
return;
}
DbgPrint(L" AccountName: %s, DomainName: %s\n", accountName, domainName);
}
static BOOL AddSeSecurityNamePrivilege() {
HANDLE token = 0;
DbgPrint(
L"## Attempting to add SE_SECURITY_NAME privilege to process token ##\n");
DWORD err;
LUID luid;
if (!LookupPrivilegeValue(0, SE_SECURITY_NAME, &luid)) {
err = GetLastError();
if (err != ERROR_SUCCESS) {
DbgPrint(L" failed: Unable to lookup privilege value. error = %u\n",
err);
return FALSE;
}
}
LUID_AND_ATTRIBUTES attr;
attr.Attributes = SE_PRIVILEGE_ENABLED;
attr.Luid = luid;
TOKEN_PRIVILEGES priv;
priv.PrivilegeCount = 1;
priv.Privileges[0] = attr;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
err = GetLastError();
if (err != ERROR_SUCCESS) {
DbgPrint(L" failed: Unable obtain process token. error = %u\n", err);
return FALSE;
}
}
TOKEN_PRIVILEGES oldPriv;
DWORD retSize;
AdjustTokenPrivileges(token, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), &oldPriv,
&retSize);
err = GetLastError();
if (err != ERROR_SUCCESS) {
DbgPrint(L" failed: Unable to adjust token privileges: %u\n", err);
CloseHandle(token);
return FALSE;
}
BOOL privAlreadyPresent = FALSE;
for (unsigned int i = 0; i < oldPriv.PrivilegeCount; i++) {
if (oldPriv.Privileges[i].Luid.HighPart == luid.HighPart &&
oldPriv.Privileges[i].Luid.LowPart == luid.LowPart) {
privAlreadyPresent = TRUE;
break;
}
}
DbgPrint(privAlreadyPresent ? L" success: privilege already present\n"
: L" success: privilege added\n");
if (token)
CloseHandle(token);
return TRUE;
}
#define MirrorCheckFlag(val, flag) \
if (val & flag) { \
DbgPrint(L"\t" L#flag L"\n"); \
}
static NTSTATUS DOKAN_CALLBACK
MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext,
ACCESS_MASK DesiredAccess, ULONG FileAttributes,
ULONG ShareAccess, ULONG CreateDisposition,
ULONG CreateOptions, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
DWORD fileAttr;
NTSTATUS status = STATUS_SUCCESS;
DWORD creationDisposition;
DWORD fileAttributesAndFlags;
DWORD error = 0;
SECURITY_ATTRIBUTES securityAttrib;
ACCESS_MASK genericDesiredAccess;
securityAttrib.nLength = sizeof(securityAttrib);
securityAttrib.lpSecurityDescriptor =
SecurityContext->AccessState.SecurityDescriptor;
securityAttrib.bInheritHandle = FALSE;
DokanMapKernelToUserCreateFileFlags(
FileAttributes, CreateOptions, CreateDisposition, &fileAttributesAndFlags,
&creationDisposition);
genericDesiredAccess = DokanMapStandardToGenericAccess(DesiredAccess);
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"CreateFile : %s\n", filePath);
PrintUserName(DokanFileInfo);
/*
if (ShareMode == 0 && AccessMode & FILE_WRITE_DATA)
ShareMode = FILE_SHARE_WRITE;
else if (ShareMode == 0)
ShareMode = FILE_SHARE_READ;
*/
DbgPrint(L"\tShareMode = 0x%x\n", ShareAccess);
MirrorCheckFlag(ShareAccess, FILE_SHARE_READ);
MirrorCheckFlag(ShareAccess, FILE_SHARE_WRITE);
MirrorCheckFlag(ShareAccess, FILE_SHARE_DELETE);
DbgPrint(L"\tDesiredAccess = 0x%x\n", DesiredAccess);
MirrorCheckFlag(DesiredAccess, GENERIC_READ);
MirrorCheckFlag(DesiredAccess, GENERIC_WRITE);
MirrorCheckFlag(DesiredAccess, GENERIC_EXECUTE);
MirrorCheckFlag(DesiredAccess, DELETE);
MirrorCheckFlag(DesiredAccess, FILE_READ_DATA);
MirrorCheckFlag(DesiredAccess, FILE_READ_ATTRIBUTES);
MirrorCheckFlag(DesiredAccess, FILE_READ_EA);
MirrorCheckFlag(DesiredAccess, READ_CONTROL);
MirrorCheckFlag(DesiredAccess, FILE_WRITE_DATA);
MirrorCheckFlag(DesiredAccess, FILE_WRITE_ATTRIBUTES);
MirrorCheckFlag(DesiredAccess, FILE_WRITE_EA);
MirrorCheckFlag(DesiredAccess, FILE_APPEND_DATA);
MirrorCheckFlag(DesiredAccess, WRITE_DAC);
MirrorCheckFlag(DesiredAccess, WRITE_OWNER);
MirrorCheckFlag(DesiredAccess, SYNCHRONIZE);
MirrorCheckFlag(DesiredAccess, FILE_EXECUTE);
MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_READ);
MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_WRITE);
MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_EXECUTE);
// When filePath is a directory, needs to change the flag so that the file can
// be opened.
fileAttr = GetFileAttributes(filePath);
if (fileAttr != INVALID_FILE_ATTRIBUTES &&
(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&
!(CreateOptions & FILE_NON_DIRECTORY_FILE)) {
DokanFileInfo->IsDirectory = TRUE;
if (DesiredAccess & DELETE) {
// Needed by FindFirstFile to see if directory is empty or not
ShareAccess |= FILE_SHARE_READ;
}
}
DbgPrint(L"\tFlagsAndAttributes = 0x%x\n", fileAttributesAndFlags);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ARCHIVE);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ENCRYPTED);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_HIDDEN);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_NORMAL);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_OFFLINE);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_READONLY);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_SYSTEM);
MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_TEMPORARY);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_WRITE_THROUGH);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OVERLAPPED);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_NO_BUFFERING);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_RANDOM_ACCESS);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_SEQUENTIAL_SCAN);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_DELETE_ON_CLOSE);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_BACKUP_SEMANTICS);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_POSIX_SEMANTICS);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OPEN_REPARSE_POINT);
MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OPEN_NO_RECALL);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_ANONYMOUS);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_IDENTIFICATION);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_IMPERSONATION);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_DELEGATION);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_CONTEXT_TRACKING);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_EFFECTIVE_ONLY);
MirrorCheckFlag(fileAttributesAndFlags, SECURITY_SQOS_PRESENT);
if (creationDisposition == CREATE_NEW) {
DbgPrint(L"\tCREATE_NEW\n");
} else if (creationDisposition == OPEN_ALWAYS) {
DbgPrint(L"\tOPEN_ALWAYS\n");
} else if (creationDisposition == CREATE_ALWAYS) {
DbgPrint(L"\tCREATE_ALWAYS\n");
} else if (creationDisposition == OPEN_EXISTING) {
DbgPrint(L"\tOPEN_EXISTING\n");
} else if (creationDisposition == TRUNCATE_EXISTING) {
DbgPrint(L"\tTRUNCATE_EXISTING\n");
} else {
DbgPrint(L"\tUNKNOWN creationDisposition!\n");
}
if (DokanFileInfo->IsDirectory) {
// It is a create directory request
if (creationDisposition == CREATE_NEW) {
if (!CreateDirectory(filePath, &securityAttrib)) {
error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
status = DokanNtStatusFromWin32(error);
}
} else if (creationDisposition == OPEN_ALWAYS) {
if (!CreateDirectory(filePath, &securityAttrib)) {
error = GetLastError();
if (error != ERROR_ALREADY_EXISTS) {
DbgPrint(L"\terror code = %d\n\n", error);
status = DokanNtStatusFromWin32(error);
}
}
}
if (status == STATUS_SUCCESS) {
//Check first if we're trying to open a file as a directory.
if (fileAttr != INVALID_FILE_ATTRIBUTES &&
!(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&
(CreateOptions & FILE_DIRECTORY_FILE)) {
return STATUS_NOT_A_DIRECTORY;
}
// FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles
handle =
CreateFile(filePath, genericDesiredAccess, ShareAccess,
&securityAttrib, OPEN_EXISTING,
fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE) {
error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
status = DokanNtStatusFromWin32(error);
} else {
DokanFileInfo->Context =
(ULONG64)handle; // save the file handle in Context
}
}
} else {
// It is a create file request
if (fileAttr != INVALID_FILE_ATTRIBUTES &&
(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&
CreateDisposition == FILE_CREATE)
return STATUS_OBJECT_NAME_COLLISION; // File already exist because
// GetFileAttributes found it
handle = CreateFile(
filePath,
genericDesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE,
ShareAccess,
&securityAttrib, // security attribute
creationDisposition,
fileAttributesAndFlags, // |FILE_FLAG_NO_BUFFERING,
NULL); // template file handle
if (handle == INVALID_HANDLE_VALUE) {
error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
status = DokanNtStatusFromWin32(error);
} else {
DokanFileInfo->Context =
(ULONG64)handle; // save the file handle in Context
if (creationDisposition == OPEN_ALWAYS ||
creationDisposition == CREATE_ALWAYS) {
error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
DbgPrint(L"\tOpen an already existing file\n");
// Open succeed but we need to inform the driver
// that the file open and not created by returning STATUS_OBJECT_NAME_COLLISION
return STATUS_OBJECT_NAME_COLLISION;
}
}
}
}
DbgPrint(L"\n");
return status;
}
#pragma warning(push)
#pragma warning(disable : 4305)
static void DOKAN_CALLBACK MirrorCloseFile(LPCWSTR FileName,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
GetFilePath(filePath, MAX_PATH, FileName);
if (DokanFileInfo->Context) {
DbgPrint(L"CloseFile: %s\n", filePath);
DbgPrint(L"\terror : not cleanuped file\n\n");
CloseHandle((HANDLE)DokanFileInfo->Context);
DokanFileInfo->Context = 0;
} else {
DbgPrint(L"Close: %s\n\n", filePath);
}
}
static void DOKAN_CALLBACK MirrorCleanup(LPCWSTR FileName,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
GetFilePath(filePath, MAX_PATH, FileName);
if (DokanFileInfo->Context) {
DbgPrint(L"Cleanup: %s\n\n", filePath);
CloseHandle((HANDLE)(DokanFileInfo->Context));
DokanFileInfo->Context = 0;
} else {
DbgPrint(L"Cleanup: %s\n\tinvalid handle\n\n", filePath);
}
if (DokanFileInfo->DeleteOnClose) {
// Should already be deleted by CloseHandle
// if open with FILE_FLAG_DELETE_ON_CLOSE
DbgPrint(L"\tDeleteOnClose\n");
if (DokanFileInfo->IsDirectory) {
DbgPrint(L" DeleteDirectory ");
if (!RemoveDirectory(filePath)) {
DbgPrint(L"error code = %d\n\n", GetLastError());
} else {
DbgPrint(L"success\n\n");
}
} else {
DbgPrint(L" DeleteFile ");
if (DeleteFile(filePath) == 0) {
DbgPrint(L" error code = %d\n\n", GetLastError());
} else {
DbgPrint(L"success\n\n");
}
}
}
}
static NTSTATUS DOKAN_CALLBACK MirrorReadFile(LPCWSTR FileName, LPVOID Buffer,
DWORD BufferLength,
LPDWORD ReadLength,
LONGLONG Offset,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle = (HANDLE)DokanFileInfo->Context;
ULONG offset = (ULONG)Offset;
BOOL opened = FALSE;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"ReadFile : %s\n", filePath);
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle, cleanuped?\n");
handle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
DbgPrint(L"\tCreateFile error : %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
opened = TRUE;
}
LARGE_INTEGER distanceToMove;
distanceToMove.QuadPart = Offset;
if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) {
DWORD error = GetLastError();
DbgPrint(L"\tseek error, offset = %d\n\n", offset);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
if (!ReadFile(handle, Buffer, BufferLength, ReadLength, NULL)) {
DWORD error = GetLastError();
DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n",
error, BufferLength, *ReadLength);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
} else {
DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", BufferLength,
*ReadLength, offset);
}
if (opened)
CloseHandle(handle);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(LPCWSTR FileName, LPCVOID Buffer,
DWORD NumberOfBytesToWrite,
LPDWORD NumberOfBytesWritten,
LONGLONG Offset,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle = (HANDLE)DokanFileInfo->Context;
BOOL opened = FALSE;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"WriteFile : %s, offset %I64d, length %d\n", filePath, Offset,
NumberOfBytesToWrite);
// reopen the file
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle, cleanuped?\n");
handle = CreateFile(filePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
DbgPrint(L"\tCreateFile error : %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
opened = TRUE;
}
UINT64 fileSize = 0;
DWORD fileSizeLow = 0;
DWORD fileSizeHigh = 0;
fileSizeLow = GetFileSize(handle, &fileSizeHigh);
if (fileSizeLow == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
DbgPrint(L"\tcan not get a file size error = %d\n", error);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
fileSize = ((UINT64)fileSizeHigh << 32) | fileSizeLow;
LARGE_INTEGER distanceToMove;
if (DokanFileInfo->WriteToEndOfFile) {
LARGE_INTEGER z;
z.QuadPart = 0;
if (!SetFilePointerEx(handle, z, NULL, FILE_END)) {
DWORD error = GetLastError();
DbgPrint(L"\tseek error, offset = EOF, error = %d\n", error);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
} else {
// Paging IO cannot write after allocate file size.
if (DokanFileInfo->PagingIo) {
if ((UINT64)Offset >= fileSize) {
*NumberOfBytesWritten = 0;
if (opened)
CloseHandle(handle);
return STATUS_SUCCESS;
}
if (((UINT64)Offset + NumberOfBytesToWrite) > fileSize) {
UINT64 bytes = fileSize - Offset;
if (bytes >> 32) {
NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL);
} else {
NumberOfBytesToWrite = (DWORD)bytes;
}
}
}
if ((UINT64)Offset > fileSize) {
// In the mirror sample helperZeroFileData is not necessary. NTFS will
// zero a hole.
// But if user's file system is different from NTFS( or other Windows's
// file systems ) then users will have to zero the hole themselves.
}
distanceToMove.QuadPart = Offset;
if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) {
DWORD error = GetLastError();
DbgPrint(L"\tseek error, offset = %I64d, error = %d\n", Offset, error);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
}
if (!WriteFile(handle, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten,
NULL)) {
DWORD error = GetLastError();
DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n",
error, NumberOfBytesToWrite, *NumberOfBytesWritten);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
} else {
DbgPrint(L"\twrite %d, offset %I64d\n\n", *NumberOfBytesWritten, Offset);
}
// close the file when it is reopened
if (opened)
CloseHandle(handle);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorFlushFileBuffers(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle = (HANDLE)DokanFileInfo->Context;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"FlushFileBuffers : %s\n", filePath);
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_SUCCESS;
}
if (FlushFileBuffers(handle)) {
return STATUS_SUCCESS;
} else {
DWORD error = GetLastError();
DbgPrint(L"\tflush error code = %d\n", error);
return DokanNtStatusFromWin32(error);
}
}
static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation(
LPCWSTR FileName, LPBY_HANDLE_FILE_INFORMATION HandleFileInformation,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle = (HANDLE)DokanFileInfo->Context;
BOOL opened = FALSE;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"GetFileInfo : %s\n", filePath);
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle, cleanuped?\n");
handle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
DbgPrint(L"\tCreateFile error : %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
opened = TRUE;
}
if (!GetFileInformationByHandle(handle, HandleFileInformation)) {
DbgPrint(L"\terror code = %d\n", GetLastError());
// FileName is a root directory
// in this case, FindFirstFile can't get directory information
if (wcslen(FileName) == 1) {
DbgPrint(L" root dir\n");
HandleFileInformation->dwFileAttributes = GetFileAttributes(filePath);
} else {
WIN32_FIND_DATAW find;
ZeroMemory(&find, sizeof(WIN32_FIND_DATAW));
HANDLE findHandle = FindFirstFile(filePath, &find);
if (findHandle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
DbgPrint(L"\tFindFirstFile error code = %d\n\n", error);
if (opened)
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
HandleFileInformation->dwFileAttributes = find.dwFileAttributes;
HandleFileInformation->ftCreationTime = find.ftCreationTime;
HandleFileInformation->ftLastAccessTime = find.ftLastAccessTime;
HandleFileInformation->ftLastWriteTime = find.ftLastWriteTime;
HandleFileInformation->nFileSizeHigh = find.nFileSizeHigh;
HandleFileInformation->nFileSizeLow = find.nFileSizeLow;
DbgPrint(L"\tFindFiles OK, file size = %d\n", find.nFileSizeLow);
FindClose(findHandle);
}
} else {
DbgPrint(L"\tGetFileInformationByHandle success, file size = %d\n",
HandleFileInformation->nFileSizeLow);
}
DbgPrint(L"\n");
if (opened)
CloseHandle(handle);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorFindFiles(LPCWSTR FileName,
PFillFindData FillFindData, // function pointer
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
size_t fileLen;
HANDLE hFind;
WIN32_FIND_DATAW findData;
DWORD error;
int count = 0;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"FindFiles :%s\n", filePath);
fileLen = wcslen(filePath);
if (filePath[fileLen - 1] != L'\\') {
filePath[fileLen++] = L'\\';
}
filePath[fileLen] = L'*';
filePath[fileLen + 1] = L'\0';
hFind = FindFirstFile(filePath, &findData);
if (hFind == INVALID_HANDLE_VALUE) {
error = GetLastError();
DbgPrint(L"\tinvalid file handle. Error is %u\n\n", error);
return DokanNtStatusFromWin32(error);
}
// Root folder does not have . and .. folder - we remove them
BOOLEAN rootFolder = (wcscmp(FileName, L"\\") == 0);
do {
if (!rootFolder || (wcscmp(findData.cFileName, L".") != 0 &&
wcscmp(findData.cFileName, L"..") != 0))
FillFindData(&findData, DokanFileInfo);
count++;
} while (FindNextFile(hFind, &findData) != 0);
error = GetLastError();
FindClose(hFind);
if (error != ERROR_NO_MORE_FILES) {
DbgPrint(L"\tFindNextFile error. Error is %u\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\tFindFiles return %d entries in %s\n\n", count, filePath);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorDeleteFile(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle = (HANDLE)DokanFileInfo->Context;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"DeleteFile %s - %d\n", filePath, DokanFileInfo->DeleteOnClose);
DWORD dwAttrib = GetFileAttributes(filePath);
if (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
return STATUS_ACCESS_DENIED;
if (handle && handle != INVALID_HANDLE_VALUE) {
FILE_DISPOSITION_INFO fdi;
fdi.DeleteFile = DokanFileInfo->DeleteOnClose;
if (!SetFileInformationByHandle(handle, FileDispositionInfo, &fdi,
sizeof(FILE_DISPOSITION_INFO)))
return DokanNtStatusFromWin32(GetLastError());
}
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorDeleteDirectory(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
// HANDLE handle = (HANDLE)DokanFileInfo->Context;
HANDLE hFind;
WIN32_FIND_DATAW findData;
size_t fileLen;
ZeroMemory(filePath, sizeof(filePath));
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"DeleteDirectory %s - %d\n", filePath,
DokanFileInfo->DeleteOnClose);
if (!DokanFileInfo->DeleteOnClose)
//Dokan notify that the file is requested not to be deleted.
return STATUS_SUCCESS;
fileLen = wcslen(filePath);
if (filePath[fileLen - 1] != L'\\') {
filePath[fileLen++] = L'\\';
}
filePath[fileLen] = L'*';
filePath[fileLen + 1] = L'\0';
hFind = FindFirstFile(filePath, &findData);
if (hFind == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
do {
if (wcscmp(findData.cFileName, L"..") != 0 &&
wcscmp(findData.cFileName, L".") != 0) {
FindClose(hFind);
DbgPrint(L"\tDirectory is not empty: %s\n", findData.cFileName);
return STATUS_DIRECTORY_NOT_EMPTY;
}
} while (FindNextFile(hFind, &findData) != 0);
DWORD error = GetLastError();
if (error != ERROR_NO_MORE_FILES) {
DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
FindClose(hFind);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorMoveFile(LPCWSTR FileName, // existing file name
LPCWSTR NewFileName, BOOL ReplaceIfExisting,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
WCHAR newFilePath[MAX_PATH];
HANDLE handle;
DWORD bufferSize;
BOOL result;
size_t newFilePathLen;
PFILE_RENAME_INFO renameInfo = NULL;
GetFilePath(filePath, MAX_PATH, FileName);
GetFilePath(newFilePath, MAX_PATH, NewFileName);
DbgPrint(L"MoveFile %s -> %s\n\n", filePath, newFilePath);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
newFilePathLen = wcslen(newFilePath);
// the PFILE_RENAME_INFO struct has space for one WCHAR for the name at
// the end, so that
// accounts for the null terminator
bufferSize = (DWORD)(sizeof(FILE_RENAME_INFO) +
newFilePathLen * sizeof(newFilePath[0]));
renameInfo = (PFILE_RENAME_INFO)malloc(bufferSize);
if (!renameInfo) {
return STATUS_BUFFER_OVERFLOW;
}
ZeroMemory(renameInfo, bufferSize);
renameInfo->ReplaceIfExists =
ReplaceIfExisting
? TRUE
: FALSE; // some warning about converting BOOL to BOOLEAN
renameInfo->RootDirectory = NULL; // hope it is never needed, shouldn't be
renameInfo->FileNameLength =
(DWORD)newFilePathLen *
sizeof(newFilePath[0]); // they want length in bytes
wcscpy_s(renameInfo->FileName, newFilePathLen + 1, newFilePath);
result = SetFileInformationByHandle(handle, FileRenameInfo, renameInfo,
bufferSize);
free(renameInfo);
if (result) {
return STATUS_SUCCESS;
} else {
DWORD error = GetLastError();
DbgPrint(L"\tMoveFile error = %u\n", error);
return DokanNtStatusFromWin32(error);
}
}
static NTSTATUS DOKAN_CALLBACK MirrorLockFile(LPCWSTR FileName,
LONGLONG ByteOffset,
LONGLONG Length,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
LARGE_INTEGER offset;
LARGE_INTEGER length;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"LockFile %s\n", filePath);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
length.QuadPart = Length;
offset.QuadPart = ByteOffset;
if (!LockFile(handle, offset.LowPart, offset.HighPart, length.LowPart,
length.HighPart)) {
DWORD error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\tsuccess\n\n");
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile(
LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
LARGE_INTEGER offset;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"SetEndOfFile %s, %I64d\n", filePath, ByteOffset);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
offset.QuadPart = ByteOffset;
if (!SetFilePointerEx(handle, offset, NULL, FILE_BEGIN)) {
DWORD error = GetLastError();
DbgPrint(L"\tSetFilePointer error: %d, offset = %I64d\n\n", error,
ByteOffset);
return DokanNtStatusFromWin32(error);
}
if (!SetEndOfFile(handle)) {
DWORD error = GetLastError();
DbgPrint(L"\tSetEndOfFile error code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(
LPCWSTR FileName, LONGLONG AllocSize, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
LARGE_INTEGER fileSize;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"SetAllocationSize %s, %I64d\n", filePath, AllocSize);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
if (GetFileSizeEx(handle, &fileSize)) {
if (AllocSize < fileSize.QuadPart) {
fileSize.QuadPart = AllocSize;
if (!SetFilePointerEx(handle, fileSize, NULL, FILE_BEGIN)) {
DWORD error = GetLastError();
DbgPrint(L"\tSetAllocationSize: SetFilePointer eror: %d, "
L"offset = %I64d\n\n",
error, AllocSize);
return DokanNtStatusFromWin32(error);
}
if (!SetEndOfFile(handle)) {
DWORD error = GetLastError();
DbgPrint(L"\tSetEndOfFile error code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
}
} else {
DWORD error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorSetFileAttributes(
LPCWSTR FileName, DWORD FileAttributes, PDOKAN_FILE_INFO DokanFileInfo) {
UNREFERENCED_PARAMETER(DokanFileInfo);
WCHAR filePath[MAX_PATH];
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"SetFileAttributes %s\n", filePath);
if (!SetFileAttributes(filePath, FileAttributes)) {
DWORD error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\n");
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorSetFileTime(LPCWSTR FileName, CONST FILETIME *CreationTime,
CONST FILETIME *LastAccessTime, CONST FILETIME *LastWriteTime,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"SetFileTime %s\n", filePath);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
if (!SetFileTime(handle, CreationTime, LastAccessTime, LastWriteTime)) {
DWORD error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\n");
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK
MirrorUnlockFile(LPCWSTR FileName, LONGLONG ByteOffset, LONGLONG Length,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE handle;
LARGE_INTEGER length;
LARGE_INTEGER offset;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"UnlockFile %s\n", filePath);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
length.QuadPart = Length;
offset.QuadPart = ByteOffset;
if (!UnlockFile(handle, offset.LowPart, offset.HighPart, length.LowPart,
length.HighPart)) {
DWORD error = GetLastError();
DbgPrint(L"\terror code = %d\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\tsuccess\n\n");
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(
LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG BufferLength,
PULONG LengthNeeded, PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
BOOLEAN requestingSaclInfo;
UNREFERENCED_PARAMETER(DokanFileInfo);
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"GetFileSecurity %s\n", filePath);
MirrorCheckFlag(*SecurityInformation, FILE_SHARE_READ);
MirrorCheckFlag(*SecurityInformation, OWNER_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, GROUP_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, DACL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, SACL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, LABEL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, SCOPE_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation,
PROCESS_TRUST_LABEL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, BACKUP_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION);
MirrorCheckFlag(*SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION);
requestingSaclInfo = ((*SecurityInformation & SACL_SECURITY_INFORMATION) ||
(*SecurityInformation & BACKUP_SECURITY_INFORMATION));
if (!g_HasSeSecurityPrivilege) {
*SecurityInformation &= ~SACL_SECURITY_INFORMATION;
*SecurityInformation &= ~BACKUP_SECURITY_INFORMATION;
}
DbgPrint(L" Opening new handle with READ_CONTROL access\n");
HANDLE handle = CreateFile(
filePath, READ_CONTROL | ((requestingSaclInfo && g_HasSeSecurityPrivilege)
? ACCESS_SYSTEM_SECURITY
: 0),
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, // security attribute
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, // |FILE_FLAG_NO_BUFFERING,
NULL);
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
int error = GetLastError();
return DokanNtStatusFromWin32(error);
}
if (!GetUserObjectSecurity(handle, SecurityInformation, SecurityDescriptor,
BufferLength, LengthNeeded)) {
int error = GetLastError();
if (error == ERROR_INSUFFICIENT_BUFFER) {
DbgPrint(L" GetUserObjectSecurity error: ERROR_INSUFFICIENT_BUFFER\n");
CloseHandle(handle);
return STATUS_BUFFER_OVERFLOW;
} else {
DbgPrint(L" GetUserObjectSecurity error: %d\n", error);
CloseHandle(handle);
return DokanNtStatusFromWin32(error);
}
}
CloseHandle(handle);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(
LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG SecurityDescriptorLength,
PDOKAN_FILE_INFO DokanFileInfo) {
HANDLE handle;
WCHAR filePath[MAX_PATH];
UNREFERENCED_PARAMETER(SecurityDescriptorLength);
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"SetFileSecurity %s\n", filePath);
handle = (HANDLE)DokanFileInfo->Context;
if (!handle || handle == INVALID_HANDLE_VALUE) {
DbgPrint(L"\tinvalid handle\n\n");
return STATUS_INVALID_HANDLE;
}
if (!SetUserObjectSecurity(handle, SecurityInformation, SecurityDescriptor)) {
int error = GetLastError();
DbgPrint(L" SetUserObjectSecurity error: %d\n", error);
return DokanNtStatusFromWin32(error);
}
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorGetVolumeInformation(
LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, LPDWORD VolumeSerialNumber,
LPDWORD MaximumComponentLength, LPDWORD FileSystemFlags,
LPWSTR FileSystemNameBuffer, DWORD FileSystemNameSize,
PDOKAN_FILE_INFO DokanFileInfo) {
UNREFERENCED_PARAMETER(DokanFileInfo);
wcscpy_s(VolumeNameBuffer, VolumeNameSize, L"DOKAN");
*VolumeSerialNumber = 0x19831116;
*MaximumComponentLength = 256;
*FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK |
FILE_PERSISTENT_ACLS;
// File system name could be anything up to 10 characters.
// But Windows check few feature availability based on file system name.
// For this, it is recommended to set NTFS or FAT here.
wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"NTFS");
return STATUS_SUCCESS;
}
/*
//Uncomment for personalize disk space
static NTSTATUS DOKAN_CALLBACK MirrorDokanGetDiskFreeSpace(
PULONGLONG FreeBytesAvailable, PULONGLONG TotalNumberOfBytes,
PULONGLONG TotalNumberOfFreeBytes, PDOKAN_FILE_INFO DokanFileInfo) {
UNREFERENCED_PARAMETER(DokanFileInfo);
*FreeBytesAvailable = (ULONGLONG)(512 * 1024 * 1024);
*TotalNumberOfBytes = 9223372036854775807;
*TotalNumberOfFreeBytes = 9223372036854775807;
return STATUS_SUCCESS;
}
*/
/**
* Avoid #include <winternl.h> which as conflict with FILE_INFORMATION_CLASS
* definition.
* This only for MirrorFindStreams. Link with ntdll.lib still required.
*
* Not needed if you're not using NtQueryInformationFile!
*
* BEGIN
*/
#pragma warning(push)
#pragma warning(disable : 4201)
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
} DUMMYUNIONNAME;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
#pragma warning(pop)
NTSYSCALLAPI NTSTATUS NTAPI NtQueryInformationFile(
_In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_writes_bytes_(Length) PVOID FileInformation, _In_ ULONG Length,
_In_ FILE_INFORMATION_CLASS FileInformationClass);
/**
* END
*/
NTSTATUS DOKAN_CALLBACK
MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData,
PDOKAN_FILE_INFO DokanFileInfo) {
WCHAR filePath[MAX_PATH];
HANDLE hFind;
WIN32_FIND_STREAM_DATA findData;
DWORD error;
int count = 0;
GetFilePath(filePath, MAX_PATH, FileName);
DbgPrint(L"FindStreams :%s\n", filePath);
hFind = FindFirstStreamW(filePath, FindStreamInfoStandard, &findData, 0);
if (hFind == INVALID_HANDLE_VALUE) {
error = GetLastError();
DbgPrint(L"\tinvalid file handle. Error is %u\n\n", error);
return DokanNtStatusFromWin32(error);
}
FillFindStreamData(&findData, DokanFileInfo);
count++;
while (FindNextStreamW(hFind, &findData) != 0) {
FillFindStreamData(&findData, DokanFileInfo);
count++;
}
error = GetLastError();
FindClose(hFind);
if (error != ERROR_HANDLE_EOF) {
DbgPrint(L"\tFindNextStreamW error. Error is %u\n\n", error);
return DokanNtStatusFromWin32(error);
}
DbgPrint(L"\tFindStreams return %d entries in %s\n\n", count, filePath);
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorMounted(PDOKAN_FILE_INFO DokanFileInfo) {
UNREFERENCED_PARAMETER(DokanFileInfo);
DbgPrint(L"Mounted\n");
return STATUS_SUCCESS;
}
static NTSTATUS DOKAN_CALLBACK MirrorUnmounted(PDOKAN_FILE_INFO DokanFileInfo) {
UNREFERENCED_PARAMETER(DokanFileInfo);
DbgPrint(L"Unmounted\n");
return STATUS_SUCCESS;
}
#pragma warning(pop)
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
switch (dwCtrlType) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
SetConsoleCtrlHandler(CtrlHandler, FALSE);
DokanRemoveMountPoint(MountPoint);
return TRUE;
default:
return FALSE;
}
}
void ShowUsage() {
// clang-format off
fprintf(stderr, "mirror.exe\n"
" /r RootDirectory (ex. /r c:\\test)\t\t Directory source to mirror.\n"
" /l MountPoint (ex. /l m)\t\t\t Mount point. Can be M:\\ (drive letter) or empty NTFS folder C:\\mount\\dokan .\n"
" /t ThreadCount (ex. /t 5)\t\t\t Number of threads to be used internally by Dokan library.\n\t\t\t\t\t\t More threads will handle more event at the same time.\n"
" /d (enable debug output)\t\t\t Enable debug output to an attached debugger.\n"
" /s (use stderr for output)\t\t\t Enable debug output to stderr.\n"
" /n (use network drive)\t\t\t Show device as network device.\n"
" /m (use removable drive)\t\t\t Show device as removable media.\n"
" /w (write-protect drive)\t\t\t Read only filesystem.\n"
" /o (use mount manager)\t\t\t Register device to Windows mount manager.\n\t\t\t\t\t\t This enables advanced Windows features like recycle bin and more...\n"
" /c (mount for current session only)\t\t Device only visible for current user session.\n"
" /u (UNC provider name ex. \\localhost\\myfs)\t UNC name used for network volume.\n"
" /a Allocation unit size (ex. /a 512)\t\t Allocation Unit Size of the volume. This will behave on the disk file size.\n"
" /k Sector size (ex. /k 512)\t\t\t Sector Size of the volume. This will behave on the disk file size.\n"
" /f User mode Lock\t\t\t\t Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it.\n"
" /i (Timeout in Milliseconds ex. /i 30000)\t Timeout until a running operation is aborted and the device is unmounted.\n\n"
"Examples:\n"
"\tmirror.exe /r C:\\Users /l M:\t\t\t# Mirror C:\\Users as RootDirectory into a drive of letter M:\\.\n"
"\tmirror.exe /r C:\\Users /l C:\\mount\\dokan\t# Mirror C:\\Users as RootDirectory into NTFS folder C:\\mount\\dokan.\n"
"\tmirror.exe /r C:\\Users /l M: /n /u \\myfs\\myfs1\t# Mirror C:\\Users as RootDirectory into a network drive M:\\. with UNC \\\\myfs\\myfs1\n\n"
"Unmount the drive with CTRL + C in the console or alternatively via \"dokanctl /u MountPoint\".\n");
// clang-format on
}
int __cdecl wmain(ULONG argc, PWCHAR argv[]) {
int status;
ULONG command;
PDOKAN_OPERATIONS dokanOperations =
(PDOKAN_OPERATIONS)malloc(sizeof(DOKAN_OPERATIONS));
if (dokanOperations == NULL) {
return EXIT_FAILURE;
}
PDOKAN_OPTIONS dokanOptions = (PDOKAN_OPTIONS)malloc(sizeof(DOKAN_OPTIONS));
if (dokanOptions == NULL) {
free(dokanOperations);
return EXIT_FAILURE;
}
if (argc < 3) {
ShowUsage();
free(dokanOperations);
free(dokanOptions);
return EXIT_FAILURE;
}
g_DebugMode = FALSE;
g_UseStdErr = FALSE;
ZeroMemory(dokanOptions, sizeof(DOKAN_OPTIONS));
dokanOptions->Version = DOKAN_VERSION;
dokanOptions->ThreadCount = 0; // use default
for (command = 1; command < argc; command++) {
switch (towlower(argv[command][1])) {
case L'r':
command++;
wcscpy_s(RootDirectory, sizeof(RootDirectory) / sizeof(WCHAR),
argv[command]);
DbgPrint(L"RootDirectory: %ls\n", RootDirectory);
break;
case L'l':
command++;
wcscpy_s(MountPoint, sizeof(MountPoint) / sizeof(WCHAR), argv[command]);
dokanOptions->MountPoint = MountPoint;
break;
case L't':
command++;
dokanOptions->ThreadCount = (USHORT)_wtoi(argv[command]);
break;
case L'd':
g_DebugMode = TRUE;
break;
case L's':
g_UseStdErr = TRUE;
break;
case L'n':
dokanOptions->Options |= DOKAN_OPTION_NETWORK;
break;
case L'm':
dokanOptions->Options |= DOKAN_OPTION_REMOVABLE;
break;
case L'w':
dokanOptions->Options |= DOKAN_OPTION_WRITE_PROTECT;
break;
case L'o':
dokanOptions->Options |= DOKAN_OPTION_MOUNT_MANAGER;
break;
case L'c':
dokanOptions->Options |= DOKAN_OPTION_CURRENT_SESSION;
break;
case L'f':
dokanOptions->Options |= DOKAN_OPTION_FILELOCK_USER_MODE;
break;
case L'u':
command++;
wcscpy_s(UNCName, sizeof(UNCName) / sizeof(WCHAR), argv[command]);
dokanOptions->UNCName = UNCName;
DbgPrint(L"UNC Name: %ls\n", UNCName);
break;
case L'i':
command++;
dokanOptions->Timeout = (ULONG)_wtol(argv[command]);
break;
case L'a':
command++;
dokanOptions->AllocationUnitSize = (ULONG)_wtol(argv[command]);
break;
case L'k':
command++;
dokanOptions->SectorSize = (ULONG)_wtol(argv[command]);
break;
default:
fwprintf(stderr, L"unknown command: %s\n", argv[command]);
free(dokanOperations);
free(dokanOptions);
return EXIT_FAILURE;
}
}
if (wcscmp(UNCName, L"") != 0 &&
!(dokanOptions->Options & DOKAN_OPTION_NETWORK)) {
fwprintf(
stderr,
L" Warning: UNC provider name should be set on network drive only.\n");
}
if (dokanOptions->Options & DOKAN_OPTION_NETWORK &&
dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) {
fwprintf(stderr, L"Mount manager cannot be used on network drive.\n");
free(dokanOperations);
free(dokanOptions);
return EXIT_FAILURE;
}
if (!(dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) &&
wcscmp(MountPoint, L"") == 0) {
fwprintf(stderr, L"Mount Point required.\n");
free(dokanOperations);
free(dokanOptions);
return EXIT_FAILURE;
}
if ((dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) &&
(dokanOptions->Options & DOKAN_OPTION_CURRENT_SESSION)) {
fwprintf(stderr,
L"Mount Manager always mount the drive for all user sessions.\n");
free(dokanOperations);
free(dokanOptions);
return EXIT_FAILURE;
}
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
fwprintf(stderr, L"Control Handler is not set.\n");
}
// Add security name privilege. Required here to handle GetFileSecurity
// properly.
g_HasSeSecurityPrivilege = AddSeSecurityNamePrivilege();
if (!g_HasSeSecurityPrivilege) {
fwprintf(stderr, L"Failed to add security privilege to process\n");
fwprintf(stderr,
L"\t=> GetFileSecurity/SetFileSecurity may not work properly\n");
fwprintf(stderr, L"\t=> Please restart mirror sample with administrator "
L"rights to fix it\n");
}
if (g_DebugMode) {
dokanOptions->Options |= DOKAN_OPTION_DEBUG;
}
if (g_UseStdErr) {
dokanOptions->Options |= DOKAN_OPTION_STDERR;
}
dokanOptions->Options |= DOKAN_OPTION_ALT_STREAM;
ZeroMemory(dokanOperations, sizeof(DOKAN_OPERATIONS));
dokanOperations->ZwCreateFile = MirrorCreateFile;
dokanOperations->Cleanup = MirrorCleanup;
dokanOperations->CloseFile = MirrorCloseFile;
dokanOperations->ReadFile = MirrorReadFile;
dokanOperations->WriteFile = MirrorWriteFile;
dokanOperations->FlushFileBuffers = MirrorFlushFileBuffers;
dokanOperations->GetFileInformation = MirrorGetFileInformation;
dokanOperations->FindFiles = MirrorFindFiles;
dokanOperations->FindFilesWithPattern = NULL;
dokanOperations->SetFileAttributes = MirrorSetFileAttributes;
dokanOperations->SetFileTime = MirrorSetFileTime;
dokanOperations->DeleteFile = MirrorDeleteFile;
dokanOperations->DeleteDirectory = MirrorDeleteDirectory;
dokanOperations->MoveFile = MirrorMoveFile;
dokanOperations->SetEndOfFile = MirrorSetEndOfFile;
dokanOperations->SetAllocationSize = MirrorSetAllocationSize;
dokanOperations->LockFile = MirrorLockFile;
dokanOperations->UnlockFile = MirrorUnlockFile;
dokanOperations->GetFileSecurity = MirrorGetFileSecurity;
dokanOperations->SetFileSecurity = MirrorSetFileSecurity;
dokanOperations->GetDiskFreeSpace = NULL; // MirrorDokanGetDiskFreeSpace;
dokanOperations->GetVolumeInformation = MirrorGetVolumeInformation;
dokanOperations->Unmounted = MirrorUnmounted;
dokanOperations->FindStreams = MirrorFindStreams;
dokanOperations->Mounted = MirrorMounted;
status = DokanMain(dokanOptions, dokanOperations);
switch (status) {
case DOKAN_SUCCESS:
fprintf(stderr, "Success\n");
break;
case DOKAN_ERROR:
fprintf(stderr, "Error\n");
break;
case DOKAN_DRIVE_LETTER_ERROR:
fprintf(stderr, "Bad Drive letter\n");
break;
case DOKAN_DRIVER_INSTALL_ERROR:
fprintf(stderr, "Can't install driver\n");
break;
case DOKAN_START_ERROR:
fprintf(stderr, "Driver something wrong\n");
break;
case DOKAN_MOUNT_ERROR:
fprintf(stderr, "Can't assign a drive letter\n");
break;
case DOKAN_MOUNT_POINT_ERROR:
fprintf(stderr, "Mount point error\n");
break;
case DOKAN_VERSION_ERROR:
fprintf(stderr, "Version error\n");
break;
default:
fprintf(stderr, "Unknown error: %d\n", status);
break;
}
free(dokanOptions);
free(dokanOperations);
return EXIT_SUCCESS;
}