1 /* Copyright (c) Mark Harmstone 2017 2 * 3 * This file is part of WinBtrfs. 4 * 5 * WinBtrfs is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public Licence as published by 7 * the Free Software Foundation, either version 3 of the Licence, or 8 * (at your option) any later version. 9 * 10 * WinBtrfs is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public Licence for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public Licence 16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "shellext.h" 19 #include <windows.h> 20 #include <strsafe.h> 21 #include <stddef.h> 22 #include <sys/stat.h> 23 #include <iostream> 24 #include "recv.h" 25 #include "resource.h" 26 #include "crc32c.h" 27 28 29 #ifndef _MSC_VER 30 #ifdef __REACTOS__ 31 #define __cpuidex __cpuidex_ // prevent redeclaration 32 #endif 33 #include <cpuid.h> 34 #else 35 #include <intrin.h> 36 #endif 37 38 39 const string EA_NTACL = "security.NTACL"; 40 const string EA_DOSATTRIB = "user.DOSATTRIB"; 41 const string EA_REPARSE = "user.reparse"; 42 const string EA_EA = "user.EA"; 43 const string XATTR_USER = "user."; 44 45 bool BtrfsRecv::find_tlv(uint8_t* data, ULONG datalen, uint16_t type, void** value, ULONG* len) { 46 size_t off = 0; 47 48 while (off < datalen) { 49 btrfs_send_tlv* tlv = (btrfs_send_tlv*)(data + off); 50 uint8_t* payload = data + off + sizeof(btrfs_send_tlv); 51 52 if (off + sizeof(btrfs_send_tlv) + tlv->length > datalen) // file is truncated 53 return false; 54 55 if (tlv->type == type) { 56 *value = payload; 57 *len = tlv->length; 58 return true; 59 } 60 61 off += sizeof(btrfs_send_tlv) + tlv->length; 62 } 63 64 return false; 65 } 66 67 void BtrfsRecv::cmd_subvol(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) { 68 string name; 69 BTRFS_UUID* uuid; 70 uint64_t* gen; 71 ULONG uuidlen, genlen; 72 btrfs_create_subvol* bcs; 73 NTSTATUS Status; 74 IO_STATUS_BLOCK iosb; 75 76 { 77 char* namebuf; 78 ULONG namelen; 79 80 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen)) 81 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 82 83 name = string(namebuf, namelen); 84 } 85 86 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) 87 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid"); 88 89 if (uuidlen < sizeof(BTRFS_UUID)) 90 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID)); 91 92 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) 93 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid"); 94 95 if (genlen < sizeof(uint64_t)) 96 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t)); 97 98 this->subvol_uuid = *uuid; 99 this->stransid = *gen; 100 101 auto nameu = utf8_to_utf16(name); 102 103 size_t bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR)); 104 bcs = (btrfs_create_subvol*)malloc(bcslen); 105 106 bcs->readonly = true; 107 bcs->posix = true; 108 bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); 109 memcpy(bcs->name, nameu.c_str(), bcs->namelen); 110 111 Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); 112 if (!NT_SUCCESS(Status)) 113 throw string_error(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 114 115 subvolpath = dirpath; 116 subvolpath += L"\\"; 117 subvolpath += nameu; 118 119 if (dir != INVALID_HANDLE_VALUE) 120 CloseHandle(dir); 121 122 if (master != INVALID_HANDLE_VALUE) 123 CloseHandle(master); 124 125 master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 126 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 127 if (master == INVALID_HANDLE_VALUE) 128 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 129 130 Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); 131 if (!NT_SUCCESS(Status)) 132 throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 133 134 dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, 135 FILE_SHARE_READ | FILE_SHARE_WRITE, 136 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 137 if (dir == INVALID_HANDLE_VALUE) 138 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 139 140 subvolpath += L"\\"; 141 142 add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath); 143 144 num_received++; 145 } 146 147 void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, uint64_t transid, const wstring& path) { 148 subvol_cache sc; 149 150 sc.uuid = *uuid; 151 sc.transid = transid; 152 sc.path = path; 153 154 cache.push_back(sc); 155 } 156 157 void BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) { 158 string name; 159 BTRFS_UUID *uuid, *parent_uuid; 160 uint64_t *gen, *parent_transid; 161 ULONG uuidlen, genlen, paruuidlen, partransidlen; 162 btrfs_create_snapshot* bcs; 163 NTSTATUS Status; 164 IO_STATUS_BLOCK iosb; 165 wstring parpath; 166 btrfs_find_subvol bfs; 167 WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH]; 168 size_t bcslen; 169 170 { 171 char* namebuf; 172 ULONG namelen; 173 174 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen)) 175 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 176 177 name = string(namebuf, namelen); 178 } 179 180 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) 181 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid"); 182 183 if (uuidlen < sizeof(BTRFS_UUID)) 184 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID)); 185 186 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) 187 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid"); 188 189 if (genlen < sizeof(uint64_t)) 190 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t)); 191 192 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&parent_uuid, &paruuidlen)) 193 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid"); 194 195 if (paruuidlen < sizeof(BTRFS_UUID)) 196 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", paruuidlen, sizeof(BTRFS_UUID)); 197 198 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&parent_transid, &partransidlen)) 199 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid"); 200 201 if (partransidlen < sizeof(uint64_t)) 202 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", partransidlen, sizeof(uint64_t)); 203 204 this->subvol_uuid = *uuid; 205 this->stransid = *gen; 206 207 auto nameu = utf8_to_utf16(name); 208 209 bfs.uuid = *parent_uuid; 210 bfs.ctransid = *parent_transid; 211 212 Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol), 213 parpathw, sizeof(parpathw)); 214 if (Status == STATUS_NOT_FOUND) 215 throw string_error(IDS_RECV_CANT_FIND_PARENT_SUBVOL); 216 else if (!NT_SUCCESS(Status)) 217 throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 218 219 if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 220 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 221 222 parpath = volpathw; 223 if (parpath.substr(parpath.length() - 1) == L"\\") 224 parpath = parpath.substr(0, parpath.length() - 1); 225 226 parpath += parpathw; 227 228 { 229 win_handle subvol = CreateFileW(parpath.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 230 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); 231 if (subvol == INVALID_HANDLE_VALUE) 232 throw string_error(IDS_RECV_CANT_OPEN_PATH, parpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 233 234 bcslen = offsetof(btrfs_create_snapshot, name[0]) + (nameu.length() * sizeof(WCHAR)); 235 bcs = (btrfs_create_snapshot*)malloc(bcslen); 236 237 bcs->readonly = true; 238 bcs->posix = true; 239 bcs->subvol = subvol; 240 bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); 241 memcpy(bcs->name, nameu.c_str(), bcs->namelen); 242 243 Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, (ULONG)bcslen, nullptr, 0); 244 if (!NT_SUCCESS(Status)) 245 throw string_error(IDS_RECV_CREATE_SNAPSHOT_FAILED, Status, format_ntstatus(Status).c_str()); 246 } 247 248 subvolpath = dirpath; 249 subvolpath += L"\\"; 250 subvolpath += nameu; 251 252 if (dir != INVALID_HANDLE_VALUE) 253 CloseHandle(dir); 254 255 if (master != INVALID_HANDLE_VALUE) 256 CloseHandle(master); 257 258 master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 259 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 260 if (master == INVALID_HANDLE_VALUE) 261 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 262 263 Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0); 264 if (!NT_SUCCESS(Status)) 265 throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 266 267 dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, 268 FILE_SHARE_READ | FILE_SHARE_WRITE, 269 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 270 if (dir == INVALID_HANDLE_VALUE) 271 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 272 273 subvolpath += L"\\"; 274 275 add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath); 276 277 num_received++; 278 } 279 280 void BtrfsRecv::cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 281 uint64_t *inode, *rdev = nullptr, *mode = nullptr; 282 ULONG inodelen; 283 NTSTATUS Status; 284 IO_STATUS_BLOCK iosb; 285 btrfs_mknod* bmn; 286 wstring nameu, pathlinku; 287 288 { 289 char* name; 290 ULONG namelen; 291 292 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) 293 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 294 295 nameu = utf8_to_utf16(string(name, namelen)); 296 } 297 298 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_INODE, (void**)&inode, &inodelen)) 299 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"inode"); 300 301 if (inodelen < sizeof(uint64_t)) 302 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"inode", inodelen, sizeof(uint64_t)); 303 304 if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) { 305 ULONG rdevlen, modelen; 306 307 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_RDEV, (void**)&rdev, &rdevlen)) 308 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"rdev"); 309 310 if (rdevlen < sizeof(uint64_t)) 311 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"rdev", rdev, sizeof(uint64_t)); 312 313 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) 314 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode"); 315 316 if (modelen < sizeof(uint64_t)) 317 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t)); 318 } else if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) { 319 char* pathlink; 320 ULONG pathlinklen; 321 322 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&pathlink, &pathlinklen)) 323 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link"); 324 325 pathlinku = utf8_to_utf16(string(pathlink, pathlinklen)); 326 } 327 328 size_t bmnsize = sizeof(btrfs_mknod) - sizeof(WCHAR) + (nameu.length() * sizeof(WCHAR)); 329 bmn = (btrfs_mknod*)malloc(bmnsize); 330 331 bmn->inode = *inode; 332 333 if (cmd->cmd == BTRFS_SEND_CMD_MKDIR) 334 bmn->type = BTRFS_TYPE_DIRECTORY; 335 else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD) 336 bmn->type = *mode & S_IFCHR ? BTRFS_TYPE_CHARDEV : BTRFS_TYPE_BLOCKDEV; 337 else if (cmd->cmd == BTRFS_SEND_CMD_MKFIFO) 338 bmn->type = BTRFS_TYPE_FIFO; 339 else if (cmd->cmd == BTRFS_SEND_CMD_MKSOCK) 340 bmn->type = BTRFS_TYPE_SOCKET; 341 else 342 bmn->type = BTRFS_TYPE_FILE; 343 344 bmn->st_rdev = rdev ? *rdev : 0; 345 bmn->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR)); 346 memcpy(bmn->name, nameu.c_str(), bmn->namelen); 347 348 Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_MKNOD, bmn, (ULONG)bmnsize, nullptr, 0); 349 if (!NT_SUCCESS(Status)) { 350 free(bmn); 351 throw string_error(IDS_RECV_MKNOD_FAILED, Status, format_ntstatus(Status).c_str()); 352 } 353 354 free(bmn); 355 356 if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) { 357 REPARSE_DATA_BUFFER* rdb; 358 btrfs_set_inode_info bsii; 359 360 size_t rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR)); 361 362 if (rdblen >= 0x10000) 363 throw string_error(IDS_RECV_PATH_TOO_LONG, funcname); 364 365 rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen); 366 367 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK; 368 rdb->ReparseDataLength = (uint16_t)(rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer)); 369 rdb->Reserved = 0; 370 rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; 371 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); 372 rdb->SymbolicLinkReparseBuffer.PrintNameOffset = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); 373 rdb->SymbolicLinkReparseBuffer.PrintNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR)); 374 rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE; 375 376 memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength); 377 memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)), 378 pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength); 379 380 win_handle h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 381 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 382 if (h == INVALID_HANDLE_VALUE) { 383 free(rdb); 384 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 385 } 386 387 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_SET_REPARSE_POINT, rdb, (ULONG)rdblen, nullptr, 0); 388 if (!NT_SUCCESS(Status)) { 389 free(rdb); 390 throw string_error(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str()); 391 } 392 393 free(rdb); 394 395 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 396 397 bsii.mode_changed = true; 398 bsii.st_mode = 0777; 399 400 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); 401 if (!NT_SUCCESS(Status)) 402 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); 403 } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) { 404 uint64_t* mode; 405 ULONG modelen; 406 407 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) { 408 btrfs_set_inode_info bsii; 409 410 if (modelen < sizeof(uint64_t)) 411 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t)); 412 413 win_handle h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 414 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 415 if (h == INVALID_HANDLE_VALUE) 416 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 417 418 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 419 420 bsii.mode_changed = true; 421 bsii.st_mode = (uint32_t)*mode; 422 423 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); 424 if (!NT_SUCCESS(Status)) 425 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); 426 } 427 } 428 } 429 430 void BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 431 wstring pathu, path_tou; 432 433 { 434 char* path; 435 ULONG path_len; 436 437 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) 438 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 439 440 pathu = utf8_to_utf16(string(path, path_len)); 441 } 442 443 { 444 char* path_to; 445 ULONG path_to_len; 446 447 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len)) 448 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_to"); 449 450 path_tou = utf8_to_utf16(string(path_to, path_to_len)); 451 } 452 453 if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str())) 454 throw string_error(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 455 } 456 457 void BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 458 wstring pathu, path_linku; 459 460 { 461 char* path; 462 ULONG path_len; 463 464 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) 465 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 466 467 pathu = utf8_to_utf16(string(path, path_len)); 468 } 469 470 { 471 char* path_link; 472 ULONG path_link_len; 473 474 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len)) 475 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link"); 476 477 path_linku = utf8_to_utf16(string(path_link, path_link_len)); 478 } 479 480 if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), nullptr)) 481 throw string_error(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 482 } 483 484 void BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 485 wstring pathu; 486 ULONG att; 487 488 { 489 char* path; 490 ULONG pathlen; 491 492 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 493 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 494 495 pathu = utf8_to_utf16(string(path, pathlen)); 496 } 497 498 att = GetFileAttributesW((subvolpath + pathu).c_str()); 499 if (att == INVALID_FILE_ATTRIBUTES) 500 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 501 502 if (att & FILE_ATTRIBUTE_READONLY) { 503 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) 504 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 505 } 506 507 if (!DeleteFileW((subvolpath + pathu).c_str())) 508 throw string_error(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 509 } 510 511 void BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 512 wstring pathu; 513 ULONG att; 514 515 { 516 char* path; 517 ULONG pathlen; 518 519 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 520 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 521 522 pathu = utf8_to_utf16(string(path, pathlen)); 523 } 524 525 att = GetFileAttributesW((subvolpath + pathu).c_str()); 526 if (att == INVALID_FILE_ATTRIBUTES) 527 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 528 529 if (att & FILE_ATTRIBUTE_READONLY) { 530 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) 531 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 532 } 533 534 if (!RemoveDirectoryW((subvolpath + pathu).c_str())) 535 throw string_error(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 536 } 537 538 void BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 539 string xattrname; 540 uint8_t* xattrdata; 541 ULONG xattrdatalen; 542 wstring pathu; 543 544 { 545 char* path; 546 ULONG pathlen; 547 548 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 549 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 550 551 pathu = utf8_to_utf16(string(path, pathlen)); 552 } 553 554 { 555 char* xattrnamebuf; 556 ULONG xattrnamelen; 557 558 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen)) 559 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name"); 560 561 xattrname = string(xattrnamebuf, xattrnamelen); 562 } 563 564 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen)) 565 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data"); 566 567 if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && 568 xattrname != EA_DOSATTRIB && xattrname != EA_EA && xattrname != EA_REPARSE) { 569 ULONG att; 570 571 auto streamname = utf8_to_utf16(xattrname); 572 573 att = GetFileAttributesW((subvolpath + pathu).c_str()); 574 if (att == INVALID_FILE_ATTRIBUTES) 575 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 576 577 if (att & FILE_ATTRIBUTE_READONLY) { 578 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) 579 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 580 } 581 582 streamname = streamname.substr(XATTR_USER.length()); 583 584 win_handle h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0, 585 nullptr, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, nullptr); 586 if (h == INVALID_HANDLE_VALUE) 587 throw string_error(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str()); 588 589 if (xattrdatalen > 0) { 590 if (!WriteFile(h, xattrdata, xattrdatalen, nullptr, nullptr)) 591 throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 592 } 593 594 if (att & FILE_ATTRIBUTE_READONLY) { 595 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) 596 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 597 } 598 } else { 599 IO_STATUS_BLOCK iosb; 600 NTSTATUS Status; 601 ULONG perms = FILE_WRITE_ATTRIBUTES; 602 btrfs_set_xattr* bsxa; 603 604 if (xattrname == EA_NTACL) 605 perms |= WRITE_DAC | WRITE_OWNER; 606 else if (xattrname == EA_EA) 607 perms |= FILE_WRITE_EA; 608 609 win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 610 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr); 611 if (h == INVALID_HANDLE_VALUE) 612 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 613 614 size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length() + xattrdatalen; 615 bsxa = (btrfs_set_xattr*)malloc(bsxalen); 616 if (!bsxa) 617 throw string_error(IDS_OUT_OF_MEMORY); 618 619 bsxa->namelen = (uint16_t)xattrname.length(); 620 bsxa->valuelen = (uint16_t)xattrdatalen; 621 memcpy(bsxa->data, xattrname.c_str(), xattrname.length()); 622 memcpy(&bsxa->data[xattrname.length()], xattrdata, xattrdatalen); 623 624 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0); 625 if (!NT_SUCCESS(Status)) { 626 free(bsxa); 627 throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str()); 628 } 629 630 free(bsxa); 631 } 632 } 633 634 void BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 635 wstring pathu; 636 string xattrname; 637 638 { 639 char* path; 640 ULONG pathlen; 641 642 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 643 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 644 645 pathu = utf8_to_utf16(string(path, pathlen)); 646 } 647 648 { 649 char* xattrnamebuf; 650 ULONG xattrnamelen; 651 652 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen)) 653 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name"); 654 655 xattrname = string(xattrnamebuf, xattrnamelen); 656 } 657 658 if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && xattrname != EA_DOSATTRIB && xattrname != EA_EA) { // deleting stream 659 ULONG att; 660 661 auto streamname = utf8_to_utf16(xattrname); 662 663 streamname = streamname.substr(XATTR_USER.length()); 664 665 att = GetFileAttributesW((subvolpath + pathu).c_str()); 666 if (att == INVALID_FILE_ATTRIBUTES) 667 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 668 669 if (att & FILE_ATTRIBUTE_READONLY) { 670 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) 671 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 672 } 673 674 if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str())) 675 throw string_error(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str()); 676 677 if (att & FILE_ATTRIBUTE_READONLY) { 678 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) 679 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 680 } 681 } else { 682 IO_STATUS_BLOCK iosb; 683 NTSTATUS Status; 684 ULONG perms = FILE_WRITE_ATTRIBUTES; 685 btrfs_set_xattr* bsxa; 686 687 if (xattrname == EA_NTACL) 688 perms |= WRITE_DAC | WRITE_OWNER; 689 else if (xattrname == EA_EA) 690 perms |= FILE_WRITE_EA; 691 692 win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 693 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr); 694 if (h == INVALID_HANDLE_VALUE) 695 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 696 697 size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length(); 698 bsxa = (btrfs_set_xattr*)malloc(bsxalen); 699 if (!bsxa) 700 throw string_error(IDS_OUT_OF_MEMORY); 701 702 bsxa->namelen = (uint16_t)(xattrname.length()); 703 bsxa->valuelen = 0; 704 memcpy(bsxa->data, xattrname.c_str(), xattrname.length()); 705 706 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0); 707 if (!NT_SUCCESS(Status)) { 708 free(bsxa); 709 throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str()); 710 } 711 712 free(bsxa); 713 } 714 } 715 716 void BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 717 uint64_t* offset; 718 uint8_t* writedata; 719 ULONG offsetlen, datalen; 720 wstring pathu; 721 HANDLE h; 722 LARGE_INTEGER offli; 723 NTSTATUS Status; 724 IO_STATUS_BLOCK iosb; 725 726 { 727 char* path; 728 ULONG pathlen; 729 730 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 731 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 732 733 pathu = utf8_to_utf16(string(path, pathlen)); 734 } 735 736 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) 737 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset"); 738 739 if (offsetlen < sizeof(uint64_t)) 740 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t)); 741 742 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen)) 743 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"data"); 744 745 if (lastwritepath != pathu) { 746 FILE_BASIC_INFO fbi; 747 748 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { 749 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) 750 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 751 } 752 753 CloseHandle(lastwritefile); 754 755 lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str()); 756 if (lastwriteatt == INVALID_FILE_ATTRIBUTES) 757 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 758 759 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { 760 if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY)) 761 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 762 } 763 764 h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING, 765 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 766 if (h == INVALID_HANDLE_VALUE) 767 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 768 769 lastwritepath = pathu; 770 lastwritefile = h; 771 772 memset(&fbi, 0, sizeof(FILE_BASIC_INFO)); 773 774 fbi.LastWriteTime.QuadPart = -1; 775 776 Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation); 777 if (!NT_SUCCESS(Status)) 778 throw ntstatus_error(Status); 779 } else 780 h = lastwritefile; 781 782 offli.QuadPart = *offset; 783 784 if (SetFilePointer(h, offli.LowPart, &offli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 785 throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 786 787 if (!WriteFile(h, writedata, datalen, nullptr, nullptr)) 788 throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 789 } 790 791 void BtrfsRecv::cmd_clone(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 792 uint64_t *offset, *cloneoffset, *clonetransid, *clonelen; 793 BTRFS_UUID* cloneuuid; 794 ULONG i, offsetlen, cloneoffsetlen, cloneuuidlen, clonetransidlen, clonelenlen; 795 wstring pathu, clonepathu, clonepar; 796 btrfs_find_subvol bfs; 797 NTSTATUS Status; 798 IO_STATUS_BLOCK iosb; 799 WCHAR cloneparw[MAX_PATH]; 800 DUPLICATE_EXTENTS_DATA ded; 801 LARGE_INTEGER filesize; 802 bool found = false; 803 804 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) 805 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset"); 806 807 if (offsetlen < sizeof(uint64_t)) 808 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t)); 809 810 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_LENGTH, (void**)&clonelen, &clonelenlen)) 811 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_len"); 812 813 if (clonelenlen < sizeof(uint64_t)) 814 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_len", clonelenlen, sizeof(uint64_t)); 815 816 { 817 char* path; 818 ULONG pathlen; 819 820 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 821 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 822 823 pathu = utf8_to_utf16(string(path, pathlen)); 824 } 825 826 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&cloneuuid, &cloneuuidlen)) 827 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid"); 828 829 if (cloneuuidlen < sizeof(BTRFS_UUID)) 830 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", cloneuuidlen, sizeof(BTRFS_UUID)); 831 832 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&clonetransid, &clonetransidlen)) 833 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid"); 834 835 if (clonetransidlen < sizeof(uint64_t)) 836 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", clonetransidlen, sizeof(uint64_t)); 837 838 { 839 char* clonepath; 840 ULONG clonepathlen; 841 842 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_PATH, (void**)&clonepath, &clonepathlen)) 843 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_path"); 844 845 clonepathu = utf8_to_utf16(string(clonepath, clonepathlen)); 846 } 847 848 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_OFFSET, (void**)&cloneoffset, &cloneoffsetlen)) 849 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_offset"); 850 851 if (cloneoffsetlen < sizeof(uint64_t)) 852 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_offset", cloneoffsetlen, sizeof(uint64_t)); 853 854 for (i = 0; i < cache.size(); i++) { 855 if (!memcmp(cloneuuid, &cache[i].uuid, sizeof(BTRFS_UUID)) && *clonetransid == cache[i].transid) { 856 clonepar = cache[i].path; 857 found = true; 858 break; 859 } 860 } 861 862 if (!found) { 863 WCHAR volpathw[MAX_PATH]; 864 865 bfs.uuid = *cloneuuid; 866 bfs.ctransid = *clonetransid; 867 868 Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol), 869 cloneparw, sizeof(cloneparw)); 870 if (Status == STATUS_NOT_FOUND) 871 throw string_error(IDS_RECV_CANT_FIND_CLONE_SUBVOL); 872 else if (!NT_SUCCESS(Status)) 873 throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 874 875 if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) 876 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 877 878 clonepar = volpathw; 879 if (clonepar.substr(clonepar.length() - 1) == L"\\") 880 clonepar = clonepar.substr(0, clonepar.length() - 1); 881 882 clonepar += cloneparw; 883 clonepar += L"\\"; 884 885 add_cache_entry(cloneuuid, *clonetransid, clonepar); 886 } 887 888 { 889 win_handle src = CreateFileW((clonepar + clonepathu).c_str(), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 890 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); 891 if (src == INVALID_HANDLE_VALUE) 892 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, (clonepar + clonepathu).c_str(), GetLastError(), format_message(GetLastError()).c_str()); 893 894 win_handle dest = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 895 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); 896 if (dest == INVALID_HANDLE_VALUE) 897 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 898 899 if (!GetFileSizeEx(dest, &filesize)) 900 throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 901 902 if ((uint64_t)filesize.QuadPart < *offset + *clonelen) { 903 LARGE_INTEGER sizeli; 904 905 sizeli.QuadPart = *offset + *clonelen; 906 907 if (SetFilePointer(dest, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 908 throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 909 910 if (!SetEndOfFile(dest)) 911 throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 912 } 913 914 ded.FileHandle = src; 915 ded.SourceFileOffset.QuadPart = *cloneoffset; 916 ded.TargetFileOffset.QuadPart = *offset; 917 ded.ByteCount.QuadPart = *clonelen; 918 919 Status = NtFsControlFile(dest, nullptr, nullptr, nullptr, &iosb, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), 920 nullptr, 0); 921 if (!NT_SUCCESS(Status)) 922 throw string_error(IDS_RECV_DUPLICATE_EXTENTS_FAILED, Status, format_ntstatus(Status).c_str()); 923 } 924 } 925 926 void BtrfsRecv::cmd_truncate(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 927 uint64_t* size; 928 ULONG sizelen; 929 wstring pathu; 930 LARGE_INTEGER sizeli; 931 DWORD att; 932 933 { 934 char* path; 935 ULONG pathlen; 936 937 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 938 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 939 940 pathu = utf8_to_utf16(string(path, pathlen)); 941 } 942 943 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_SIZE, (void**)&size, &sizelen)) 944 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"size"); 945 946 if (sizelen < sizeof(uint64_t)) 947 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"size", sizelen, sizeof(uint64_t)); 948 949 att = GetFileAttributesW((subvolpath + pathu).c_str()); 950 if (att == INVALID_FILE_ATTRIBUTES) 951 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 952 953 if (att & FILE_ATTRIBUTE_READONLY) { 954 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) 955 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 956 } 957 958 { 959 win_handle h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, 960 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 961 962 if (h == INVALID_HANDLE_VALUE) 963 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 964 965 sizeli.QuadPart = *size; 966 967 if (SetFilePointer(h, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 968 throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 969 970 if (!SetEndOfFile(h)) 971 throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 972 } 973 974 if (att & FILE_ATTRIBUTE_READONLY) { 975 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) 976 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 977 } 978 } 979 980 void BtrfsRecv::cmd_chmod(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 981 win_handle h; 982 uint32_t* mode; 983 ULONG modelen; 984 wstring pathu; 985 btrfs_set_inode_info bsii; 986 NTSTATUS Status; 987 IO_STATUS_BLOCK iosb; 988 989 { 990 char* path; 991 ULONG pathlen; 992 993 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 994 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 995 996 pathu = utf8_to_utf16(string(path, pathlen)); 997 } 998 999 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) 1000 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode"); 1001 1002 if (modelen < sizeof(uint32_t)) 1003 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint32_t)); 1004 1005 h = CreateFileW((subvolpath + pathu).c_str(), WRITE_DAC, 0, nullptr, OPEN_EXISTING, 1006 FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); 1007 if (h == INVALID_HANDLE_VALUE) 1008 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 1009 1010 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 1011 1012 bsii.mode_changed = true; 1013 bsii.st_mode = *mode; 1014 1015 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); 1016 if (!NT_SUCCESS(Status)) 1017 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); 1018 } 1019 1020 void BtrfsRecv::cmd_chown(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 1021 win_handle h; 1022 uint32_t *uid, *gid; 1023 ULONG uidlen, gidlen; 1024 wstring pathu; 1025 btrfs_set_inode_info bsii; 1026 1027 { 1028 char* path; 1029 ULONG pathlen; 1030 1031 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 1032 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 1033 1034 pathu = utf8_to_utf16(string(path, pathlen)); 1035 } 1036 1037 h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES | WRITE_OWNER | WRITE_DAC, 0, nullptr, OPEN_EXISTING, 1038 FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); 1039 if (h == INVALID_HANDLE_VALUE) 1040 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 1041 1042 memset(&bsii, 0, sizeof(btrfs_set_inode_info)); 1043 1044 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_UID, (void**)&uid, &uidlen)) { 1045 if (uidlen < sizeof(uint32_t)) 1046 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uid", uidlen, sizeof(uint32_t)); 1047 1048 bsii.uid_changed = true; 1049 bsii.st_uid = *uid; 1050 } 1051 1052 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_GID, (void**)&gid, &gidlen)) { 1053 if (gidlen < sizeof(uint32_t)) 1054 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"gid", gidlen, sizeof(uint32_t)); 1055 1056 bsii.gid_changed = true; 1057 bsii.st_gid = *gid; 1058 } 1059 1060 if (bsii.uid_changed || bsii.gid_changed) { 1061 NTSTATUS Status; 1062 IO_STATUS_BLOCK iosb; 1063 1064 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0); 1065 if (!NT_SUCCESS(Status)) 1066 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str()); 1067 } 1068 } 1069 1070 static __inline uint64_t unix_time_to_win(BTRFS_TIME* t) { 1071 return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000; 1072 } 1073 1074 void BtrfsRecv::cmd_utimes(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) { 1075 wstring pathu; 1076 win_handle h; 1077 FILE_BASIC_INFO fbi; 1078 BTRFS_TIME* time; 1079 ULONG timelen; 1080 IO_STATUS_BLOCK iosb; 1081 NTSTATUS Status; 1082 1083 { 1084 char* path; 1085 ULONG pathlen; 1086 1087 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) 1088 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path"); 1089 1090 pathu = utf8_to_utf16(string(path, pathlen)); 1091 } 1092 1093 h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING, 1094 FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr); 1095 if (h == INVALID_HANDLE_VALUE) 1096 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 1097 1098 memset(&fbi, 0, sizeof(FILE_BASIC_INFO)); 1099 1100 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) 1101 fbi.CreationTime.QuadPart = unix_time_to_win(time); 1102 1103 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) 1104 fbi.LastAccessTime.QuadPart = unix_time_to_win(time); 1105 1106 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) 1107 fbi.LastWriteTime.QuadPart = unix_time_to_win(time); 1108 1109 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME)) 1110 fbi.ChangeTime.QuadPart = unix_time_to_win(time); 1111 1112 Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation); 1113 if (!NT_SUCCESS(Status)) 1114 throw ntstatus_error(Status); 1115 } 1116 1117 static void delete_directory(const wstring& dir) { 1118 WIN32_FIND_DATAW fff; 1119 1120 fff_handle h = FindFirstFileW((dir + L"*").c_str(), &fff); 1121 1122 if (h == INVALID_HANDLE_VALUE) 1123 return; 1124 1125 do { 1126 wstring file; 1127 1128 file = fff.cFileName; 1129 1130 if (file != L"." && file != L"..") { 1131 if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 1132 SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); 1133 1134 if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 1135 if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 1136 delete_directory(dir + file + L"\\"); 1137 else 1138 RemoveDirectoryW((dir + file).c_str()); 1139 } else 1140 DeleteFileW((dir + file).c_str()); 1141 } 1142 } while (FindNextFileW(h, &fff)); 1143 1144 RemoveDirectoryW(dir.c_str()); 1145 } 1146 1147 static bool check_csum(btrfs_send_command* cmd, uint8_t* data) { 1148 uint32_t crc32 = cmd->csum, calc; 1149 1150 cmd->csum = 0; 1151 1152 calc = calc_crc32c(0, (uint8_t*)cmd, sizeof(btrfs_send_command)); 1153 1154 if (cmd->length > 0) 1155 calc = calc_crc32c(calc, data, cmd->length); 1156 1157 return calc == crc32 ? true : false; 1158 } 1159 1160 void BtrfsRecv::do_recv(const win_handle& f, uint64_t* pos, uint64_t size, const win_handle& parent) { 1161 try { 1162 btrfs_send_header header; 1163 bool ended = false; 1164 1165 if (!ReadFile(f, &header, sizeof(btrfs_send_header), nullptr, nullptr)) 1166 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1167 1168 *pos += sizeof(btrfs_send_header); 1169 1170 if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic))) 1171 throw string_error(IDS_RECV_NOT_A_SEND_STREAM); 1172 1173 if (header.version > 1) 1174 throw string_error(IDS_RECV_UNSUPPORTED_VERSION, header.version); 1175 1176 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536); 1177 1178 lastwritefile = INVALID_HANDLE_VALUE; 1179 lastwritepath = L""; 1180 lastwriteatt = 0; 1181 1182 while (true) { 1183 btrfs_send_command cmd; 1184 uint8_t* data = nullptr; 1185 ULONG progress; 1186 1187 if (cancelling) 1188 break; 1189 1190 progress = (ULONG)((float)*pos * 65536.0f / (float)size); 1191 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0); 1192 1193 if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), nullptr, nullptr)) { 1194 if (GetLastError() != ERROR_HANDLE_EOF) 1195 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1196 1197 break; 1198 } 1199 1200 *pos += sizeof(btrfs_send_command); 1201 1202 if (cmd.length > 0) { 1203 if (*pos + cmd.length > size) 1204 throw string_error(IDS_RECV_FILE_TRUNCATED); 1205 1206 data = (uint8_t*)malloc(cmd.length); 1207 if (!data) 1208 throw string_error(IDS_OUT_OF_MEMORY); 1209 } 1210 1211 try { 1212 if (data) { 1213 if (!ReadFile(f, data, cmd.length, nullptr, nullptr)) 1214 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1215 1216 *pos += cmd.length; 1217 } 1218 1219 if (!check_csum(&cmd, data)) 1220 throw string_error(IDS_RECV_CSUM_ERROR); 1221 1222 if (cmd.cmd == BTRFS_SEND_CMD_END) { 1223 ended = true; 1224 break; 1225 } 1226 1227 if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) { 1228 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { 1229 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) 1230 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1231 } 1232 1233 CloseHandle(lastwritefile); 1234 1235 lastwritefile = INVALID_HANDLE_VALUE; 1236 lastwritepath = L""; 1237 lastwriteatt = 0; 1238 } 1239 1240 switch (cmd.cmd) { 1241 case BTRFS_SEND_CMD_SUBVOL: 1242 cmd_subvol(hwnd, &cmd, data, parent); 1243 break; 1244 1245 case BTRFS_SEND_CMD_SNAPSHOT: 1246 cmd_snapshot(hwnd, &cmd, data, parent); 1247 break; 1248 1249 case BTRFS_SEND_CMD_MKFILE: 1250 case BTRFS_SEND_CMD_MKDIR: 1251 case BTRFS_SEND_CMD_MKNOD: 1252 case BTRFS_SEND_CMD_MKFIFO: 1253 case BTRFS_SEND_CMD_MKSOCK: 1254 case BTRFS_SEND_CMD_SYMLINK: 1255 cmd_mkfile(hwnd, &cmd, data); 1256 break; 1257 1258 case BTRFS_SEND_CMD_RENAME: 1259 cmd_rename(hwnd, &cmd, data); 1260 break; 1261 1262 case BTRFS_SEND_CMD_LINK: 1263 cmd_link(hwnd, &cmd, data); 1264 break; 1265 1266 case BTRFS_SEND_CMD_UNLINK: 1267 cmd_unlink(hwnd, &cmd, data); 1268 break; 1269 1270 case BTRFS_SEND_CMD_RMDIR: 1271 cmd_rmdir(hwnd, &cmd, data); 1272 break; 1273 1274 case BTRFS_SEND_CMD_SET_XATTR: 1275 cmd_setxattr(hwnd, &cmd, data); 1276 break; 1277 1278 case BTRFS_SEND_CMD_REMOVE_XATTR: 1279 cmd_removexattr(hwnd, &cmd, data); 1280 break; 1281 1282 case BTRFS_SEND_CMD_WRITE: 1283 cmd_write(hwnd, &cmd, data); 1284 break; 1285 1286 case BTRFS_SEND_CMD_CLONE: 1287 cmd_clone(hwnd, &cmd, data); 1288 break; 1289 1290 case BTRFS_SEND_CMD_TRUNCATE: 1291 cmd_truncate(hwnd, &cmd, data); 1292 break; 1293 1294 case BTRFS_SEND_CMD_CHMOD: 1295 cmd_chmod(hwnd, &cmd, data); 1296 break; 1297 1298 case BTRFS_SEND_CMD_CHOWN: 1299 cmd_chown(hwnd, &cmd, data); 1300 break; 1301 1302 case BTRFS_SEND_CMD_UTIMES: 1303 cmd_utimes(hwnd, &cmd, data); 1304 break; 1305 1306 case BTRFS_SEND_CMD_UPDATE_EXTENT: 1307 // does nothing 1308 break; 1309 1310 default: 1311 throw string_error(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd); 1312 } 1313 } catch (...) { 1314 if (data) free(data); 1315 throw; 1316 } 1317 1318 if (data) free(data); 1319 } 1320 1321 if (lastwritefile != INVALID_HANDLE_VALUE) { 1322 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) { 1323 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) 1324 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1325 } 1326 1327 CloseHandle(lastwritefile); 1328 } 1329 1330 if (!ended && !cancelling) 1331 throw string_error(IDS_RECV_FILE_TRUNCATED); 1332 1333 if (!cancelling) { 1334 NTSTATUS Status; 1335 IO_STATUS_BLOCK iosb; 1336 btrfs_received_subvol brs; 1337 1338 brs.generation = stransid; 1339 brs.uuid = subvol_uuid; 1340 1341 Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol), 1342 nullptr, 0); 1343 if (!NT_SUCCESS(Status)) 1344 throw string_error(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str()); 1345 } 1346 1347 CloseHandle(dir); 1348 1349 if (master != INVALID_HANDLE_VALUE) 1350 CloseHandle(master); 1351 } catch (...) { 1352 if (subvolpath != L"") { 1353 ULONG attrib; 1354 1355 attrib = GetFileAttributesW(subvolpath.c_str()); 1356 attrib &= ~FILE_ATTRIBUTE_READONLY; 1357 1358 if (SetFileAttributesW(subvolpath.c_str(), attrib)) 1359 delete_directory(subvolpath); 1360 } 1361 1362 throw; 1363 } 1364 } 1365 1366 #if defined(_X86_) || defined(_AMD64_) 1367 static void check_cpu() { 1368 bool have_sse42 = false; 1369 1370 #ifndef _MSC_VER 1371 { 1372 uint32_t eax, ebx, ecx, edx; 1373 1374 __cpuid(1, eax, ebx, ecx, edx); 1375 1376 if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) 1377 have_sse42 = ecx & bit_SSE4_2; 1378 } 1379 #else 1380 { 1381 int cpu_info[4]; 1382 1383 __cpuid(cpu_info, 1); 1384 have_sse42 = (unsigned int)cpu_info[2] & (1 << 20); 1385 } 1386 #endif 1387 1388 if (have_sse42) 1389 calc_crc32c = calc_crc32c_hw; 1390 } 1391 #endif 1392 1393 DWORD BtrfsRecv::recv_thread() { 1394 LARGE_INTEGER size; 1395 uint64_t pos = 0; 1396 bool b = true; 1397 1398 running = true; 1399 1400 try { 1401 win_handle f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); 1402 if (f == INVALID_HANDLE_VALUE) 1403 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 1404 1405 if (!GetFileSizeEx(f, &size)) 1406 throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1407 1408 { 1409 win_handle parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1410 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr); 1411 if (parent == INVALID_HANDLE_VALUE) 1412 throw string_error(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str()); 1413 1414 do { 1415 do_recv(f, &pos, size.QuadPart, parent); 1416 } while (pos < (uint64_t)size.QuadPart); 1417 } 1418 } catch (const exception& e) { 1419 auto msg = utf8_to_utf16(e.what()); 1420 1421 SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str()); 1422 1423 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0); 1424 1425 b = false; 1426 } 1427 1428 if (b && hwnd) { 1429 wstring s; 1430 1431 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0); 1432 1433 if (num_received == 1) { 1434 load_string(module, IDS_RECV_SUCCESS, s); 1435 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str()); 1436 } else { 1437 wstring t; 1438 1439 load_string(module, IDS_RECV_SUCCESS_PLURAL, s); 1440 1441 wstring_sprintf(t, s, num_received); 1442 1443 SetDlgItemTextW(hwnd, IDC_RECV_MSG, t.c_str()); 1444 } 1445 1446 load_string(module, IDS_RECV_BUTTON_OK, s); 1447 1448 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str()); 1449 } 1450 1451 thread = nullptr; 1452 running = false; 1453 1454 return 0; 1455 } 1456 1457 static DWORD WINAPI global_recv_thread(LPVOID lpParameter) { 1458 BtrfsRecv* br = (BtrfsRecv*)lpParameter; 1459 1460 return br->recv_thread(); 1461 } 1462 1463 INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1464 switch (uMsg) { 1465 case WM_INITDIALOG: 1466 try { 1467 this->hwnd = hwndDlg; 1468 thread = CreateThread(nullptr, 0, global_recv_thread, this, 0, nullptr); 1469 1470 if (!thread) 1471 throw string_error(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str()); 1472 } catch (const exception& e) { 1473 auto msg = utf8_to_utf16(e.what()); 1474 1475 SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str()); 1476 1477 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0); 1478 } 1479 break; 1480 1481 case WM_COMMAND: 1482 switch (HIWORD(wParam)) { 1483 case BN_CLICKED: 1484 switch (LOWORD(wParam)) { 1485 case IDOK: 1486 case IDCANCEL: 1487 if (running) { 1488 wstring s; 1489 1490 cancelling = true; 1491 1492 if (!load_string(module, IDS_RECV_CANCELLED, s)) 1493 throw last_error(GetLastError()); 1494 1495 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str()); 1496 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0); 1497 1498 if (!load_string(module, IDS_RECV_BUTTON_OK, s)) 1499 throw last_error(GetLastError()); 1500 1501 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str()); 1502 } else 1503 EndDialog(hwndDlg, 1); 1504 1505 return true; 1506 } 1507 break; 1508 } 1509 break; 1510 } 1511 1512 return false; 1513 } 1514 1515 static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1516 BtrfsRecv* br; 1517 1518 if (uMsg == WM_INITDIALOG) { 1519 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 1520 br = (BtrfsRecv*)lParam; 1521 } else { 1522 br = (BtrfsRecv*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 1523 } 1524 1525 if (br) 1526 return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam); 1527 else 1528 return false; 1529 } 1530 1531 void BtrfsRecv::Open(HWND hwnd, const wstring& file, const wstring& path, bool quiet) { 1532 streamfile = file; 1533 dirpath = path; 1534 subvolpath = L""; 1535 1536 #if defined(_X86_) || defined(_AMD64_) 1537 check_cpu(); 1538 #endif 1539 1540 if (quiet) 1541 recv_thread(); 1542 else { 1543 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0) 1544 throw last_error(GetLastError()); 1545 } 1546 } 1547 1548 extern "C" void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1549 try { 1550 OPENFILENAMEW ofn; 1551 WCHAR file[MAX_PATH]; 1552 win_handle token; 1553 TOKEN_PRIVILEGES* tp; 1554 LUID luid; 1555 ULONG tplen; 1556 1557 set_dpi_aware(); 1558 1559 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1560 throw last_error(GetLastError()); 1561 1562 tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES)); 1563 tp = (TOKEN_PRIVILEGES*)malloc(tplen); 1564 if (!tp) 1565 throw string_error(IDS_OUT_OF_MEMORY); 1566 1567 tp->PrivilegeCount = 3; 1568 1569 #ifdef __clang__ 1570 #pragma clang diagnostic push 1571 #pragma clang diagnostic ignored "-Warray-bounds" 1572 #endif // __clang__ 1573 1574 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) { 1575 free(tp); 1576 throw last_error(GetLastError()); 1577 } 1578 1579 tp->Privileges[0].Luid = luid; 1580 tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1581 1582 if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) { 1583 free(tp); 1584 throw last_error(GetLastError()); 1585 } 1586 1587 tp->Privileges[1].Luid = luid; 1588 tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; 1589 1590 if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) { 1591 free(tp); 1592 throw last_error(GetLastError()); 1593 } 1594 1595 tp->Privileges[2].Luid = luid; 1596 tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED; 1597 1598 if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) { 1599 free(tp); 1600 throw last_error(GetLastError()); 1601 } 1602 1603 #ifdef __clang__ 1604 #pragma clang diagnostic pop 1605 #endif // __clang__ 1606 1607 file[0] = 0; 1608 1609 memset(&ofn, 0, sizeof(OPENFILENAMEW)); 1610 ofn.lStructSize = sizeof(OPENFILENAMEW); 1611 ofn.hwndOwner = hwnd; 1612 ofn.hInstance = module; 1613 ofn.lpstrFile = file; 1614 ofn.nMaxFile = sizeof(file) / sizeof(WCHAR); 1615 ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; 1616 1617 if (GetOpenFileNameW(&ofn)) { 1618 BtrfsRecv recv; 1619 1620 recv.Open(hwnd, file, lpszCmdLine, false); 1621 } 1622 1623 free(tp); 1624 } catch (const exception& e) { 1625 error_message(hwnd, e.what()); 1626 } 1627 } 1628 1629 extern "C" void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1630 try { 1631 vector<wstring> args; 1632 1633 command_line_to_args(lpszCmdLine, args); 1634 1635 if (args.size() >= 2) { 1636 win_handle token; 1637 TOKEN_PRIVILEGES* tp; 1638 ULONG tplen; 1639 LUID luid; 1640 1641 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1642 return; 1643 1644 tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES)); 1645 tp = (TOKEN_PRIVILEGES*)malloc(tplen); 1646 if (!tp) 1647 return; 1648 1649 tp->PrivilegeCount = 3; 1650 1651 #ifdef __clang__ 1652 #pragma clang diagnostic push 1653 #pragma clang diagnostic ignored "-Warray-bounds" 1654 #endif // __clang__ 1655 1656 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) { 1657 free(tp); 1658 return; 1659 } 1660 1661 tp->Privileges[0].Luid = luid; 1662 tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1663 1664 if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) { 1665 free(tp); 1666 return; 1667 } 1668 1669 tp->Privileges[1].Luid = luid; 1670 tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; 1671 1672 if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) { 1673 free(tp); 1674 return; 1675 } 1676 1677 tp->Privileges[2].Luid = luid; 1678 tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED; 1679 1680 #ifdef __clang__ 1681 #pragma clang diagnostic pop 1682 #endif // __clang__ 1683 1684 if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) { 1685 free(tp); 1686 return; 1687 } 1688 1689 free(tp); 1690 1691 BtrfsRecv br; 1692 br.Open(nullptr, args[0], args[1], true); 1693 } 1694 } catch (const exception& e) { 1695 cerr << "Error: " << e.what() << endl; 1696 } 1697 } 1698