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