mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
497 lines
11 KiB
C++
497 lines
11 KiB
C++
#include "../Interface/Server.h"
|
|
#include "../Interface/ThreadPool.h"
|
|
#include "../stringtools.h"
|
|
#include "ImdiskSrv.h"
|
|
#include "vhdfile.h"
|
|
#include "FileWrapper.h"
|
|
#include <Windows.h>
|
|
#include <Subauth.h>
|
|
#include <imdproxy.h>
|
|
#include <imdisk.h>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
namespace
|
|
{
|
|
HRESULT ModifyPrivilege(
|
|
IN LPCTSTR szPrivilege,
|
|
IN BOOL fEnable)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TOKEN_PRIVILEGES NewState;
|
|
LUID luid;
|
|
HANDLE hToken = NULL;
|
|
|
|
// Open the process token for this process.
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken))
|
|
{
|
|
Server->Log("Failed OpenProcessToken", LL_ERROR);
|
|
return ERROR_FUNCTION_FAILED;
|
|
}
|
|
|
|
// Get the local unique ID for the privilege.
|
|
if (!LookupPrivilegeValue(NULL,
|
|
szPrivilege,
|
|
&luid))
|
|
{
|
|
CloseHandle(hToken);
|
|
Server->Log("Failed LookupPrivilegeValue", LL_ERROR);
|
|
return ERROR_FUNCTION_FAILED;
|
|
}
|
|
|
|
// Assign values to the TOKEN_PRIVILEGE structure.
|
|
NewState.PrivilegeCount = 1;
|
|
NewState.Privileges[0].Luid = luid;
|
|
NewState.Privileges[0].Attributes =
|
|
(fEnable ? SE_PRIVILEGE_ENABLED : 0);
|
|
|
|
// Adjust the token privilege.
|
|
if (!AdjustTokenPrivileges(hToken,
|
|
FALSE,
|
|
&NewState,
|
|
0,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
Server->Log("Failed AdjustTokenPrivileges", LL_ERROR);
|
|
hr = ERROR_FUNCTION_FAILED;
|
|
}
|
|
|
|
// Close the handle.
|
|
CloseHandle(hToken);
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool stopImdiskSvc()
|
|
{
|
|
SC_HANDLE sc = OpenSCManager(NULL, NULL,
|
|
SC_MANAGER_ALL_ACCESS);
|
|
|
|
if (sc == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SC_HANDLE service = OpenService(
|
|
sc, L"ImDskSvc",
|
|
SERVICE_STOP | SERVICE_QUERY_STATUS |
|
|
SERVICE_CHANGE_CONFIG);
|
|
|
|
if (service == NULL)
|
|
{
|
|
CloseServiceHandle(sc);
|
|
return false;
|
|
}
|
|
|
|
bool ret = true;
|
|
do
|
|
{
|
|
SERVICE_STATUS ssp;
|
|
if (!ControlService(service, SERVICE_CONTROL_STOP, &ssp))
|
|
{
|
|
ret = false;
|
|
}
|
|
|
|
if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
|
{
|
|
ret = false;
|
|
}
|
|
|
|
} while (0);
|
|
|
|
CloseServiceHandle(sc);
|
|
CloseServiceHandle(service);
|
|
|
|
return ret;
|
|
}
|
|
|
|
class ScopedCloseHandle
|
|
{
|
|
public:
|
|
ScopedCloseHandle(HANDLE h)
|
|
: h(h) {}
|
|
|
|
~ScopedCloseHandle()
|
|
{
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(h);
|
|
}
|
|
}
|
|
|
|
void reset(HANDLE hNew = INVALID_HANDLE_VALUE)
|
|
{
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(h);
|
|
}
|
|
h = hNew;
|
|
}
|
|
|
|
void release()
|
|
{
|
|
h = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
private:
|
|
HANDLE h;
|
|
};
|
|
|
|
class ImdiskConnection : public IThread
|
|
{
|
|
public:
|
|
ImdiskConnection(HANDLE hPipe)
|
|
: hPipe(hPipe)
|
|
{}
|
|
|
|
~ImdiskConnection()
|
|
{
|
|
if (hPipe != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hPipe);
|
|
}
|
|
}
|
|
|
|
bool send_error(DWORD code)
|
|
{
|
|
IMDPROXY_CONNECT_RESP connect_resp = {};
|
|
connect_resp.error_code = code;
|
|
DWORD written;
|
|
BOOL b = WriteFile(hPipe, &connect_resp, sizeof(connect_resp), &written, NULL);
|
|
return b && written == sizeof(connect_resp);
|
|
}
|
|
|
|
void connect()
|
|
{
|
|
IMDPROXY_CONNECT_REQ connect_req;
|
|
|
|
DWORD read;
|
|
BOOL b = ReadFile(hPipe, &connect_req.request_code, sizeof(connect_req.request_code), &read, NULL);
|
|
if (!b || read != sizeof(connect_req.request_code))
|
|
{
|
|
return;
|
|
}
|
|
if (connect_req.request_code != IMDPROXY_REQ_CONNECT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
b = ReadFile(hPipe, reinterpret_cast<char*>(&connect_req) + sizeof(connect_req.request_code),
|
|
sizeof(connect_req) - sizeof(connect_req.request_code), &read, NULL);
|
|
if (!b || read != sizeof(connect_req) - sizeof(connect_req.request_code))
|
|
{
|
|
return;
|
|
}
|
|
if (connect_req.length == 0 || connect_req.length > 520)
|
|
{
|
|
return;
|
|
}
|
|
std::wstring connect_string;
|
|
connect_string.resize(connect_req.length);
|
|
b = ReadFile(hPipe, &connect_string[0], static_cast<DWORD>(connect_req.length), &read, NULL);
|
|
if (!b || read != connect_req.length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IMDISK_PROXY_TYPE(connect_req.flags) != IMDISK_PROXY_TYPE_TCP)
|
|
{
|
|
send_error(ERROR_NOT_SUPPORTED);
|
|
return;
|
|
}
|
|
|
|
size_t l_col = connect_string.find_last_of(':');
|
|
|
|
int64 offset = 0;
|
|
int64 length = -1;
|
|
if (l_col > 3)
|
|
{
|
|
std::string slice = Server->ConvertFromWchar(connect_string.substr(l_col + 1));
|
|
offset = watoi64(getuntil("-", slice));
|
|
length = watoi64(getafter("-", slice));
|
|
connect_string.erase(l_col, connect_string.size() - l_col);
|
|
}
|
|
else
|
|
{
|
|
offset = 512 * 1024;
|
|
}
|
|
|
|
std::string img_fn = Server->ConvertFromWchar(connect_string);
|
|
|
|
std::string ext = findextension(img_fn);
|
|
|
|
std::unique_ptr<IFile> imgf;
|
|
|
|
std::unique_ptr<VHDFile> vhdfile;
|
|
if (ext == "raw")
|
|
{
|
|
imgf.reset(Server->openFile(img_fn, MODE_READ));
|
|
}
|
|
else
|
|
{
|
|
vhdfile.reset(new VHDFile(img_fn, true, 0));
|
|
if (vhdfile->isOpen())
|
|
{
|
|
imgf.reset(new FileWrapper(vhdfile.get(), 0));
|
|
}
|
|
}
|
|
|
|
if(imgf.get()==NULL)
|
|
{
|
|
send_error(ERROR_FILE_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (length == -1)
|
|
{
|
|
length = imgf->Size() - offset;
|
|
}
|
|
else
|
|
{
|
|
length = (std::min)(length, imgf->Size() - offset);
|
|
}
|
|
|
|
std::string uid;
|
|
uid.resize(16);
|
|
Server->randomFill(&uid[0], uid.size());
|
|
|
|
std::wstring pipe_name = L"\\\\.\\PIPE\\" + Server->ConvertToWchar(bytesToHex(uid));
|
|
|
|
HANDLE hConnectPipe1 = CreateNamedPipe(pipe_name.c_str(),
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, 0, PIPE_UNLIMITED_INSTANCES,
|
|
0, 0, 0, NULL);
|
|
|
|
if (hConnectPipe1 == INVALID_HANDLE_VALUE)
|
|
{
|
|
send_error(GetLastError());
|
|
return;
|
|
}
|
|
|
|
ScopedCloseHandle closeConnectPipe1(hConnectPipe1);
|
|
|
|
OVERLAPPED ovl = {};
|
|
ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
ScopedCloseHandle closeEvent(ovl.hEvent);
|
|
|
|
if (!ConnectNamedPipe(hConnectPipe1, &ovl)
|
|
&& GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
send_error(GetLastError());
|
|
return;
|
|
}
|
|
|
|
HANDLE hConnectPipe2 = CreateFile(pipe_name.c_str(),
|
|
GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
|
|
if (hConnectPipe2 == INVALID_HANDLE_VALUE)
|
|
{
|
|
send_error(GetLastError());
|
|
return;
|
|
}
|
|
|
|
ScopedCloseHandle closeConnectPipe2(hConnectPipe2);
|
|
|
|
if (WaitForSingleObject(ovl.hEvent, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
send_error(ERROR_CONNECTION_ABORTED);
|
|
return;
|
|
}
|
|
|
|
closeEvent.reset();
|
|
|
|
IMDPROXY_CONNECT_RESP connect_resp = {};
|
|
|
|
{
|
|
HANDLE hDriver = CreateFile(IMDISK_CTL_DOSDEV_NAME,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hDriver == INVALID_HANDLE_VALUE)
|
|
{
|
|
send_error(GetLastError());
|
|
return;
|
|
}
|
|
|
|
ScopedCloseHandle closeDriver(hDriver);
|
|
|
|
DWORD bytes_returned;
|
|
if (!DeviceIoControl(hDriver, IOCTL_IMDISK_REFERENCE_HANDLE,
|
|
&hConnectPipe1, sizeof(hConnectPipe1), &connect_resp.object_ptr,
|
|
sizeof(connect_resp.object_ptr), &bytes_returned, NULL))
|
|
{
|
|
send_error(GetLastError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
DWORD written;
|
|
b = WriteFile(hPipe, &connect_resp, sizeof(connect_resp), &written, NULL);
|
|
if (!b || written != sizeof(connect_resp))
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::vector<char> buf;
|
|
|
|
while (true)
|
|
{
|
|
ULONGLONG request_code;
|
|
b = ReadFile(hConnectPipe2, &request_code, sizeof(request_code), &read, NULL);
|
|
if (!b || read != sizeof(request_code))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (request_code == IMDPROXY_REQ_INFO)
|
|
{
|
|
IMDPROXY_INFO_RESP info_resp;
|
|
info_resp.file_size = length;
|
|
info_resp.flags = IMDPROXY_FLAG_RO;
|
|
info_resp.req_alignment = 512;
|
|
|
|
b = WriteFile(hConnectPipe2, &info_resp, sizeof(info_resp), &written, NULL);
|
|
|
|
if (!b || written != sizeof(info_resp))
|
|
{
|
|
Server->Log("Error sending info response. Err: " + convert((int64)GetLastError()), LL_ERROR);
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
else if (request_code != IMDPROXY_REQ_READ)
|
|
{
|
|
Server->Log("Unsupported ImDisk request code "+ convert(request_code), LL_ERROR);
|
|
|
|
ULONGLONG resp = ENODEV;
|
|
WriteFile(hConnectPipe2, &resp, sizeof(resp), &written, NULL);
|
|
return;
|
|
}
|
|
|
|
IMDPROXY_READ_REQ read_req = {};
|
|
|
|
b = ReadFile(hConnectPipe2, &read_req.offset, sizeof(read_req) - sizeof(request_code), &read, NULL);
|
|
if (!b || read!=sizeof(read_req)-sizeof(request_code))
|
|
{
|
|
Server->Log("Reading read request failed. Err: " + convert((int64)GetLastError()), LL_ERROR);
|
|
break;
|
|
}
|
|
|
|
if (buf.size() < read_req.length + sizeof(IMDPROXY_READ_RESP))
|
|
{
|
|
buf.resize(read_req.length + sizeof(IMDPROXY_READ_RESP));
|
|
}
|
|
|
|
//Server->Log("Read req offset=" + convert(read_req.offset) + " length=" + convert(read_req.length), LL_INFO);
|
|
|
|
IMDPROXY_READ_RESP* read_resp = reinterpret_cast<IMDPROXY_READ_RESP*>(buf.data());
|
|
read_resp->length = 0;
|
|
read_resp->errorno = 0;
|
|
|
|
if (read_req.length > 0)
|
|
{
|
|
bool has_read_error = false;
|
|
_u32 rc = imgf->Read(read_req.offset+offset, buf.data() + sizeof(IMDPROXY_READ_RESP)+ read_resp->length,
|
|
static_cast<_u32>(read_req.length), &has_read_error);
|
|
if (has_read_error)
|
|
{
|
|
read_resp->errorno = EIO;
|
|
read_resp->length = 0;
|
|
rc = 0;
|
|
}
|
|
else
|
|
{
|
|
read_resp->length += rc;
|
|
}
|
|
}
|
|
|
|
//Server->Log("Read resp length=" + convert(read_resp->length), LL_INFO);
|
|
b = WriteFile(hConnectPipe2, read_resp, static_cast<DWORD>(sizeof(IMDPROXY_READ_RESP) + read_resp->length), &written, NULL);
|
|
if (!b || written != sizeof(IMDPROXY_READ_RESP) + read_resp->length)
|
|
{
|
|
Server->Log("Sending read response failed. Err: " + convert((int64)GetLastError()), LL_ERROR);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void operator()()
|
|
{
|
|
connect();
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
HANDLE hPipe;
|
|
};
|
|
}
|
|
|
|
|
|
ImdiskSrv::ImdiskSrv()
|
|
{
|
|
|
|
}
|
|
|
|
bool ImdiskSrv::installed()
|
|
{
|
|
HANDLE hDriver = CreateFile(IMDISK_CTL_DOSDEV_NAME,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hDriver != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hDriver);
|
|
}
|
|
|
|
return hDriver != INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
void ImdiskSrv::operator()()
|
|
{
|
|
HRESULT hr = ModifyPrivilege(SE_TCB_NAME, TRUE);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
Server->Log("Failed to modify SE_TCB privilege. VHD mounting won't work.", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
stopImdiskSvc();
|
|
|
|
while (true)
|
|
{
|
|
HANDLE hNamedPipe = CreateNamedPipe(IMDPROXY_SVC_PIPE_DOSDEV_NAME,
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH,
|
|
0,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
0, 0, 0, NULL);
|
|
|
|
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
Server->Log("Error creating named pipe to ImProxy. Err: " + convert((int64)GetLastError()), LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (!ConnectNamedPipe(hNamedPipe, NULL))
|
|
{
|
|
Server->Log("Error connecting named pipe to ImProxy. Err: " + convert((int64)GetLastError()), LL_ERROR);
|
|
CloseHandle(hNamedPipe);
|
|
return;
|
|
}
|
|
|
|
Server->createThread(new ImdiskConnection(hNamedPipe), "imdisk conn");
|
|
}
|
|
}
|