mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
403 lines
13 KiB
C++
403 lines
13 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2021 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include "DocanyMount.h"
|
|
#include "../Interface/BackupFileSystem.h"
|
|
#include "../Interface/Server.h"
|
|
#include <dokan/dokan.h>
|
|
#include <sddl.h>
|
|
#include "../stringtools.h"
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext,
|
|
ACCESS_MASK DesiredAccess, ULONG FileAttributes,
|
|
ULONG ShareAccess, ULONG CreateDisposition,
|
|
ULONG CreateOptions, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
IBackupFileSystem* fs = reinterpret_cast<IBackupFileSystem*>(DokanFileInfo->DokanOptions->GlobalContext);
|
|
|
|
size_t offs = 0;
|
|
if (*FileName == '\\')
|
|
offs = 1;
|
|
|
|
IFsFile* file = fs->openFile(Server->ConvertFromWchar(FileName + offs), MODE_READ);
|
|
|
|
if (file == nullptr)
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
DokanFileInfo->Context = reinterpret_cast<ULONG64>(file);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static void DOKAN_CALLBACK
|
|
DokanCloseFile(LPCWSTR FileName,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
}
|
|
|
|
static void DOKAN_CALLBACK
|
|
DokanCleanup(LPCWSTR FileName,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
delete reinterpret_cast<IFsFile*>(DokanFileInfo->Context);
|
|
DokanFileInfo->Context = 0;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanReadFile(LPCWSTR FileName, LPVOID Buffer,
|
|
DWORD BufferLength,
|
|
LPDWORD ReadLength,
|
|
LONGLONG Offset,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
IFsFile* file = reinterpret_cast<IFsFile*>(DokanFileInfo->Context);
|
|
|
|
if (file == nullptr)
|
|
{
|
|
IBackupFileSystem* fs = reinterpret_cast<IBackupFileSystem*>(DokanFileInfo->DokanOptions->GlobalContext);
|
|
|
|
size_t offs = 0;
|
|
if (*FileName == '\\')
|
|
offs = 1;
|
|
|
|
file = fs->openFile(Server->ConvertFromWchar(FileName + offs), MODE_READ);
|
|
|
|
if (file == nullptr)
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
bool has_read_error = false;
|
|
*ReadLength = file->Read(Offset, reinterpret_cast<char*>(Buffer), BufferLength, &has_read_error);
|
|
|
|
if (DokanFileInfo->Context == 0)
|
|
{
|
|
delete file;
|
|
}
|
|
|
|
if (has_read_error)
|
|
{
|
|
if (errno == 23)
|
|
return STATUS_CRC_ERROR;
|
|
|
|
return DokanNtStatusFromWin32(errno);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanWriteFile(LPCWSTR FileName, LPCVOID Buffer,
|
|
DWORD NumberOfBytesToWrite,
|
|
LPDWORD NumberOfBytesWritten,
|
|
LONGLONG Offset,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanFlushFileBuffers(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanGetFileInformation(
|
|
LPCWSTR FileName, LPBY_HANDLE_FILE_INFORMATION HandleFileInformation,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
memset(HandleFileInformation, 0, sizeof(BY_HANDLE_FILE_INFORMATION));
|
|
|
|
IFsFile* file = reinterpret_cast<IFsFile*>(DokanFileInfo->Context);
|
|
|
|
LARGE_INTEGER fsize;
|
|
fsize.QuadPart = file->Size();
|
|
|
|
HandleFileInformation->nFileSizeLow = fsize.LowPart;
|
|
HandleFileInformation->nFileSizeHigh = fsize.HighPart;
|
|
|
|
IBackupFileSystem* fs = reinterpret_cast<IBackupFileSystem*>(DokanFileInfo->DokanOptions->GlobalContext);
|
|
|
|
size_t offs = 0;
|
|
if (*FileName == '\\')
|
|
offs = 1;
|
|
|
|
int ftype = fs->getFileType(Server->ConvertFromWchar(FileName + offs));
|
|
|
|
if (ftype & EFileType_Directory)
|
|
HandleFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanFindFiles(LPCWSTR FileName,
|
|
PFillFindData FillFindData,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
IBackupFileSystem* fs = reinterpret_cast<IBackupFileSystem*>(DokanFileInfo->DokanOptions->GlobalContext);
|
|
|
|
size_t offs = 0;
|
|
if (*FileName == '\\')
|
|
offs = 1;
|
|
|
|
std::string ldir = Server->ConvertFromWchar(FileName + offs);
|
|
|
|
int top_path = 0;
|
|
for (char ch : ldir)
|
|
if (ch == '\\')
|
|
++top_path;
|
|
|
|
std::vector<SFile> files = fs->listFiles(ldir);
|
|
for (SFile& file : files)
|
|
{
|
|
if (!ldir.empty() && top_path == 0 &&
|
|
(file.name == ".hashes" ||
|
|
next(file.name, 0, ".symlink_")))
|
|
continue;
|
|
|
|
if (ldir.empty() &&
|
|
file.name.find(".new") != std::string::npos)
|
|
continue;
|
|
|
|
WIN32_FIND_DATAW find_data = {};
|
|
std::wstring wfilename = Server->ConvertToWchar(file.name);
|
|
memcpy(find_data.cFileName, wfilename.c_str(), (std::min)(259ULL, wfilename.size()*sizeof(wchar_t)));
|
|
LARGE_INTEGER fsize;
|
|
fsize.QuadPart = file.size;
|
|
find_data.nFileSizeLow = fsize.LowPart;
|
|
find_data.nFileSizeHigh = fsize.HighPart;
|
|
LARGE_INTEGER created;
|
|
created.QuadPart = file.created;
|
|
find_data.ftCreationTime.dwLowDateTime = created.LowPart;
|
|
find_data.ftCreationTime.dwHighDateTime = created.HighPart;
|
|
LARGE_INTEGER last_modified;
|
|
last_modified.QuadPart = file.last_modified;
|
|
find_data.ftLastWriteTime.dwLowDateTime = last_modified.LowPart;
|
|
find_data.ftLastWriteTime.dwHighDateTime = last_modified.HighPart;
|
|
LARGE_INTEGER accessed;
|
|
accessed.QuadPart = file.accessed;
|
|
find_data.ftLastAccessTime.dwLowDateTime = accessed.LowPart;
|
|
find_data.ftLastAccessTime.dwHighDateTime = accessed.HighPart;
|
|
find_data.dwFileAttributes = file.isdir ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
|
FillFindData(&find_data, DokanFileInfo);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanDeleteFile(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanDeleteDirectory(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanMoveFile(LPCWSTR FileName,
|
|
LPCWSTR NewFileName, BOOL ReplaceIfExisting,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanLockFile(LPCWSTR FileName,
|
|
LONGLONG ByteOffset,
|
|
LONGLONG Length,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanSetEndOfFile(
|
|
LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanSetAllocationSize(
|
|
LPCWSTR FileName, LONGLONG AllocSize, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanSetFileAttributes(
|
|
LPCWSTR FileName, DWORD FileAttributes, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanSetFileTime(LPCWSTR FileName, CONST FILETIME* CreationTime,
|
|
CONST FILETIME* LastAccessTime, CONST FILETIME* LastWriteTime,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK
|
|
DokanUnlockFile(LPCWSTR FileName, LONGLONG ByteOffset, LONGLONG Length,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanGetFileSecurity(
|
|
LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG BufferLength,
|
|
PULONG LengthNeeded, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
const TCHAR* szSD = TEXT("D:") // Discretionary ACL
|
|
TEXT("(A;OICI;GA;;;WD)"); // Allow full control
|
|
// to everyone
|
|
|
|
BOOL b = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
szSD,
|
|
SDDL_REVISION_1,
|
|
&SecurityDescriptor,
|
|
NULL);
|
|
|
|
if (!b)
|
|
{
|
|
return DokanNtStatusFromWin32(GetLastError());
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanSetFileSecurity(
|
|
LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG SecurityDescriptorLength,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanGetVolumeInformation(
|
|
LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, LPDWORD VolumeSerialNumber,
|
|
LPDWORD MaximumComponentLength, LPDWORD FileSystemFlags,
|
|
LPWSTR FileSystemNameBuffer, DWORD FileSystemNameSize,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
wcscpy_s(VolumeNameBuffer, VolumeNameSize, L"URBACKUP");
|
|
|
|
if (VolumeSerialNumber)
|
|
*VolumeSerialNumber = 0x19831116;
|
|
if (MaximumComponentLength)
|
|
*MaximumComponentLength = 255;
|
|
|
|
if (FileSystemFlags)
|
|
{
|
|
*FileSystemFlags = FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK |
|
|
FILE_PERSISTENT_ACLS | FILE_NAMED_STREAMS | FILE_CASE_SENSITIVE_SEARCH |
|
|
FILE_CASE_PRESERVED_NAMES;
|
|
}
|
|
|
|
wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"NTFS");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanGetDiskFreeSpace(
|
|
PULONGLONG FreeBytesAvailable, PULONGLONG TotalNumberOfBytes,
|
|
PULONGLONG TotalNumberOfFreeBytes, PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
*FreeBytesAvailable = 10LL * 1024 * 1024 * 1024 * 1024;
|
|
*TotalNumberOfFreeBytes = *FreeBytesAvailable;
|
|
*TotalNumberOfBytes = *FreeBytesAvailable * 2;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DOKAN_CALLBACK
|
|
DokanFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData,
|
|
PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanMounted(PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK DokanUnmounted(PDOKAN_FILE_INFO DokanFileInfo)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
bool dokany_mount(IBackupFileSystem* fs, const std::string& mount_path)
|
|
{
|
|
DOKAN_OPTIONS dokanOptions = {};
|
|
|
|
dokanOptions.Version = DOKAN_VERSION;
|
|
dokanOptions.ThreadCount = 0;
|
|
|
|
std::wstring mount_path_w = Server->ConvertToWchar(mount_path);
|
|
dokanOptions.MountPoint = mount_path_w.data();
|
|
//dokanOptions.Options |= DOKAN_OPTION_NETWORK;
|
|
dokanOptions.Options |= DOKAN_OPTION_WRITE_PROTECT;
|
|
dokanOptions.Options |= DOKAN_OPTION_CASE_SENSITIVE;
|
|
dokanOptions.Options |= DOKAN_OPTION_DEBUG;
|
|
dokanOptions.Options |= DOKAN_OPTION_STDERR;
|
|
dokanOptions.GlobalContext = reinterpret_cast<ULONG64>(fs);
|
|
|
|
DOKAN_OPERATIONS dokanOperations = {};
|
|
|
|
dokanOperations.ZwCreateFile = DokanCreateFile;
|
|
dokanOperations.Cleanup = DokanCleanup;
|
|
dokanOperations.CloseFile = DokanCloseFile;
|
|
dokanOperations.ReadFile = DokanReadFile;
|
|
dokanOperations.WriteFile = DokanWriteFile;
|
|
dokanOperations.FlushFileBuffers = DokanFlushFileBuffers;
|
|
dokanOperations.GetFileInformation = DokanGetFileInformation;
|
|
dokanOperations.FindFiles = DokanFindFiles;
|
|
dokanOperations.FindFilesWithPattern = NULL;
|
|
dokanOperations.SetFileAttributes = DokanSetFileAttributes;
|
|
dokanOperations.SetFileTime = DokanSetFileTime;
|
|
dokanOperations.DeleteFile = DokanDeleteFile;
|
|
dokanOperations.DeleteDirectory = DokanDeleteDirectory;
|
|
dokanOperations.MoveFile = DokanMoveFile;
|
|
dokanOperations.SetEndOfFile = DokanSetEndOfFile;
|
|
dokanOperations.SetAllocationSize = DokanSetAllocationSize;
|
|
dokanOperations.LockFile = DokanLockFile;
|
|
dokanOperations.UnlockFile = DokanUnlockFile;
|
|
dokanOperations.GetFileSecurity = DokanGetFileSecurity;
|
|
dokanOperations.SetFileSecurity = DokanSetFileSecurity;
|
|
dokanOperations.GetDiskFreeSpace = DokanGetDiskFreeSpace;
|
|
dokanOperations.GetVolumeInformation = DokanGetVolumeInformation;
|
|
dokanOperations.Unmounted = DokanUnmounted;
|
|
dokanOperations.FindStreams = DokanFindStreams;
|
|
dokanOperations.Mounted = DokanMounted;
|
|
|
|
int status = DokanMain(&dokanOptions, &dokanOperations);
|
|
|
|
if (status != DOKAN_SUCCESS)
|
|
return false;
|
|
|
|
return true;
|
|
}
|