Something went wrong. Try again.
Reactos
Something went wrong. Try again.
65 kB · 1697 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698/* Copyright (c) Mark Harmstone 2017 * * This file is part of WinBtrfs. * * WinBtrfs is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public Licence as published by * the Free Software Foundation, either version 3 of the Licence, or * (at your option) any later version. * * WinBtrfs 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 Lesser General Public Licence for more details. * * You should have received a copy of the GNU Lesser General Public Licence * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "shellext.h"#include <windows.h>#include <strsafe.h>#include <stddef.h>#include <sys/stat.h>#include <iostream>#include "recv.h"#include "resource.h"#include "crc32c.h"
#ifndef _MSC_VER#ifdef __REACTOS__#define __cpuidex __cpuidex_ // prevent redeclaration#endif#include <cpuid.h>#else#include <intrin.h>#endif
const string EA_NTACL = "security.NTACL";const string EA_DOSATTRIB = "user.DOSATTRIB";const string EA_REPARSE = "user.reparse";const string EA_EA = "user.EA";const string XATTR_USER = "user.";
bool BtrfsRecv::find_tlv(uint8_t* data, ULONG datalen, uint16_t type, void** value, ULONG* len) { size_t off = 0;
while (off < datalen) { btrfs_send_tlv* tlv = (btrfs_send_tlv*)(data + off); uint8_t* payload = data + off + sizeof(btrfs_send_tlv);
if (off + sizeof(btrfs_send_tlv) + tlv->length > datalen) // file is truncated return false;
if (tlv->type == type) { *value = payload; *len = tlv->length; return true; }
off += sizeof(btrfs_send_tlv) + tlv->length; }
return false;}
void BtrfsRecv::cmd_subvol(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) { string name; BTRFS_UUID* uuid; uint64_t* gen; ULONG uuidlen, genlen; btrfs_create_subvol* bcs; NTSTATUS Status; IO_STATUS_BLOCK iosb;
{ char* namebuf; ULONG namelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
name = string(namebuf, namelen); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
if (uuidlen < sizeof(BTRFS_UUID)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid");
if (genlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t));
this->subvol_uuid = *uuid; this->stransid = *gen;
auto nameu = utf8_to_utf16(name);
size_t bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR)); bcs = (btrfs_create_subvol*)malloc(bcslen);
bcs->readonly = true; bcs->posix = true; bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); memcpy(bcs->name, nameu.c_str(), bcs->namelen);
Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
subvolpath = dirpath; subvolpath += L"\\"; subvolpath += nameu;
if (dir != INVALID_HANDLE_VALUE) CloseHandle(dir);
if (master != INVALID_HANDLE_VALUE) CloseHandle(master);
master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (master == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (dir == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
subvolpath += L"\\";
add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
num_received++;}
void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, uint64_t transid, const wstring& path) { subvol_cache sc;
sc.uuid = *uuid; sc.transid = transid; sc.path = path;
cache.push_back(sc);}
void BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) { string name; BTRFS_UUID *uuid, *parent_uuid; uint64_t *gen, *parent_transid; ULONG uuidlen, genlen, paruuidlen, partransidlen; btrfs_create_snapshot* bcs; NTSTATUS Status; IO_STATUS_BLOCK iosb; wstring parpath; btrfs_find_subvol bfs; WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH]; size_t bcslen;
{ char* namebuf; ULONG namelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
name = string(namebuf, namelen); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
if (uuidlen < sizeof(BTRFS_UUID)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid");
if (genlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&parent_uuid, &paruuidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
if (paruuidlen < sizeof(BTRFS_UUID)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", paruuidlen, sizeof(BTRFS_UUID));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&parent_transid, &partransidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
if (partransidlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", partransidlen, sizeof(uint64_t));
this->subvol_uuid = *uuid; this->stransid = *gen;
auto nameu = utf8_to_utf16(name);
bfs.uuid = *parent_uuid; bfs.ctransid = *parent_transid;
Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol), parpathw, sizeof(parpathw)); if (Status == STATUS_NOT_FOUND) throw string_error(IDS_RECV_CANT_FIND_PARENT_SUBVOL); else if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
parpath = volpathw; if (parpath.substr(parpath.length() - 1) == L"\\") parpath = parpath.substr(0, parpath.length() - 1);
parpath += parpathw;
{ win_handle subvol = CreateFileW(parpath.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (subvol == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, parpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
bcslen = offsetof(btrfs_create_snapshot, name[0]) + (nameu.length() * sizeof(WCHAR)); bcs = (btrfs_create_snapshot*)malloc(bcslen);
bcs->readonly = true; bcs->posix = true; bcs->subvol = subvol; bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); memcpy(bcs->name, nameu.c_str(), bcs->namelen);
Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, (ULONG)bcslen, nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_CREATE_SNAPSHOT_FAILED, Status, format_ntstatus(Status).c_str()); }
subvolpath = dirpath; subvolpath += L"\\"; subvolpath += nameu;
if (dir != INVALID_HANDLE_VALUE) CloseHandle(dir);
if (master != INVALID_HANDLE_VALUE) CloseHandle(master);
master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (master == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (dir == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
subvolpath += L"\\";
add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
num_received++;}
void BtrfsRecv::cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { uint64_t *inode, *rdev = nullptr, *mode = nullptr; ULONG inodelen; NTSTATUS Status; IO_STATUS_BLOCK iosb; btrfs_mknod* bmn; wstring nameu, pathlinku;
{ char* name; ULONG namelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
nameu = utf8_to_utf16(string(name, namelen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_INODE, (void**)&inode, &inodelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"inode");
if (inodelen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"inode", inodelen, sizeof(uint64_t));
if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) { ULONG rdevlen, modelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_RDEV, (void**)&rdev, &rdevlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"rdev");
if (rdevlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"rdev", rdev, sizeof(uint64_t));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode");
if (modelen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t)); } else if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) { char* pathlink; ULONG pathlinklen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&pathlink, &pathlinklen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
pathlinku = utf8_to_utf16(string(pathlink, pathlinklen)); }
size_t bmnsize = sizeof(btrfs_mknod) - sizeof(WCHAR) + (nameu.length() * sizeof(WCHAR)); bmn = (btrfs_mknod*)malloc(bmnsize);
bmn->inode = *inode;
if (cmd->cmd == BTRFS_SEND_CMD_MKDIR) bmn->type = BTRFS_TYPE_DIRECTORY; else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD) bmn->type = *mode & S_IFCHR ? BTRFS_TYPE_CHARDEV : BTRFS_TYPE_BLOCKDEV; else if (cmd->cmd == BTRFS_SEND_CMD_MKFIFO) bmn->type = BTRFS_TYPE_FIFO; else if (cmd->cmd == BTRFS_SEND_CMD_MKSOCK) bmn->type = BTRFS_TYPE_SOCKET; else bmn->type = BTRFS_TYPE_FILE;
bmn->st_rdev = rdev ? *rdev : 0; bmn->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); memcpy(bmn->name, nameu.c_str(), bmn->namelen);
Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_MKNOD, bmn, (ULONG)bmnsize, nullptr, 0); if (!NT_SUCCESS(Status)) { free(bmn); throw string_error(IDS_RECV_MKNOD_FAILED, Status, format_ntstatus(Status).c_str()); }
free(bmn);
if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) { REPARSE_DATA_BUFFER* rdb; btrfs_set_inode_info bsii;
size_t rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR));
if (rdblen >= 0x10000) throw string_error(IDS_RECV_PATH_TOO_LONG, funcname);
rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen);
rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK; rdb->ReparseDataLength = (uint16_t)(rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer)); rdb->Reserved = 0; rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.PrintNameOffset = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.PrintNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength); memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)), pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength);
win_handle h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) { free(rdb); throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); }
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_SET_REPARSE_POINT, rdb, (ULONG)rdblen, nullptr, 0); if (!NT_SUCCESS(Status)) { free(rdb); throw string_error(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str()); }
free(rdb);
memset(&bsii, 0, sizeof(btrfs_set_inode_info));
bsii.mode_changed = true; bsii.st_mode = 0777;
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) { uint64_t* mode; ULONG modelen;
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) { btrfs_set_inode_info bsii;
if (modelen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t));
win_handle h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
memset(&bsii, 0, sizeof(btrfs_set_inode_info));
bsii.mode_changed = true; bsii.st_mode = (uint32_t)*mode;
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); } }}
void BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu, path_tou;
{ char* path; ULONG path_len;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, path_len)); }
{ char* path_to; ULONG path_to_len;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_to");
path_tou = utf8_to_utf16(string(path_to, path_to_len)); }
if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str())) throw string_error(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str());}
void BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu, path_linku;
{ char* path; ULONG path_len;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, path_len)); }
{ char* path_link; ULONG path_link_len;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
path_linku = utf8_to_utf16(string(path_link, path_link_len)); }
if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), nullptr)) throw string_error(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str());}
void BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu; ULONG att;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
att = GetFileAttributesW((subvolpath + pathu).c_str()); if (att == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
if (!DeleteFileW((subvolpath + pathu).c_str())) throw string_error(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());}
void BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu; ULONG att;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
att = GetFileAttributesW((subvolpath + pathu).c_str()); if (att == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
if (!RemoveDirectoryW((subvolpath + pathu).c_str())) throw string_error(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());}
void BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { string xattrname; uint8_t* xattrdata; ULONG xattrdatalen; wstring pathu;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
{ char* xattrnamebuf; ULONG xattrnamelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
xattrname = string(xattrnamebuf, xattrnamelen); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data");
if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && xattrname != EA_DOSATTRIB && xattrname != EA_EA && xattrname != EA_REPARSE) { ULONG att;
auto streamname = utf8_to_utf16(xattrname);
att = GetFileAttributesW((subvolpath + pathu).c_str()); if (att == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
streamname = streamname.substr(XATTR_USER.length());
win_handle h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
if (xattrdatalen > 0) { if (!WriteFile(h, xattrdata, xattrdatalen, nullptr, nullptr)) throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); } } else { IO_STATUS_BLOCK iosb; NTSTATUS Status; ULONG perms = FILE_WRITE_ATTRIBUTES; btrfs_set_xattr* bsxa;
if (xattrname == EA_NTACL) perms |= WRITE_DAC | WRITE_OWNER; else if (xattrname == EA_EA) perms |= FILE_WRITE_EA;
win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length() + xattrdatalen; bsxa = (btrfs_set_xattr*)malloc(bsxalen); if (!bsxa) throw string_error(IDS_OUT_OF_MEMORY);
bsxa->namelen = (uint16_t)xattrname.length(); bsxa->valuelen = (uint16_t)xattrdatalen; memcpy(bsxa->data, xattrname.c_str(), xattrname.length()); memcpy(&bsxa->data[xattrname.length()], xattrdata, xattrdatalen);
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0); if (!NT_SUCCESS(Status)) { free(bsxa); throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str()); }
free(bsxa); }}
void BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu; string xattrname;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
{ char* xattrnamebuf; ULONG xattrnamelen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
xattrname = string(xattrnamebuf, xattrnamelen); }
if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && xattrname != EA_DOSATTRIB && xattrname != EA_EA) { // deleting stream ULONG att;
auto streamname = utf8_to_utf16(xattrname);
streamname = streamname.substr(XATTR_USER.length());
att = GetFileAttributesW((subvolpath + pathu).c_str()); if (att == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str())) throw string_error(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); } } else { IO_STATUS_BLOCK iosb; NTSTATUS Status; ULONG perms = FILE_WRITE_ATTRIBUTES; btrfs_set_xattr* bsxa;
if (xattrname == EA_NTACL) perms |= WRITE_DAC | WRITE_OWNER; else if (xattrname == EA_EA) perms |= FILE_WRITE_EA;
win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length(); bsxa = (btrfs_set_xattr*)malloc(bsxalen); if (!bsxa) throw string_error(IDS_OUT_OF_MEMORY);
bsxa->namelen = (uint16_t)(xattrname.length()); bsxa->valuelen = 0; memcpy(bsxa->data, xattrname.c_str(), xattrname.length());
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0); if (!NT_SUCCESS(Status)) { free(bsxa); throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str()); }
free(bsxa); }}
void BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { uint64_t* offset; uint8_t* writedata; ULONG offsetlen, datalen; wstring pathu; HANDLE h; LARGE_INTEGER offli; NTSTATUS Status; IO_STATUS_BLOCK iosb;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset");
if (offsetlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"data");
if (lastwritepath != pathu) { FILE_BASIC_INFO fbi;
if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
CloseHandle(lastwritefile);
lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str()); if (lastwriteatt == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
lastwritepath = pathu; lastwritefile = h;
memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
fbi.LastWriteTime.QuadPart = -1;
Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation); if (!NT_SUCCESS(Status)) throw ntstatus_error(Status); } else h = lastwritefile;
offli.QuadPart = *offset;
if (SetFilePointer(h, offli.LowPart, &offli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (!WriteFile(h, writedata, datalen, nullptr, nullptr)) throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());}
void BtrfsRecv::cmd_clone(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { uint64_t *offset, *cloneoffset, *clonetransid, *clonelen; BTRFS_UUID* cloneuuid; ULONG i, offsetlen, cloneoffsetlen, cloneuuidlen, clonetransidlen, clonelenlen; wstring pathu, clonepathu, clonepar; btrfs_find_subvol bfs; NTSTATUS Status; IO_STATUS_BLOCK iosb; WCHAR cloneparw[MAX_PATH]; DUPLICATE_EXTENTS_DATA ded; LARGE_INTEGER filesize; bool found = false;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset");
if (offsetlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_LENGTH, (void**)&clonelen, &clonelenlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_len");
if (clonelenlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_len", clonelenlen, sizeof(uint64_t));
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&cloneuuid, &cloneuuidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
if (cloneuuidlen < sizeof(BTRFS_UUID)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", cloneuuidlen, sizeof(BTRFS_UUID));
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&clonetransid, &clonetransidlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
if (clonetransidlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", clonetransidlen, sizeof(uint64_t));
{ char* clonepath; ULONG clonepathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_PATH, (void**)&clonepath, &clonepathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_path");
clonepathu = utf8_to_utf16(string(clonepath, clonepathlen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_OFFSET, (void**)&cloneoffset, &cloneoffsetlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_offset");
if (cloneoffsetlen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_offset", cloneoffsetlen, sizeof(uint64_t));
for (i = 0; i < cache.size(); i++) { if (!memcmp(cloneuuid, &cache[i].uuid, sizeof(BTRFS_UUID)) && *clonetransid == cache[i].transid) { clonepar = cache[i].path; found = true; break; } }
if (!found) { WCHAR volpathw[MAX_PATH];
bfs.uuid = *cloneuuid; bfs.ctransid = *clonetransid;
Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol), cloneparw, sizeof(cloneparw)); if (Status == STATUS_NOT_FOUND) throw string_error(IDS_RECV_CANT_FIND_CLONE_SUBVOL); else if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
clonepar = volpathw; if (clonepar.substr(clonepar.length() - 1) == L"\\") clonepar = clonepar.substr(0, clonepar.length() - 1);
clonepar += cloneparw; clonepar += L"\\";
add_cache_entry(cloneuuid, *clonetransid, clonepar); }
{ win_handle src = CreateFileW((clonepar + clonepathu).c_str(), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (src == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, (clonepar + clonepathu).c_str(), GetLastError(), format_message(GetLastError()).c_str());
win_handle dest = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (dest == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
if (!GetFileSizeEx(dest, &filesize)) throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if ((uint64_t)filesize.QuadPart < *offset + *clonelen) { LARGE_INTEGER sizeli;
sizeli.QuadPart = *offset + *clonelen;
if (SetFilePointer(dest, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (!SetEndOfFile(dest)) throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
ded.FileHandle = src; ded.SourceFileOffset.QuadPart = *cloneoffset; ded.TargetFileOffset.QuadPart = *offset; ded.ByteCount.QuadPart = *clonelen;
Status = NtFsControlFile(dest, nullptr, nullptr, nullptr, &iosb, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_DUPLICATE_EXTENTS_FAILED, Status, format_ntstatus(Status).c_str()); }}
void BtrfsRecv::cmd_truncate(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { uint64_t* size; ULONG sizelen; wstring pathu; LARGE_INTEGER sizeli; DWORD att;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_SIZE, (void**)&size, &sizelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"size");
if (sizelen < sizeof(uint64_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"size", sizelen, sizeof(uint64_t));
att = GetFileAttributesW((subvolpath + pathu).c_str()); if (att == INVALID_FILE_ATTRIBUTES) throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
{ win_handle h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
sizeli.QuadPart = *size;
if (SetFilePointer(h, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
if (!SetEndOfFile(h)) throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
if (att & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }}
void BtrfsRecv::cmd_chmod(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { win_handle h; uint32_t* mode; ULONG modelen; wstring pathu; btrfs_set_inode_info bsii; NTSTATUS Status; IO_STATUS_BLOCK iosb;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode");
if (modelen < sizeof(uint32_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint32_t));
h = CreateFileW((subvolpath + pathu).c_str(), WRITE_DAC, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
memset(&bsii, 0, sizeof(btrfs_set_inode_info));
bsii.mode_changed = true; bsii.st_mode = *mode;
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());}
void BtrfsRecv::cmd_chown(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { win_handle h; uint32_t *uid, *gid; ULONG uidlen, gidlen; wstring pathu; btrfs_set_inode_info bsii;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES | WRITE_OWNER | WRITE_DAC, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
memset(&bsii, 0, sizeof(btrfs_set_inode_info));
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_UID, (void**)&uid, &uidlen)) { if (uidlen < sizeof(uint32_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uid", uidlen, sizeof(uint32_t));
bsii.uid_changed = true; bsii.st_uid = *uid; }
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_GID, (void**)&gid, &gidlen)) { if (gidlen < sizeof(uint32_t)) throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"gid", gidlen, sizeof(uint32_t));
bsii.gid_changed = true; bsii.st_gid = *gid; }
if (bsii.uid_changed || bsii.gid_changed) { NTSTATUS Status; IO_STATUS_BLOCK iosb;
Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); }}
static __inline uint64_t unix_time_to_win(BTRFS_TIME* t) { return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;}
void BtrfsRecv::cmd_utimes(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { wstring pathu; win_handle h; FILE_BASIC_INFO fbi; BTRFS_TIME* time; ULONG timelen; IO_STATUS_BLOCK iosb; NTSTATUS Status;
{ char* path; ULONG pathlen;
if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
pathu = utf8_to_utf16(string(path, pathlen)); }
h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (h == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) fbi.CreationTime.QuadPart = unix_time_to_win(time);
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) fbi.LastAccessTime.QuadPart = unix_time_to_win(time);
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) fbi.LastWriteTime.QuadPart = unix_time_to_win(time);
if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) fbi.ChangeTime.QuadPart = unix_time_to_win(time);
Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation); if (!NT_SUCCESS(Status)) throw ntstatus_error(Status);}
static void delete_directory(const wstring& dir) { WIN32_FIND_DATAW fff;
fff_handle h = FindFirstFileW((dir + L"*").c_str(), &fff);
if (h == INVALID_HANDLE_VALUE) return;
do { wstring file;
file = fff.cFileName;
if (file != L"." && file != L"..") { if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY) SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) delete_directory(dir + file + L"\\"); else RemoveDirectoryW((dir + file).c_str()); } else DeleteFileW((dir + file).c_str()); } } while (FindNextFileW(h, &fff));
RemoveDirectoryW(dir.c_str());}
static bool check_csum(btrfs_send_command* cmd, uint8_t* data) { uint32_t crc32 = cmd->csum, calc;
cmd->csum = 0;
calc = calc_crc32c(0, (uint8_t*)cmd, sizeof(btrfs_send_command));
if (cmd->length > 0) calc = calc_crc32c(calc, data, cmd->length);
return calc == crc32 ? true : false;}
void BtrfsRecv::do_recv(const win_handle& f, uint64_t* pos, uint64_t size, const win_handle& parent) { try { btrfs_send_header header; bool ended = false;
if (!ReadFile(f, &header, sizeof(btrfs_send_header), nullptr, nullptr)) throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
*pos += sizeof(btrfs_send_header);
if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic))) throw string_error(IDS_RECV_NOT_A_SEND_STREAM);
if (header.version > 1) throw string_error(IDS_RECV_UNSUPPORTED_VERSION, header.version);
SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536);
lastwritefile = INVALID_HANDLE_VALUE; lastwritepath = L""; lastwriteatt = 0;
while (true) { btrfs_send_command cmd; uint8_t* data = nullptr; ULONG progress;
if (cancelling) break;
progress = (ULONG)((float)*pos * 65536.0f / (float)size); SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0);
if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), nullptr, nullptr)) { if (GetLastError() != ERROR_HANDLE_EOF) throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
break; }
*pos += sizeof(btrfs_send_command);
if (cmd.length > 0) { if (*pos + cmd.length > size) throw string_error(IDS_RECV_FILE_TRUNCATED);
data = (uint8_t*)malloc(cmd.length); if (!data) throw string_error(IDS_OUT_OF_MEMORY); }
try { if (data) { if (!ReadFile(f, data, cmd.length, nullptr, nullptr)) throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
*pos += cmd.length; }
if (!check_csum(&cmd, data)) throw string_error(IDS_RECV_CSUM_ERROR);
if (cmd.cmd == BTRFS_SEND_CMD_END) { ended = true; break; }
if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) { if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
CloseHandle(lastwritefile);
lastwritefile = INVALID_HANDLE_VALUE; lastwritepath = L""; lastwriteatt = 0; }
switch (cmd.cmd) { case BTRFS_SEND_CMD_SUBVOL: cmd_subvol(hwnd, &cmd, data, parent); break;
case BTRFS_SEND_CMD_SNAPSHOT: cmd_snapshot(hwnd, &cmd, data, parent); break;
case BTRFS_SEND_CMD_MKFILE: case BTRFS_SEND_CMD_MKDIR: case BTRFS_SEND_CMD_MKNOD: case BTRFS_SEND_CMD_MKFIFO: case BTRFS_SEND_CMD_MKSOCK: case BTRFS_SEND_CMD_SYMLINK: cmd_mkfile(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_RENAME: cmd_rename(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_LINK: cmd_link(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_UNLINK: cmd_unlink(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_RMDIR: cmd_rmdir(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_SET_XATTR: cmd_setxattr(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_REMOVE_XATTR: cmd_removexattr(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_WRITE: cmd_write(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_CLONE: cmd_clone(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_TRUNCATE: cmd_truncate(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_CHMOD: cmd_chmod(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_CHOWN: cmd_chown(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_UTIMES: cmd_utimes(hwnd, &cmd, data); break;
case BTRFS_SEND_CMD_UPDATE_EXTENT: // does nothing break;
default: throw string_error(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd); } } catch (...) { if (data) free(data); throw; }
if (data) free(data); }
if (lastwritefile != INVALID_HANDLE_VALUE) { if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); }
CloseHandle(lastwritefile); }
if (!ended && !cancelling) throw string_error(IDS_RECV_FILE_TRUNCATED);
if (!cancelling) { NTSTATUS Status; IO_STATUS_BLOCK iosb; btrfs_received_subvol brs;
brs.generation = stransid; brs.uuid = subvol_uuid;
Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol), nullptr, 0); if (!NT_SUCCESS(Status)) throw string_error(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); }
CloseHandle(dir);
if (master != INVALID_HANDLE_VALUE) CloseHandle(master); } catch (...) { if (subvolpath != L"") { ULONG attrib;
attrib = GetFileAttributesW(subvolpath.c_str()); attrib &= ~FILE_ATTRIBUTE_READONLY;
if (SetFileAttributesW(subvolpath.c_str(), attrib)) delete_directory(subvolpath); }
throw; }}
#if defined(_X86_) || defined(_AMD64_)static void check_cpu() { bool have_sse42 = false;
#ifndef _MSC_VER { uint32_t eax, ebx, ecx, edx;
__cpuid(1, eax, ebx, ecx, edx);
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) have_sse42 = ecx & bit_SSE4_2; }#else { int cpu_info[4];
__cpuid(cpu_info, 1); have_sse42 = (unsigned int)cpu_info[2] & (1 << 20); }#endif
if (have_sse42) calc_crc32c = calc_crc32c_hw;}#endif
DWORD BtrfsRecv::recv_thread() { LARGE_INTEGER size; uint64_t pos = 0; bool b = true;
running = true;
try { win_handle f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (f == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str());
if (!GetFileSizeEx(f, &size)) throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
{ win_handle parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (parent == INVALID_HANDLE_VALUE) throw string_error(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
do { do_recv(f, &pos, size.QuadPart, parent); } while (pos < (uint64_t)size.QuadPart); } } catch (const exception& e) { auto msg = utf8_to_utf16(e.what());
SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
b = false; }
if (b && hwnd) { wstring s;
SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0);
if (num_received == 1) { load_string(module, IDS_RECV_SUCCESS, s); SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str()); } else { wstring t;
load_string(module, IDS_RECV_SUCCESS_PLURAL, s);
wstring_sprintf(t, s, num_received);
SetDlgItemTextW(hwnd, IDC_RECV_MSG, t.c_str()); }
load_string(module, IDS_RECV_BUTTON_OK, s);
SetDlgItemTextW(hwnd, IDCANCEL, s.c_str()); }
thread = nullptr; running = false;
return 0;}
static DWORD WINAPI global_recv_thread(LPVOID lpParameter) { BtrfsRecv* br = (BtrfsRecv*)lpParameter;
return br->recv_thread();}
INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: try { this->hwnd = hwndDlg; thread = CreateThread(nullptr, 0, global_recv_thread, this, 0, nullptr);
if (!thread) throw string_error(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str()); } catch (const exception& e) { auto msg = utf8_to_utf16(e.what());
SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0); } break;
case WM_COMMAND: switch (HIWORD(wParam)) { case BN_CLICKED: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: if (running) { wstring s;
cancelling = true;
if (!load_string(module, IDS_RECV_CANCELLED, s)) throw last_error(GetLastError());
SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str()); SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0);
if (!load_string(module, IDS_RECV_BUTTON_OK, s)) throw last_error(GetLastError());
SetDlgItemTextW(hwnd, IDCANCEL, s.c_str()); } else EndDialog(hwndDlg, 1);
return true; } break; } break; }
return false;}
static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { BtrfsRecv* br;
if (uMsg == WM_INITDIALOG) { SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); br = (BtrfsRecv*)lParam; } else { br = (BtrfsRecv*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); }
if (br) return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam); else return false;}
void BtrfsRecv::Open(HWND hwnd, const wstring& file, const wstring& path, bool quiet) { streamfile = file; dirpath = path; subvolpath = L"";
#if defined(_X86_) || defined(_AMD64_) check_cpu();#endif
if (quiet) recv_thread(); else { if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0) throw last_error(GetLastError()); }}
extern "C" void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { try { OPENFILENAMEW ofn; WCHAR file[MAX_PATH]; win_handle token; TOKEN_PRIVILEGES* tp; LUID luid; ULONG tplen;
set_dpi_aware();
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) throw last_error(GetLastError());
tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES)); tp = (TOKEN_PRIVILEGES*)malloc(tplen); if (!tp) throw string_error(IDS_OUT_OF_MEMORY);
tp->PrivilegeCount = 3;
#ifdef __clang__#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warray-bounds"#endif // __clang__
if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) { free(tp); throw last_error(GetLastError()); }
tp->Privileges[0].Luid = luid; tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) { free(tp); throw last_error(GetLastError()); }
tp->Privileges[1].Luid = luid; tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) { free(tp); throw last_error(GetLastError()); }
tp->Privileges[2].Luid = luid; tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) { free(tp); throw last_error(GetLastError()); }
#ifdef __clang__ #pragma clang diagnostic pop#endif // __clang__
file[0] = 0;
memset(&ofn, 0, sizeof(OPENFILENAMEW)); ofn.lStructSize = sizeof(OPENFILENAMEW); ofn.hwndOwner = hwnd; ofn.hInstance = module; ofn.lpstrFile = file; ofn.nMaxFile = sizeof(file) / sizeof(WCHAR); ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
if (GetOpenFileNameW(&ofn)) { BtrfsRecv recv;
recv.Open(hwnd, file, lpszCmdLine, false); }
free(tp); } catch (const exception& e) { error_message(hwnd, e.what()); }}
extern "C" void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { try { vector<wstring> args;
command_line_to_args(lpszCmdLine, args);
if (args.size() >= 2) { win_handle token; TOKEN_PRIVILEGES* tp; ULONG tplen; LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) return;
tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES)); tp = (TOKEN_PRIVILEGES*)malloc(tplen); if (!tp) return;
tp->PrivilegeCount = 3;
#ifdef __clang__#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warray-bounds"#endif // __clang__
if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) { free(tp); return; }
tp->Privileges[0].Luid = luid; tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) { free(tp); return; }
tp->Privileges[1].Luid = luid; tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) { free(tp); return; }
tp->Privileges[2].Luid = luid; tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
#ifdef __clang__#pragma clang diagnostic pop#endif // __clang__
if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) { free(tp); return; }
free(tp);
BtrfsRecv br; br.Open(nullptr, args[0], args[1], true); } } catch (const exception& e) { cerr << "Error: " << e.what() << endl; }}