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 "btrfs_drv.h" 19 20 typedef struct send_dir { 21 LIST_ENTRY list_entry; 22 uint64_t inode; 23 bool dummy; 24 BTRFS_TIME atime; 25 BTRFS_TIME mtime; 26 BTRFS_TIME ctime; 27 struct send_dir* parent; 28 uint16_t namelen; 29 char* name; 30 LIST_ENTRY deleted_children; 31 } send_dir; 32 33 typedef struct { 34 LIST_ENTRY list_entry; 35 uint64_t inode; 36 bool dir; 37 send_dir* sd; 38 char tmpname[64]; 39 } orphan; 40 41 typedef struct { 42 LIST_ENTRY list_entry; 43 ULONG namelen; 44 char name[1]; 45 } deleted_child; 46 47 typedef struct { 48 LIST_ENTRY list_entry; 49 send_dir* sd; 50 uint16_t namelen; 51 char name[1]; 52 } ref; 53 54 typedef struct { 55 send_dir* sd; 56 uint64_t last_child_inode; 57 LIST_ENTRY list_entry; 58 } pending_rmdir; 59 60 typedef struct { 61 uint64_t offset; 62 LIST_ENTRY list_entry; 63 ULONG datalen; 64 EXTENT_DATA data; 65 } send_ext; 66 67 typedef struct { 68 device_extension* Vcb; 69 root* root; 70 root* parent; 71 uint8_t* data; 72 ULONG datalen; 73 ULONG num_clones; 74 root** clones; 75 LIST_ENTRY orphans; 76 LIST_ENTRY dirs; 77 LIST_ENTRY pending_rmdirs; 78 KEVENT buffer_event; 79 send_dir* root_dir; 80 send_info* send; 81 82 struct { 83 uint64_t inode; 84 bool deleting; 85 bool new; 86 uint64_t gen; 87 uint64_t uid; 88 uint64_t olduid; 89 uint64_t gid; 90 uint64_t oldgid; 91 uint64_t mode; 92 uint64_t oldmode; 93 uint64_t size; 94 uint64_t flags; 95 BTRFS_TIME atime; 96 BTRFS_TIME mtime; 97 BTRFS_TIME ctime; 98 bool file; 99 char* path; 100 orphan* o; 101 send_dir* sd; 102 LIST_ENTRY refs; 103 LIST_ENTRY oldrefs; 104 LIST_ENTRY exts; 105 LIST_ENTRY oldexts; 106 } lastinode; 107 } send_context; 108 109 #define MAX_SEND_WRITE 0xc000 // 48 KB 110 #define SEND_BUFFER_LENGTH 0x100000 // 1 MB 111 112 static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy); 113 static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2); 114 115 static void send_command(send_context* context, uint16_t cmd) { 116 btrfs_send_command* bsc = (btrfs_send_command*)&context->data[context->datalen]; 117 118 bsc->cmd = cmd; 119 bsc->csum = 0; 120 121 context->datalen += sizeof(btrfs_send_command); 122 } 123 124 static void send_command_finish(send_context* context, ULONG pos) { 125 btrfs_send_command* bsc = (btrfs_send_command*)&context->data[pos]; 126 127 bsc->length = context->datalen - pos - sizeof(btrfs_send_command); 128 bsc->csum = calc_crc32c(0, (uint8_t*)bsc, context->datalen - pos); 129 } 130 131 static void send_add_tlv(send_context* context, uint16_t type, void* data, uint16_t length) { 132 btrfs_send_tlv* tlv = (btrfs_send_tlv*)&context->data[context->datalen]; 133 134 tlv->type = type; 135 tlv->length = length; 136 137 if (length > 0 && data) 138 RtlCopyMemory(&tlv[1], data, length); 139 140 context->datalen += sizeof(btrfs_send_tlv) + length; 141 } 142 143 static char* uint64_to_char(uint64_t num, char* buf) { 144 char *tmp, tmp2[20]; 145 146 if (num == 0) { 147 buf[0] = '0'; 148 return buf + 1; 149 } 150 151 tmp = &tmp2[20]; 152 while (num > 0) { 153 tmp--; 154 *tmp = (num % 10) + '0'; 155 num /= 10; 156 } 157 158 RtlCopyMemory(buf, tmp, tmp2 + sizeof(tmp2) - tmp); 159 160 return &buf[tmp2 + sizeof(tmp2) - tmp]; 161 } 162 163 static NTSTATUS get_orphan_name(send_context* context, uint64_t inode, uint64_t generation, char* name) { 164 char *ptr, *ptr2; 165 uint64_t index = 0; 166 KEY searchkey; 167 168 name[0] = 'o'; 169 170 ptr = uint64_to_char(inode, &name[1]); 171 *ptr = '-'; ptr++; 172 ptr = uint64_to_char(generation, ptr); 173 *ptr = '-'; ptr++; 174 ptr2 = ptr; 175 176 searchkey.obj_id = SUBVOL_ROOT_INODE; 177 searchkey.obj_type = TYPE_DIR_ITEM; 178 179 do { 180 NTSTATUS Status; 181 traverse_ptr tp; 182 183 ptr = uint64_to_char(index, ptr); 184 *ptr = 0; 185 186 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, (ULONG)(ptr - name)); 187 188 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 189 if (!NT_SUCCESS(Status)) { 190 ERR("find_item returned %08x\n", Status); 191 return Status; 192 } 193 194 if (!keycmp(searchkey, tp.item->key)) 195 goto cont; 196 197 if (context->parent) { 198 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 199 if (!NT_SUCCESS(Status)) { 200 ERR("find_item returned %08x\n", Status); 201 return Status; 202 } 203 204 if (!keycmp(searchkey, tp.item->key)) 205 goto cont; 206 } 207 208 return STATUS_SUCCESS; 209 210 cont: 211 index++; 212 ptr = ptr2; 213 } while (true); 214 } 215 216 static void add_orphan(send_context* context, orphan* o) { 217 LIST_ENTRY* le; 218 219 le = context->orphans.Flink; 220 while (le != &context->orphans) { 221 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 222 223 if (o2->inode > o->inode) { 224 InsertHeadList(o2->list_entry.Blink, &o->list_entry); 225 return; 226 } 227 228 le = le->Flink; 229 } 230 231 InsertTailList(&context->orphans, &o->list_entry); 232 } 233 234 static NTSTATUS send_read_symlink(send_context* context, uint64_t inode, char** link, uint16_t* linklen) { 235 NTSTATUS Status; 236 KEY searchkey; 237 traverse_ptr tp; 238 EXTENT_DATA* ed; 239 240 searchkey.obj_id = inode; 241 searchkey.obj_type = TYPE_EXTENT_DATA; 242 searchkey.offset = 0; 243 244 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 245 if (!NT_SUCCESS(Status)) { 246 ERR("find_item returned %08x\n", Status); 247 return Status; 248 } 249 250 if (keycmp(tp.item->key, searchkey)) { 251 ERR("could not find (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 252 return STATUS_INTERNAL_ERROR; 253 } 254 255 if (tp.item->size < sizeof(EXTENT_DATA)) { 256 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 257 tp.item->size, sizeof(EXTENT_DATA)); 258 return STATUS_INTERNAL_ERROR; 259 } 260 261 ed = (EXTENT_DATA*)tp.item->data; 262 263 if (ed->type != EXTENT_TYPE_INLINE) { 264 WARN("symlink data was not inline, returning blank string\n"); 265 *link = NULL; 266 *linklen = 0; 267 return STATUS_SUCCESS; 268 } 269 270 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) { 271 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 272 tp.item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 273 return STATUS_INTERNAL_ERROR; 274 } 275 276 *link = (char*)ed->data; 277 *linklen = (uint16_t)ed->decoded_size; 278 279 return STATUS_SUCCESS; 280 } 281 282 static NTSTATUS send_inode(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 283 NTSTATUS Status; 284 INODE_ITEM* ii; 285 286 if (tp2 && !tp) { 287 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data; 288 289 if (tp2->item->size < sizeof(INODE_ITEM)) { 290 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 291 tp2->item->size, sizeof(INODE_ITEM)); 292 return STATUS_INTERNAL_ERROR; 293 } 294 295 context->lastinode.inode = tp2->item->key.obj_id; 296 context->lastinode.deleting = true; 297 context->lastinode.gen = ii2->generation; 298 context->lastinode.mode = ii2->st_mode; 299 context->lastinode.flags = ii2->flags; 300 context->lastinode.o = NULL; 301 context->lastinode.sd = NULL; 302 303 return STATUS_SUCCESS; 304 } 305 306 ii = (INODE_ITEM*)tp->item->data; 307 308 if (tp->item->size < sizeof(INODE_ITEM)) { 309 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 310 tp->item->size, sizeof(INODE_ITEM)); 311 return STATUS_INTERNAL_ERROR; 312 } 313 314 context->lastinode.inode = tp->item->key.obj_id; 315 context->lastinode.deleting = false; 316 context->lastinode.gen = ii->generation; 317 context->lastinode.uid = ii->st_uid; 318 context->lastinode.gid = ii->st_gid; 319 context->lastinode.mode = ii->st_mode; 320 context->lastinode.size = ii->st_size; 321 context->lastinode.atime = ii->st_atime; 322 context->lastinode.mtime = ii->st_mtime; 323 context->lastinode.ctime = ii->st_ctime; 324 context->lastinode.flags = ii->flags; 325 context->lastinode.file = false; 326 context->lastinode.o = NULL; 327 context->lastinode.sd = NULL; 328 329 if (context->lastinode.path) { 330 ExFreePool(context->lastinode.path); 331 context->lastinode.path = NULL; 332 } 333 334 if (tp2) { 335 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data; 336 LIST_ENTRY* le; 337 338 if (tp2->item->size < sizeof(INODE_ITEM)) { 339 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 340 tp2->item->size, sizeof(INODE_ITEM)); 341 return STATUS_INTERNAL_ERROR; 342 } 343 344 context->lastinode.oldmode = ii2->st_mode; 345 context->lastinode.olduid = ii2->st_uid; 346 context->lastinode.oldgid = ii2->st_gid; 347 348 if ((ii2->st_mode & __S_IFREG) == __S_IFREG && (ii2->st_mode & __S_IFLNK) != __S_IFLNK && (ii2->st_mode & __S_IFSOCK) != __S_IFSOCK) 349 context->lastinode.file = true; 350 351 context->lastinode.new = false; 352 353 le = context->orphans.Flink; 354 while (le != &context->orphans) { 355 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 356 357 if (o2->inode == tp->item->key.obj_id) { 358 context->lastinode.o = o2; 359 break; 360 } else if (o2->inode > tp->item->key.obj_id) 361 break; 362 363 le = le->Flink; 364 } 365 } else 366 context->lastinode.new = true; 367 368 if (tp->item->key.obj_id == SUBVOL_ROOT_INODE) { 369 send_dir* sd; 370 371 Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL); 372 if (!NT_SUCCESS(Status)) { 373 ERR("find_send_dir returned %08x\n", Status); 374 return Status; 375 } 376 377 sd->atime = ii->st_atime; 378 sd->mtime = ii->st_mtime; 379 sd->ctime = ii->st_ctime; 380 context->root_dir = sd; 381 } else if (!tp2) { 382 ULONG pos = context->datalen; 383 uint16_t cmd; 384 send_dir* sd; 385 386 char name[64]; 387 orphan* o; 388 389 // skip creating orphan directory if we've already done so 390 if (ii->st_mode & __S_IFDIR) { 391 LIST_ENTRY* le; 392 393 le = context->orphans.Flink; 394 while (le != &context->orphans) { 395 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 396 397 if (o2->inode == tp->item->key.obj_id) { 398 context->lastinode.o = o2; 399 o2->sd->atime = ii->st_atime; 400 o2->sd->mtime = ii->st_mtime; 401 o2->sd->ctime = ii->st_ctime; 402 o2->sd->dummy = false; 403 return STATUS_SUCCESS; 404 } else if (o2->inode > tp->item->key.obj_id) 405 break; 406 407 le = le->Flink; 408 } 409 } 410 411 if ((ii->st_mode & __S_IFSOCK) == __S_IFSOCK) 412 cmd = BTRFS_SEND_CMD_MKSOCK; 413 else if ((ii->st_mode & __S_IFLNK) == __S_IFLNK) 414 cmd = BTRFS_SEND_CMD_SYMLINK; 415 else if ((ii->st_mode & __S_IFCHR) == __S_IFCHR || (ii->st_mode & __S_IFBLK) == __S_IFBLK) 416 cmd = BTRFS_SEND_CMD_MKNOD; 417 else if ((ii->st_mode & __S_IFDIR) == __S_IFDIR) 418 cmd = BTRFS_SEND_CMD_MKDIR; 419 else if ((ii->st_mode & __S_IFIFO) == __S_IFIFO) 420 cmd = BTRFS_SEND_CMD_MKFIFO; 421 else { 422 cmd = BTRFS_SEND_CMD_MKFILE; 423 context->lastinode.file = true; 424 } 425 426 send_command(context, cmd); 427 428 Status = get_orphan_name(context, tp->item->key.obj_id, ii->generation, name); 429 if (!NT_SUCCESS(Status)) { 430 ERR("get_orphan_name returned %08x\n", Status); 431 return Status; 432 } 433 434 send_add_tlv(context, BTRFS_SEND_TLV_PATH, name, (uint16_t)strlen(name)); 435 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &tp->item->key.obj_id, sizeof(uint64_t)); 436 437 if (cmd == BTRFS_SEND_CMD_MKNOD || cmd == BTRFS_SEND_CMD_MKFIFO || cmd == BTRFS_SEND_CMD_MKSOCK) { 438 uint64_t rdev = makedev((ii->st_rdev & 0xFFFFFFFFFFF) >> 20, ii->st_rdev & 0xFFFFF), mode = ii->st_mode; 439 440 send_add_tlv(context, BTRFS_SEND_TLV_RDEV, &rdev, sizeof(uint64_t)); 441 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t)); 442 } else if (cmd == BTRFS_SEND_CMD_SYMLINK && ii->st_size > 0) { 443 char* link; 444 uint16_t linklen; 445 446 Status = send_read_symlink(context, tp->item->key.obj_id, &link, &linklen); 447 if (!NT_SUCCESS(Status)) { 448 ERR("send_read_symlink returned %08x\n", Status); 449 return Status; 450 } 451 452 send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, link, linklen); 453 } 454 455 send_command_finish(context, pos); 456 457 if (ii->st_mode & __S_IFDIR) { 458 Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL); 459 if (!NT_SUCCESS(Status)) { 460 ERR("find_send_dir returned %08x\n", Status); 461 return Status; 462 } 463 464 sd->dummy = false; 465 } else 466 sd = NULL; 467 468 context->lastinode.sd = sd; 469 470 o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 471 if (!o) { 472 ERR("out of memory\n"); 473 return STATUS_INSUFFICIENT_RESOURCES; 474 } 475 476 o->inode = tp->item->key.obj_id; 477 o->dir = (ii->st_mode & __S_IFDIR && ii->st_size > 0) ? true : false; 478 strcpy(o->tmpname, name); 479 o->sd = sd; 480 add_orphan(context, o); 481 482 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, strlen(o->tmpname) + 1, ALLOC_TAG); 483 if (!context->lastinode.path) { 484 ERR("out of memory\n"); 485 return STATUS_INSUFFICIENT_RESOURCES; 486 } 487 488 strcpy(context->lastinode.path, o->tmpname); 489 490 context->lastinode.o = o; 491 } 492 493 return STATUS_SUCCESS; 494 } 495 496 static NTSTATUS send_add_dir(send_context* context, uint64_t inode, send_dir* parent, char* name, uint16_t namelen, bool dummy, LIST_ENTRY* lastentry, send_dir** psd) { 497 LIST_ENTRY* le; 498 send_dir* sd = ExAllocatePoolWithTag(PagedPool, sizeof(send_dir), ALLOC_TAG); 499 500 if (!sd) { 501 ERR("out of memory\n"); 502 return STATUS_INSUFFICIENT_RESOURCES; 503 } 504 505 sd->inode = inode; 506 sd->dummy = dummy; 507 sd->parent = parent; 508 509 if (!dummy) { 510 sd->atime = context->lastinode.atime; 511 sd->mtime = context->lastinode.mtime; 512 sd->ctime = context->lastinode.ctime; 513 } 514 515 if (namelen > 0) { 516 sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG); 517 if (!sd->name) { 518 ERR("out of memory\n"); 519 ExFreePool(sd); 520 return STATUS_INSUFFICIENT_RESOURCES; 521 } 522 523 memcpy(sd->name, name, namelen); 524 } else 525 sd->name = NULL; 526 527 sd->namelen = namelen; 528 529 InitializeListHead(&sd->deleted_children); 530 531 if (lastentry) 532 InsertHeadList(lastentry, &sd->list_entry); 533 else { 534 le = context->dirs.Flink; 535 while (le != &context->dirs) { 536 send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry); 537 538 if (sd2->inode > sd->inode) { 539 InsertHeadList(sd2->list_entry.Blink, &sd->list_entry); 540 541 if (psd) 542 *psd = sd; 543 544 return STATUS_SUCCESS; 545 } 546 547 le = le->Flink; 548 } 549 550 InsertTailList(&context->dirs, &sd->list_entry); 551 } 552 553 if (psd) 554 *psd = sd; 555 556 return STATUS_SUCCESS; 557 } 558 559 static __inline uint16_t find_path_len(send_dir* parent, uint16_t namelen) { 560 uint16_t len = namelen; 561 562 while (parent && parent->namelen > 0) { 563 len += parent->namelen + 1; 564 parent = parent->parent; 565 } 566 567 return len; 568 } 569 570 static void find_path(char* path, send_dir* parent, char* name, ULONG namelen) { 571 ULONG len = namelen; 572 573 RtlCopyMemory(path, name, namelen); 574 575 while (parent && parent->namelen > 0) { 576 RtlMoveMemory(path + parent->namelen + 1, path, len); 577 RtlCopyMemory(path, parent->name, parent->namelen); 578 path[parent->namelen] = '/'; 579 len += parent->namelen + 1; 580 581 parent = parent->parent; 582 } 583 } 584 585 static void send_add_tlv_path(send_context* context, uint16_t type, send_dir* parent, char* name, uint16_t namelen) { 586 uint16_t len = find_path_len(parent, namelen); 587 588 send_add_tlv(context, type, NULL, len); 589 590 if (len > 0) 591 find_path((char*)&context->data[context->datalen - len], parent, name, namelen); 592 } 593 594 static NTSTATUS found_path(send_context* context, send_dir* parent, char* name, uint16_t namelen) { 595 ULONG pos = context->datalen; 596 597 if (context->lastinode.o) { 598 send_command(context, BTRFS_SEND_CMD_RENAME); 599 600 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, context->root_dir, context->lastinode.o->tmpname, (uint16_t)strlen(context->lastinode.o->tmpname)); 601 602 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, parent, name, namelen); 603 604 send_command_finish(context, pos); 605 } else { 606 send_command(context, BTRFS_SEND_CMD_LINK); 607 608 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, parent, name, namelen); 609 610 send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 611 612 send_command_finish(context, pos); 613 } 614 615 if (context->lastinode.o) { 616 uint16_t pathlen; 617 618 if (context->lastinode.o->sd) { 619 if (context->lastinode.o->sd->name) 620 ExFreePool(context->lastinode.o->sd->name); 621 622 context->lastinode.o->sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG); 623 if (!context->lastinode.o->sd->name) { 624 ERR("out of memory\n"); 625 return STATUS_INSUFFICIENT_RESOURCES; 626 } 627 628 RtlCopyMemory(context->lastinode.o->sd->name, name, namelen); 629 context->lastinode.o->sd->namelen = namelen; 630 context->lastinode.o->sd->parent = parent; 631 } 632 633 if (context->lastinode.path) 634 ExFreePool(context->lastinode.path); 635 636 pathlen = find_path_len(parent, namelen); 637 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, pathlen + 1, ALLOC_TAG); 638 if (!context->lastinode.path) { 639 ERR("out of memory\n"); 640 return STATUS_INSUFFICIENT_RESOURCES; 641 } 642 643 find_path(context->lastinode.path, parent, name, namelen); 644 context->lastinode.path[pathlen] = 0; 645 646 RemoveEntryList(&context->lastinode.o->list_entry); 647 ExFreePool(context->lastinode.o); 648 649 context->lastinode.o = NULL; 650 } 651 652 return STATUS_SUCCESS; 653 } 654 655 static void send_utimes_command_dir(send_context* context, send_dir* sd, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) { 656 ULONG pos = context->datalen; 657 658 send_command(context, BTRFS_SEND_CMD_UTIMES); 659 660 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, sd->parent, sd->name, sd->namelen); 661 662 send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME)); 663 send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME)); 664 send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME)); 665 666 send_command_finish(context, pos); 667 } 668 669 static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy) { 670 NTSTATUS Status; 671 LIST_ENTRY* le; 672 char name[64]; 673 674 le = context->dirs.Flink; 675 while (le != &context->dirs) { 676 send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry); 677 678 if (sd2->inode > dir) 679 break; 680 else if (sd2->inode == dir) { 681 *psd = sd2; 682 683 if (added_dummy) 684 *added_dummy = false; 685 686 return STATUS_SUCCESS; 687 } 688 689 le = le->Flink; 690 } 691 692 if (dir == SUBVOL_ROOT_INODE) { 693 Status = send_add_dir(context, dir, NULL, NULL, 0, false, le, psd); 694 if (!NT_SUCCESS(Status)) { 695 ERR("send_add_dir returned %08x\n", Status); 696 return Status; 697 } 698 699 if (added_dummy) 700 *added_dummy = false; 701 702 return STATUS_SUCCESS; 703 } 704 705 if (context->parent) { 706 KEY searchkey; 707 traverse_ptr tp; 708 709 searchkey.obj_id = dir; 710 searchkey.obj_type = TYPE_INODE_REF; // directories should never have an extiref 711 searchkey.offset = 0xffffffffffffffff; 712 713 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 714 if (!NT_SUCCESS(Status)) { 715 ERR("find_item returned %08x\n", Status); 716 return Status; 717 } 718 719 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 720 INODE_REF* ir = (INODE_REF*)tp.item->data; 721 send_dir* parent; 722 723 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) { 724 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 725 return STATUS_INTERNAL_ERROR; 726 } 727 728 if (tp.item->key.offset == SUBVOL_ROOT_INODE) 729 parent = context->root_dir; 730 else { 731 Status = find_send_dir(context, tp.item->key.offset, generation, &parent, NULL); 732 if (!NT_SUCCESS(Status)) { 733 ERR("find_send_dir returned %08x\n", Status); 734 return Status; 735 } 736 } 737 738 Status = send_add_dir(context, dir, parent, ir->name, ir->n, true, NULL, psd); 739 if (!NT_SUCCESS(Status)) { 740 ERR("send_add_dir returned %08x\n", Status); 741 return Status; 742 } 743 744 if (added_dummy) 745 *added_dummy = false; 746 747 return STATUS_SUCCESS; 748 } 749 } 750 751 Status = get_orphan_name(context, dir, generation, name); 752 if (!NT_SUCCESS(Status)) { 753 ERR("get_orphan_name returned %08x\n", Status); 754 return Status; 755 } 756 757 Status = send_add_dir(context, dir, NULL, name, (uint16_t)strlen(name), true, le, psd); 758 if (!NT_SUCCESS(Status)) { 759 ERR("send_add_dir returned %08x\n", Status); 760 return Status; 761 } 762 763 if (added_dummy) 764 *added_dummy = true; 765 766 return STATUS_SUCCESS; 767 } 768 769 static NTSTATUS send_inode_ref(send_context* context, traverse_ptr* tp, bool tree2) { 770 NTSTATUS Status; 771 uint64_t inode = tp ? tp->item->key.obj_id : 0, dir = tp ? tp->item->key.offset : 0; 772 LIST_ENTRY* le; 773 INODE_REF* ir; 774 uint16_t len; 775 send_dir* sd = NULL; 776 orphan* o2 = NULL; 777 778 if (inode == dir) // root 779 return STATUS_SUCCESS; 780 781 if (tp->item->size < sizeof(INODE_REF)) { 782 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 783 tp->item->size, sizeof(INODE_REF)); 784 return STATUS_INTERNAL_ERROR; 785 } 786 787 if (dir != SUBVOL_ROOT_INODE) { 788 bool added_dummy; 789 790 Status = find_send_dir(context, dir, context->root->root_item.ctransid, &sd, &added_dummy); 791 if (!NT_SUCCESS(Status)) { 792 ERR("find_send_dir returned %08x\n", Status); 793 return Status; 794 } 795 796 // directory has higher inode number than file, so might need to be created 797 if (added_dummy) { 798 bool found = false; 799 800 le = context->orphans.Flink; 801 while (le != &context->orphans) { 802 o2 = CONTAINING_RECORD(le, orphan, list_entry); 803 804 if (o2->inode == dir) { 805 found = true; 806 break; 807 } else if (o2->inode > dir) 808 break; 809 810 le = le->Flink; 811 } 812 813 if (!found) { 814 ULONG pos = context->datalen; 815 816 send_command(context, BTRFS_SEND_CMD_MKDIR); 817 818 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen); 819 820 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &dir, sizeof(uint64_t)); 821 822 send_command_finish(context, pos); 823 824 o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 825 if (!o2) { 826 ERR("out of memory\n"); 827 return STATUS_INSUFFICIENT_RESOURCES; 828 } 829 830 o2->inode = dir; 831 o2->dir = true; 832 memcpy(o2->tmpname, sd->name, sd->namelen); 833 o2->tmpname[sd->namelen] = 0; 834 o2->sd = sd; 835 add_orphan(context, o2); 836 } 837 } 838 } else 839 sd = context->root_dir; 840 841 len = tp->item->size; 842 ir = (INODE_REF*)tp->item->data; 843 844 while (len > 0) { 845 ref* r; 846 847 if (len < sizeof(INODE_REF) || len < offsetof(INODE_REF, name[0]) + ir->n) { 848 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 849 return STATUS_INTERNAL_ERROR; 850 } 851 852 r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ir->n, ALLOC_TAG); 853 if (!r) { 854 ERR("out of memory\n"); 855 return STATUS_INSUFFICIENT_RESOURCES; 856 } 857 858 r->sd = sd; 859 r->namelen = ir->n; 860 RtlCopyMemory(r->name, ir->name, ir->n); 861 862 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry); 863 864 len -= (uint16_t)offsetof(INODE_REF, name[0]) + ir->n; 865 ir = (INODE_REF*)&ir->name[ir->n]; 866 } 867 868 return STATUS_SUCCESS; 869 } 870 871 static NTSTATUS send_inode_extref(send_context* context, traverse_ptr* tp, bool tree2) { 872 INODE_EXTREF* ier; 873 uint16_t len; 874 875 if (tp->item->size < sizeof(INODE_EXTREF)) { 876 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 877 tp->item->size, sizeof(INODE_EXTREF)); 878 return STATUS_INTERNAL_ERROR; 879 } 880 881 len = tp->item->size; 882 ier = (INODE_EXTREF*)tp->item->data; 883 884 while (len > 0) { 885 NTSTATUS Status; 886 send_dir* sd = NULL; 887 orphan* o2 = NULL; 888 ref* r; 889 890 if (len < sizeof(INODE_EXTREF) || len < offsetof(INODE_EXTREF, name[0]) + ier->n) { 891 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 892 return STATUS_INTERNAL_ERROR; 893 } 894 895 if (ier->dir != SUBVOL_ROOT_INODE) { 896 LIST_ENTRY* le; 897 bool added_dummy; 898 899 Status = find_send_dir(context, ier->dir, context->root->root_item.ctransid, &sd, &added_dummy); 900 if (!NT_SUCCESS(Status)) { 901 ERR("find_send_dir returned %08x\n", Status); 902 return Status; 903 } 904 905 // directory has higher inode number than file, so might need to be created 906 if (added_dummy) { 907 bool found = false; 908 909 le = context->orphans.Flink; 910 while (le != &context->orphans) { 911 o2 = CONTAINING_RECORD(le, orphan, list_entry); 912 913 if (o2->inode == ier->dir) { 914 found = true; 915 break; 916 } else if (o2->inode > ier->dir) 917 break; 918 919 le = le->Flink; 920 } 921 922 if (!found) { 923 ULONG pos = context->datalen; 924 925 send_command(context, BTRFS_SEND_CMD_MKDIR); 926 927 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen); 928 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &ier->dir, sizeof(uint64_t)); 929 930 send_command_finish(context, pos); 931 932 o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 933 if (!o2) { 934 ERR("out of memory\n"); 935 return STATUS_INSUFFICIENT_RESOURCES; 936 } 937 938 o2->inode = ier->dir; 939 o2->dir = true; 940 memcpy(o2->tmpname, sd->name, sd->namelen); 941 o2->tmpname[sd->namelen] = 0; 942 o2->sd = sd; 943 add_orphan(context, o2); 944 } 945 } 946 } else 947 sd = context->root_dir; 948 949 r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ier->n, ALLOC_TAG); 950 if (!r) { 951 ERR("out of memory\n"); 952 return STATUS_INSUFFICIENT_RESOURCES; 953 } 954 955 r->sd = sd; 956 r->namelen = ier->n; 957 RtlCopyMemory(r->name, ier->name, ier->n); 958 959 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry); 960 961 len -= (uint16_t)offsetof(INODE_EXTREF, name[0]) + ier->n; 962 ier = (INODE_EXTREF*)&ier->name[ier->n]; 963 } 964 965 return STATUS_SUCCESS; 966 } 967 968 static void send_subvol_header(send_context* context, root* r, file_ref* fr) { 969 ULONG pos = context->datalen; 970 971 send_command(context, context->parent ? BTRFS_SEND_CMD_SNAPSHOT : BTRFS_SEND_CMD_SUBVOL); 972 973 send_add_tlv(context, BTRFS_SEND_TLV_PATH, fr->dc->utf8.Buffer, fr->dc->utf8.Length); 974 975 send_add_tlv(context, BTRFS_SEND_TLV_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID)); 976 send_add_tlv(context, BTRFS_SEND_TLV_TRANSID, &r->root_item.ctransid, sizeof(uint64_t)); 977 978 if (context->parent) { 979 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID, 980 context->parent->root_item.rtransid == 0 ? &context->parent->root_item.uuid : &context->parent->root_item.received_uuid, sizeof(BTRFS_UUID)); 981 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &context->parent->root_item.ctransid, sizeof(uint64_t)); 982 } 983 984 send_command_finish(context, pos); 985 } 986 987 static void send_chown_command(send_context* context, char* path, uint64_t uid, uint64_t gid) { 988 ULONG pos = context->datalen; 989 990 send_command(context, BTRFS_SEND_CMD_CHOWN); 991 992 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 993 send_add_tlv(context, BTRFS_SEND_TLV_UID, &uid, sizeof(uint64_t)); 994 send_add_tlv(context, BTRFS_SEND_TLV_GID, &gid, sizeof(uint64_t)); 995 996 send_command_finish(context, pos); 997 } 998 999 static void send_chmod_command(send_context* context, char* path, uint64_t mode) { 1000 ULONG pos = context->datalen; 1001 1002 send_command(context, BTRFS_SEND_CMD_CHMOD); 1003 1004 mode &= 07777; 1005 1006 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1007 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t)); 1008 1009 send_command_finish(context, pos); 1010 } 1011 1012 static void send_utimes_command(send_context* context, char* path, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) { 1013 ULONG pos = context->datalen; 1014 1015 send_command(context, BTRFS_SEND_CMD_UTIMES); 1016 1017 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1018 send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME)); 1019 send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME)); 1020 send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME)); 1021 1022 send_command_finish(context, pos); 1023 } 1024 1025 static void send_truncate_command(send_context* context, char* path, uint64_t size) { 1026 ULONG pos = context->datalen; 1027 1028 send_command(context, BTRFS_SEND_CMD_TRUNCATE); 1029 1030 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1031 send_add_tlv(context, BTRFS_SEND_TLV_SIZE, &size, sizeof(uint64_t)); 1032 1033 send_command_finish(context, pos); 1034 } 1035 1036 static NTSTATUS send_unlink_command(send_context* context, send_dir* parent, uint16_t namelen, char* name) { 1037 ULONG pos = context->datalen; 1038 uint16_t pathlen; 1039 1040 send_command(context, BTRFS_SEND_CMD_UNLINK); 1041 1042 pathlen = find_path_len(parent, namelen); 1043 send_add_tlv(context, BTRFS_SEND_TLV_PATH, NULL, pathlen); 1044 1045 find_path((char*)&context->data[context->datalen - pathlen], parent, name, namelen); 1046 1047 send_command_finish(context, pos); 1048 1049 return STATUS_SUCCESS; 1050 } 1051 1052 static void send_rmdir_command(send_context* context, uint16_t pathlen, char* path) { 1053 ULONG pos = context->datalen; 1054 1055 send_command(context, BTRFS_SEND_CMD_RMDIR); 1056 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, pathlen); 1057 send_command_finish(context, pos); 1058 } 1059 1060 static NTSTATUS get_dir_last_child(send_context* context, uint64_t* last_inode) { 1061 NTSTATUS Status; 1062 KEY searchkey; 1063 traverse_ptr tp; 1064 1065 *last_inode = 0; 1066 1067 searchkey.obj_id = context->lastinode.inode; 1068 searchkey.obj_type = TYPE_DIR_INDEX; 1069 searchkey.offset = 2; 1070 1071 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 1072 if (!NT_SUCCESS(Status)) { 1073 ERR("find_item returned %08x\n", Status); 1074 return Status; 1075 } 1076 1077 do { 1078 traverse_ptr next_tp; 1079 1080 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 1081 DIR_ITEM* di = (DIR_ITEM*)tp.item->data; 1082 1083 if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 1084 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1085 return STATUS_INTERNAL_ERROR; 1086 } 1087 1088 if (di->key.obj_type == TYPE_INODE_ITEM) 1089 *last_inode = max(*last_inode, di->key.obj_id); 1090 } else 1091 break; 1092 1093 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 1094 tp = next_tp; 1095 else 1096 break; 1097 } while (true); 1098 1099 return STATUS_SUCCESS; 1100 } 1101 1102 static NTSTATUS add_pending_rmdir(send_context* context, uint64_t last_inode) { 1103 pending_rmdir* pr; 1104 LIST_ENTRY* le; 1105 1106 pr = ExAllocatePoolWithTag(PagedPool, sizeof(pending_rmdir), ALLOC_TAG); 1107 if (!pr) { 1108 ERR("out of memory\n"); 1109 return STATUS_INSUFFICIENT_RESOURCES; 1110 } 1111 1112 pr->sd = context->lastinode.sd; 1113 pr->last_child_inode = last_inode; 1114 1115 le = context->pending_rmdirs.Flink; 1116 while (le != &context->pending_rmdirs) { 1117 pending_rmdir* pr2 = CONTAINING_RECORD(le, pending_rmdir, list_entry); 1118 1119 if (pr2->last_child_inode > pr->last_child_inode) { 1120 InsertHeadList(pr2->list_entry.Blink, &pr->list_entry); 1121 return STATUS_SUCCESS; 1122 } 1123 1124 le = le->Flink; 1125 } 1126 1127 InsertTailList(&context->pending_rmdirs, &pr->list_entry); 1128 1129 return STATUS_SUCCESS; 1130 } 1131 1132 static NTSTATUS look_for_collision(send_context* context, send_dir* sd, char* name, ULONG namelen, uint64_t* inode, bool* dir) { 1133 NTSTATUS Status; 1134 KEY searchkey; 1135 traverse_ptr tp; 1136 DIR_ITEM* di; 1137 uint16_t len; 1138 1139 searchkey.obj_id = sd->inode; 1140 searchkey.obj_type = TYPE_DIR_ITEM; 1141 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, namelen); 1142 1143 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 1144 if (!NT_SUCCESS(Status)) { 1145 ERR("find_item returned %08x\n", Status); 1146 return Status; 1147 } 1148 1149 if (keycmp(tp.item->key, searchkey)) 1150 return STATUS_SUCCESS; 1151 1152 di = (DIR_ITEM*)tp.item->data; 1153 len = tp.item->size; 1154 1155 do { 1156 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 1157 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1158 return STATUS_INTERNAL_ERROR; 1159 } 1160 1161 if (di->n == namelen && RtlCompareMemory(di->name, name, namelen) == namelen) { 1162 *inode = di->key.obj_type == TYPE_INODE_ITEM ? di->key.obj_id : 0; 1163 *dir = di->type == BTRFS_TYPE_DIRECTORY ? true: false; 1164 return STATUS_OBJECT_NAME_COLLISION; 1165 } 1166 1167 di = (DIR_ITEM*)&di->name[di->m + di->n]; 1168 len -= (uint16_t)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 1169 } while (len > 0); 1170 1171 return STATUS_SUCCESS; 1172 } 1173 1174 static NTSTATUS make_file_orphan(send_context* context, uint64_t inode, bool dir, uint64_t generation, ref* r) { 1175 NTSTATUS Status; 1176 ULONG pos = context->datalen; 1177 send_dir* sd = NULL; 1178 orphan* o; 1179 LIST_ENTRY* le; 1180 char name[64]; 1181 1182 if (!dir) { 1183 deleted_child* dc; 1184 1185 dc = ExAllocatePoolWithTag(PagedPool, offsetof(deleted_child, name[0]) + r->namelen, ALLOC_TAG); 1186 if (!dc) { 1187 ERR("out of memory\n"); 1188 return STATUS_INSUFFICIENT_RESOURCES; 1189 } 1190 1191 dc->namelen = r->namelen; 1192 RtlCopyMemory(dc->name, r->name, r->namelen); 1193 InsertTailList(&r->sd->deleted_children, &dc->list_entry); 1194 } 1195 1196 le = context->orphans.Flink; 1197 while (le != &context->orphans) { 1198 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 1199 1200 if (o2->inode == inode) { 1201 send_command(context, BTRFS_SEND_CMD_UNLINK); 1202 1203 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1204 1205 send_command_finish(context, pos); 1206 1207 return STATUS_SUCCESS; 1208 } else if (o2->inode > inode) 1209 break; 1210 1211 le = le->Flink; 1212 } 1213 1214 Status = get_orphan_name(context, inode, generation, name); 1215 if (!NT_SUCCESS(Status)) { 1216 ERR("get_orphan_name returned %08x\n", Status); 1217 return Status; 1218 } 1219 1220 if (dir) { 1221 Status = find_send_dir(context, inode, generation, &sd, NULL); 1222 if (!NT_SUCCESS(Status)) { 1223 ERR("find_send_dir returned %08x\n", Status); 1224 return Status; 1225 } 1226 1227 sd->dummy = true; 1228 1229 send_command(context, BTRFS_SEND_CMD_RENAME); 1230 1231 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1232 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name)); 1233 1234 send_command_finish(context, pos); 1235 1236 if (sd->name) 1237 ExFreePool(sd->name); 1238 1239 sd->namelen = (uint16_t)strlen(name); 1240 sd->name = ExAllocatePoolWithTag(PagedPool, sd->namelen, ALLOC_TAG); 1241 if (!sd->name) { 1242 ERR("out of memory\n"); 1243 return STATUS_INSUFFICIENT_RESOURCES; 1244 } 1245 1246 RtlCopyMemory(sd->name, name, sd->namelen); 1247 sd->parent = context->root_dir; 1248 } else { 1249 send_command(context, BTRFS_SEND_CMD_RENAME); 1250 1251 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1252 1253 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name)); 1254 1255 send_command_finish(context, pos); 1256 } 1257 1258 o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 1259 if (!o) { 1260 ERR("out of memory\n"); 1261 return STATUS_INSUFFICIENT_RESOURCES; 1262 } 1263 1264 o->inode = inode; 1265 o->dir = true; 1266 strcpy(o->tmpname, name); 1267 o->sd = sd; 1268 add_orphan(context, o); 1269 1270 return STATUS_SUCCESS; 1271 } 1272 1273 static NTSTATUS flush_refs(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 1274 NTSTATUS Status; 1275 LIST_ENTRY* le; 1276 ref *nameref = NULL, *nameref2 = NULL; 1277 1278 if (context->lastinode.mode & __S_IFDIR) { // directory 1279 ref* r = IsListEmpty(&context->lastinode.refs) ? NULL : CONTAINING_RECORD(context->lastinode.refs.Flink, ref, list_entry); 1280 ref* or = IsListEmpty(&context->lastinode.oldrefs) ? NULL : CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry); 1281 1282 if (or && !context->lastinode.o) { 1283 ULONG len = find_path_len(or->sd, or->namelen); 1284 1285 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1286 if (!context->lastinode.path) { 1287 ERR("out of memory\n"); 1288 return STATUS_INSUFFICIENT_RESOURCES; 1289 } 1290 1291 find_path(context->lastinode.path, or->sd, or->name, or->namelen); 1292 context->lastinode.path[len] = 0; 1293 1294 if (!context->lastinode.sd) { 1295 Status = find_send_dir(context, context->lastinode.inode, context->lastinode.gen, &context->lastinode.sd, false); 1296 if (!NT_SUCCESS(Status)) { 1297 ERR("find_send_dir returned %08x\n", Status); 1298 return Status; 1299 } 1300 } 1301 } 1302 1303 if (r && or) { 1304 uint64_t inode; 1305 bool dir; 1306 1307 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir); 1308 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) { 1309 ERR("look_for_collision returned %08x\n", Status); 1310 return Status; 1311 } 1312 1313 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) { 1314 Status = make_file_orphan(context, inode, dir, context->parent->root_item.ctransid, r); 1315 if (!NT_SUCCESS(Status)) { 1316 ERR("make_file_orphan returned %08x\n", Status); 1317 return Status; 1318 } 1319 } 1320 1321 if (context->lastinode.o) { 1322 Status = found_path(context, r->sd, r->name, r->namelen); 1323 if (!NT_SUCCESS(Status)) { 1324 ERR("found_path returned %08x\n", Status); 1325 return Status; 1326 } 1327 1328 if (!r->sd->dummy) 1329 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1330 } else if (r->sd != or->sd || r->namelen != or->namelen || RtlCompareMemory(r->name, or->name, r->namelen) != r->namelen) { // moved or renamed 1331 ULONG pos = context->datalen, len; 1332 1333 send_command(context, BTRFS_SEND_CMD_RENAME); 1334 1335 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path)); 1336 1337 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, r->sd, r->name, r->namelen); 1338 1339 send_command_finish(context, pos); 1340 1341 if (!r->sd->dummy) 1342 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1343 1344 if (context->lastinode.sd->name) 1345 ExFreePool(context->lastinode.sd->name); 1346 1347 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, r->namelen, ALLOC_TAG); 1348 if (!context->lastinode.sd->name) { 1349 ERR("out of memory\n"); 1350 return STATUS_INSUFFICIENT_RESOURCES; 1351 } 1352 1353 RtlCopyMemory(context->lastinode.sd->name, r->name, r->namelen); 1354 context->lastinode.sd->parent = r->sd; 1355 1356 if (context->lastinode.path) 1357 ExFreePool(context->lastinode.path); 1358 1359 len = find_path_len(r->sd, r->namelen); 1360 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1361 if (!context->lastinode.path) { 1362 ERR("out of memory\n"); 1363 return STATUS_INSUFFICIENT_RESOURCES; 1364 } 1365 1366 find_path(context->lastinode.path, r->sd, r->name, r->namelen); 1367 context->lastinode.path[len] = 0; 1368 } 1369 } else if (r && !or) { // new 1370 Status = found_path(context, r->sd, r->name, r->namelen); 1371 if (!NT_SUCCESS(Status)) { 1372 ERR("found_path returned %08x\n", Status); 1373 return Status; 1374 } 1375 1376 if (!r->sd->dummy) 1377 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1378 } else { // deleted 1379 uint64_t last_inode; 1380 1381 Status = get_dir_last_child(context, &last_inode); 1382 if (!NT_SUCCESS(Status)) { 1383 ERR("get_dir_last_child returned %08x\n", Status); 1384 return Status; 1385 } 1386 1387 if (last_inode <= context->lastinode.inode) { 1388 send_rmdir_command(context, (uint16_t)strlen(context->lastinode.path), context->lastinode.path); 1389 1390 if (!or->sd->dummy) 1391 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime); 1392 } else { 1393 char name[64]; 1394 ULONG pos = context->datalen; 1395 1396 Status = get_orphan_name(context, context->lastinode.inode, context->lastinode.gen, name); 1397 if (!NT_SUCCESS(Status)) { 1398 ERR("get_orphan_name returned %08x\n", Status); 1399 return Status; 1400 } 1401 1402 send_command(context, BTRFS_SEND_CMD_RENAME); 1403 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path)); 1404 send_add_tlv(context, BTRFS_SEND_TLV_PATH_TO, name, (uint16_t)strlen(name)); 1405 send_command_finish(context, pos); 1406 1407 if (context->lastinode.sd->name) 1408 ExFreePool(context->lastinode.sd->name); 1409 1410 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, strlen(name), ALLOC_TAG); 1411 if (!context->lastinode.sd->name) { 1412 ERR("out of memory\n"); 1413 return STATUS_INSUFFICIENT_RESOURCES; 1414 } 1415 1416 RtlCopyMemory(context->lastinode.sd->name, name, strlen(name)); 1417 context->lastinode.sd->namelen = (uint16_t)strlen(name); 1418 context->lastinode.sd->dummy = true; 1419 context->lastinode.sd->parent = NULL; 1420 1421 send_utimes_command(context, NULL, &context->root_dir->atime, &context->root_dir->mtime, &context->root_dir->ctime); 1422 1423 Status = add_pending_rmdir(context, last_inode); 1424 if (!NT_SUCCESS(Status)) { 1425 ERR("add_pending_rmdir returned %08x\n", Status); 1426 return Status; 1427 } 1428 } 1429 } 1430 1431 while (!IsListEmpty(&context->lastinode.refs)) { 1432 r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry); 1433 ExFreePool(r); 1434 } 1435 1436 while (!IsListEmpty(&context->lastinode.oldrefs)) { 1437 or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry); 1438 ExFreePool(or); 1439 } 1440 1441 return STATUS_SUCCESS; 1442 } else { 1443 if (!IsListEmpty(&context->lastinode.oldrefs)) { 1444 ref* or = CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry); 1445 ULONG len = find_path_len(or->sd, or->namelen); 1446 1447 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1448 if (!context->lastinode.path) { 1449 ERR("out of memory\n"); 1450 return STATUS_INSUFFICIENT_RESOURCES; 1451 } 1452 1453 find_path(context->lastinode.path, or->sd, or->name, or->namelen); 1454 context->lastinode.path[len] = 0; 1455 nameref = or; 1456 } 1457 1458 // remove unchanged refs 1459 le = context->lastinode.oldrefs.Flink; 1460 while (le != &context->lastinode.oldrefs) { 1461 ref* or = CONTAINING_RECORD(le, ref, list_entry); 1462 LIST_ENTRY* le2; 1463 bool matched = false; 1464 1465 le2 = context->lastinode.refs.Flink; 1466 while (le2 != &context->lastinode.refs) { 1467 ref* r = CONTAINING_RECORD(le2, ref, list_entry); 1468 1469 if (r->sd == or->sd && r->namelen == or->namelen && RtlCompareMemory(r->name, or->name, r->namelen) == r->namelen) { 1470 RemoveEntryList(&r->list_entry); 1471 ExFreePool(r); 1472 matched = true; 1473 break; 1474 } 1475 1476 le2 = le2->Flink; 1477 } 1478 1479 if (matched) { 1480 le = le->Flink; 1481 RemoveEntryList(&or->list_entry); 1482 ExFreePool(or); 1483 continue; 1484 } 1485 1486 le = le->Flink; 1487 } 1488 1489 while (!IsListEmpty(&context->lastinode.refs)) { 1490 ref* r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry); 1491 uint64_t inode; 1492 bool dir; 1493 1494 if (context->parent) { 1495 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir); 1496 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) { 1497 ERR("look_for_collision returned %08x\n", Status); 1498 return Status; 1499 } 1500 1501 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) { 1502 Status = make_file_orphan(context, inode, dir, context->lastinode.gen, r); 1503 if (!NT_SUCCESS(Status)) { 1504 ERR("make_file_orphan returned %08x\n", Status); 1505 return Status; 1506 } 1507 } 1508 } 1509 1510 if (context->datalen > SEND_BUFFER_LENGTH) { 1511 Status = wait_for_flush(context, tp1, tp2); 1512 if (!NT_SUCCESS(Status)) { 1513 ERR("wait_for_flush returned %08x\n", Status); 1514 return Status; 1515 } 1516 1517 if (context->send->cancelling) 1518 return STATUS_SUCCESS; 1519 } 1520 1521 Status = found_path(context, r->sd, r->name, r->namelen); 1522 if (!NT_SUCCESS(Status)) { 1523 ERR("found_path returned %08x\n", Status); 1524 return Status; 1525 } 1526 1527 if (!r->sd->dummy) 1528 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1529 1530 if (nameref && !nameref2) 1531 nameref2 = r; 1532 else 1533 ExFreePool(r); 1534 } 1535 1536 while (!IsListEmpty(&context->lastinode.oldrefs)) { 1537 ref* or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry); 1538 bool deleted = false; 1539 1540 le = or->sd->deleted_children.Flink; 1541 while (le != &or->sd->deleted_children) { 1542 deleted_child* dc = CONTAINING_RECORD(le, deleted_child, list_entry); 1543 1544 if (dc->namelen == or->namelen && RtlCompareMemory(dc->name, or->name, or->namelen) == or->namelen) { 1545 RemoveEntryList(&dc->list_entry); 1546 ExFreePool(dc); 1547 deleted = true; 1548 break; 1549 } 1550 1551 le = le->Flink; 1552 } 1553 1554 if (!deleted) { 1555 if (context->datalen > SEND_BUFFER_LENGTH) { 1556 Status = wait_for_flush(context, tp1, tp2); 1557 if (!NT_SUCCESS(Status)) { 1558 ERR("wait_for_flush returned %08x\n", Status); 1559 return Status; 1560 } 1561 1562 if (context->send->cancelling) 1563 return STATUS_SUCCESS; 1564 } 1565 1566 Status = send_unlink_command(context, or->sd, or->namelen, or->name); 1567 if (!NT_SUCCESS(Status)) { 1568 ERR("send_unlink_command returned %08x\n", Status); 1569 return Status; 1570 } 1571 1572 if (!or->sd->dummy) 1573 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime); 1574 } 1575 1576 if (or == nameref && nameref2) { 1577 uint16_t len = find_path_len(nameref2->sd, nameref2->namelen); 1578 1579 if (context->lastinode.path) 1580 ExFreePool(context->lastinode.path); 1581 1582 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1583 if (!context->lastinode.path) { 1584 ERR("out of memory\n"); 1585 return STATUS_INSUFFICIENT_RESOURCES; 1586 } 1587 1588 find_path(context->lastinode.path, nameref2->sd, nameref2->name, nameref2->namelen); 1589 context->lastinode.path[len] = 0; 1590 1591 ExFreePool(nameref2); 1592 } 1593 1594 ExFreePool(or); 1595 } 1596 } 1597 1598 return STATUS_SUCCESS; 1599 } 1600 1601 static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 1602 NTSTATUS Status; 1603 KEY key1, key2; 1604 1605 if (tp1) 1606 key1 = tp1->item->key; 1607 1608 if (tp2) 1609 key2 = tp2->item->key; 1610 1611 ExReleaseResourceLite(&context->Vcb->tree_lock); 1612 1613 KeClearEvent(&context->send->cleared_event); 1614 KeSetEvent(&context->buffer_event, 0, true); 1615 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 1616 1617 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 1618 1619 if (context->send->cancelling) 1620 return STATUS_SUCCESS; 1621 1622 if (tp1) { 1623 Status = find_item(context->Vcb, context->root, tp1, &key1, false, NULL); 1624 if (!NT_SUCCESS(Status)) { 1625 ERR("find_item returned %08x\n", Status); 1626 return Status; 1627 } 1628 1629 if (keycmp(tp1->item->key, key1)) { 1630 ERR("readonly subvolume changed\n"); 1631 return STATUS_INTERNAL_ERROR; 1632 } 1633 } 1634 1635 if (tp2) { 1636 Status = find_item(context->Vcb, context->parent, tp2, &key2, false, NULL); 1637 if (!NT_SUCCESS(Status)) { 1638 ERR("find_item returned %08x\n", Status); 1639 return Status; 1640 } 1641 1642 if (keycmp(tp2->item->key, key2)) { 1643 ERR("readonly subvolume changed\n"); 1644 return STATUS_INTERNAL_ERROR; 1645 } 1646 } 1647 1648 return STATUS_SUCCESS; 1649 } 1650 1651 static NTSTATUS add_ext_holes(device_extension* Vcb, LIST_ENTRY* exts, uint64_t size) { 1652 uint64_t lastoff = 0; 1653 LIST_ENTRY* le; 1654 1655 le = exts->Flink; 1656 while (le != exts) { 1657 send_ext* ext = CONTAINING_RECORD(le, send_ext, list_entry); 1658 1659 if (ext->offset > lastoff) { 1660 send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1661 EXTENT_DATA2* ed2; 1662 1663 if (!ext2) { 1664 ERR("out of memory\n"); 1665 return STATUS_INSUFFICIENT_RESOURCES; 1666 } 1667 1668 ed2 = (EXTENT_DATA2*)ext2->data.data; 1669 1670 ext2->offset = lastoff; 1671 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1672 ext2->data.decoded_size = ed2->num_bytes = ext->offset - lastoff; 1673 ext2->data.type = EXTENT_TYPE_REGULAR; 1674 ed2->address = ed2->size = ed2->offset = 0; 1675 1676 InsertHeadList(le->Blink, &ext2->list_entry); 1677 } 1678 1679 if (ext->data.type == EXTENT_TYPE_INLINE) 1680 lastoff = ext->offset + ext->data.decoded_size; 1681 else { 1682 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data.data; 1683 lastoff = ext->offset + ed2->num_bytes; 1684 } 1685 1686 le = le->Flink; 1687 } 1688 1689 if (size > lastoff) { 1690 send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1691 EXTENT_DATA2* ed2; 1692 1693 if (!ext2) { 1694 ERR("out of memory\n"); 1695 return STATUS_INSUFFICIENT_RESOURCES; 1696 } 1697 1698 ed2 = (EXTENT_DATA2*)ext2->data.data; 1699 1700 ext2->offset = lastoff; 1701 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1702 ext2->data.decoded_size = ed2->num_bytes = sector_align(size - lastoff, Vcb->superblock.sector_size); 1703 ext2->data.type = EXTENT_TYPE_REGULAR; 1704 ed2->address = ed2->size = ed2->offset = 0; 1705 1706 InsertTailList(exts, &ext2->list_entry); 1707 } 1708 1709 return STATUS_SUCCESS; 1710 } 1711 1712 static NTSTATUS divide_ext(send_ext* ext, uint64_t len, bool trunc) { 1713 send_ext* ext2; 1714 EXTENT_DATA2 *ed2a, *ed2b; 1715 1716 if (ext->data.type == EXTENT_TYPE_INLINE) { 1717 if (!trunc) { 1718 ext2 = ExAllocatePoolWithTag(PagedPool, (ULONG)(offsetof(send_ext, data.data) + ext->data.decoded_size - len), ALLOC_TAG); 1719 1720 if (!ext2) { 1721 ERR("out of memory\n"); 1722 return STATUS_INSUFFICIENT_RESOURCES; 1723 } 1724 1725 ext2->offset = ext->offset + len; 1726 ext2->datalen = (ULONG)(ext->data.decoded_size - len); 1727 ext2->data.decoded_size = ext->data.decoded_size - len; 1728 ext2->data.compression = ext->data.compression; 1729 ext2->data.encryption = ext->data.encryption; 1730 ext2->data.encoding = ext->data.encoding; 1731 ext2->data.type = ext->data.type; 1732 RtlCopyMemory(ext2->data.data, ext->data.data + len, (ULONG)(ext->data.decoded_size - len)); 1733 1734 InsertHeadList(&ext->list_entry, &ext2->list_entry); 1735 } 1736 1737 ext->data.decoded_size = len; 1738 1739 return STATUS_SUCCESS; 1740 } 1741 1742 ed2a = (EXTENT_DATA2*)ext->data.data; 1743 1744 if (!trunc) { 1745 ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1746 1747 if (!ext2) { 1748 ERR("out of memory\n"); 1749 return STATUS_INSUFFICIENT_RESOURCES; 1750 } 1751 1752 ed2b = (EXTENT_DATA2*)ext2->data.data; 1753 1754 ext2->offset = ext->offset + len; 1755 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1756 1757 ext2->data.compression = ext->data.compression; 1758 ext2->data.encryption = ext->data.encryption; 1759 ext2->data.encoding = ext->data.encoding; 1760 ext2->data.type = ext->data.type; 1761 ed2b->num_bytes = ed2a->num_bytes - len; 1762 1763 if (ed2a->size == 0) { 1764 ext2->data.decoded_size = ed2b->num_bytes; 1765 ext->data.decoded_size = len; 1766 1767 ed2b->address = ed2b->size = ed2b->offset = 0; 1768 } else { 1769 ext2->data.decoded_size = ext->data.decoded_size; 1770 1771 ed2b->address = ed2a->address; 1772 ed2b->size = ed2a->size; 1773 ed2b->offset = ed2a->offset + len; 1774 } 1775 1776 InsertHeadList(&ext->list_entry, &ext2->list_entry); 1777 } 1778 1779 ed2a->num_bytes = len; 1780 1781 return STATUS_SUCCESS; 1782 } 1783 1784 static NTSTATUS sync_ext_cutoff_points(send_context* context) { 1785 NTSTATUS Status; 1786 send_ext *ext1, *ext2; 1787 1788 ext1 = CONTAINING_RECORD(context->lastinode.exts.Flink, send_ext, list_entry); 1789 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Flink, send_ext, list_entry); 1790 1791 do { 1792 uint64_t len1, len2; 1793 EXTENT_DATA2 *ed2a, *ed2b; 1794 1795 ed2a = ext1->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext1->data.data; 1796 ed2b = ext2->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext2->data.data; 1797 1798 len1 = ed2a ? ed2a->num_bytes : ext1->data.decoded_size; 1799 len2 = ed2b ? ed2b->num_bytes : ext2->data.decoded_size; 1800 1801 if (len1 < len2) { 1802 Status = divide_ext(ext2, len1, false); 1803 if (!NT_SUCCESS(Status)) { 1804 ERR("divide_ext returned %08x\n", Status); 1805 return Status; 1806 } 1807 } else if (len2 < len1) { 1808 Status = divide_ext(ext1, len2, false); 1809 if (!NT_SUCCESS(Status)) { 1810 ERR("divide_ext returned %08x\n", Status); 1811 return Status; 1812 } 1813 } 1814 1815 if (ext1->list_entry.Flink == &context->lastinode.exts || ext2->list_entry.Flink == &context->lastinode.oldexts) 1816 break; 1817 1818 ext1 = CONTAINING_RECORD(ext1->list_entry.Flink, send_ext, list_entry); 1819 ext2 = CONTAINING_RECORD(ext2->list_entry.Flink, send_ext, list_entry); 1820 } while (true); 1821 1822 ext1 = CONTAINING_RECORD(context->lastinode.exts.Blink, send_ext, list_entry); 1823 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Blink, send_ext, list_entry); 1824 1825 Status = divide_ext(ext1, context->lastinode.size - ext1->offset, true); 1826 if (!NT_SUCCESS(Status)) { 1827 ERR("divide_ext returned %08x\n", Status); 1828 return Status; 1829 } 1830 1831 Status = divide_ext(ext2, context->lastinode.size - ext2->offset, true); 1832 if (!NT_SUCCESS(Status)) { 1833 ERR("divide_ext returned %08x\n", Status); 1834 return Status; 1835 } 1836 1837 return STATUS_SUCCESS; 1838 } 1839 1840 static bool send_add_tlv_clone_path(send_context* context, root* r, uint64_t inode) { 1841 NTSTATUS Status; 1842 KEY searchkey; 1843 traverse_ptr tp; 1844 uint16_t len = 0; 1845 uint64_t num; 1846 uint8_t* ptr; 1847 1848 num = inode; 1849 1850 while (num != SUBVOL_ROOT_INODE) { 1851 searchkey.obj_id = num; 1852 searchkey.obj_type = TYPE_INODE_EXTREF; 1853 searchkey.offset = 0xffffffffffffffff; 1854 1855 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1856 if (!NT_SUCCESS(Status)) { 1857 ERR("find_item returned %08x\n", Status); 1858 return false; 1859 } 1860 1861 if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_INODE_REF && tp.item->key.obj_type != TYPE_INODE_EXTREF)) { 1862 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id); 1863 return false; 1864 } 1865 1866 if (len > 0) 1867 len++; 1868 1869 if (tp.item->key.obj_type == TYPE_INODE_REF) { 1870 INODE_REF* ir = (INODE_REF*)tp.item->data; 1871 1872 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) { 1873 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1874 return false; 1875 } 1876 1877 len += ir->n; 1878 num = tp.item->key.offset; 1879 } else { 1880 INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data; 1881 1882 if (tp.item->size < sizeof(INODE_EXTREF) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) { 1883 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1884 return false; 1885 } 1886 1887 len += ier->n; 1888 num = ier->dir; 1889 } 1890 } 1891 1892 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_PATH, NULL, len); 1893 ptr = &context->data[context->datalen]; 1894 1895 num = inode; 1896 1897 while (num != SUBVOL_ROOT_INODE) { 1898 searchkey.obj_id = num; 1899 searchkey.obj_type = TYPE_INODE_EXTREF; 1900 searchkey.offset = 0xffffffffffffffff; 1901 1902 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1903 if (!NT_SUCCESS(Status)) { 1904 ERR("find_item returned %08x\n", Status); 1905 return false; 1906 } 1907 1908 if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_INODE_REF && tp.item->key.obj_type != TYPE_INODE_EXTREF)) { 1909 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id); 1910 return false; 1911 } 1912 1913 if (num != inode) { 1914 ptr--; 1915 *ptr = '/'; 1916 } 1917 1918 if (tp.item->key.obj_type == TYPE_INODE_REF) { 1919 INODE_REF* ir = (INODE_REF*)tp.item->data; 1920 1921 RtlCopyMemory(ptr - ir->n, ir->name, ir->n); 1922 ptr -= ir->n; 1923 num = tp.item->key.offset; 1924 } else { 1925 INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data; 1926 1927 RtlCopyMemory(ptr - ier->n, ier->name, ier->n); 1928 ptr -= ier->n; 1929 num = ier->dir; 1930 } 1931 } 1932 1933 return true; 1934 } 1935 1936 static bool try_clone_edr(send_context* context, send_ext* se, EXTENT_DATA_REF* edr) { 1937 NTSTATUS Status; 1938 root* r = NULL; 1939 KEY searchkey; 1940 traverse_ptr tp; 1941 EXTENT_DATA2* seed2 = (EXTENT_DATA2*)se->data.data; 1942 1943 if (context->parent && edr->root == context->parent->id) 1944 r = context->parent; 1945 1946 if (!r && context->num_clones > 0) { 1947 ULONG i; 1948 1949 for (i = 0; i < context->num_clones; i++) { 1950 if (context->clones[i]->id == edr->root && context->clones[i] != context->root) { 1951 r = context->clones[i]; 1952 break; 1953 } 1954 } 1955 } 1956 1957 if (!r) 1958 return false; 1959 1960 searchkey.obj_id = edr->objid; 1961 searchkey.obj_type = TYPE_EXTENT_DATA; 1962 searchkey.offset = 0; 1963 1964 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1965 if (!NT_SUCCESS(Status)) { 1966 ERR("find_item returned %08x\n", Status); 1967 return false; 1968 } 1969 1970 while (true) { 1971 traverse_ptr next_tp; 1972 1973 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 1974 if (tp.item->size < sizeof(EXTENT_DATA)) 1975 ERR("(%I64x,%x,%I64x) has size %u, not at least %u as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); 1976 else { 1977 EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; 1978 1979 if (ed->type == EXTENT_TYPE_REGULAR) { 1980 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) 1981 ERR("(%I64x,%x,%I64x) has size %u, not %u as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 1982 tp.item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 1983 else { 1984 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 1985 1986 if (ed2->address == seed2->address && ed2->size == seed2->size && seed2->offset <= ed2->offset && seed2->offset + seed2->num_bytes >= ed2->offset + ed2->num_bytes) { 1987 uint64_t clone_offset = tp.item->key.offset + ed2->offset - seed2->offset; 1988 uint64_t clone_len = min(context->lastinode.size - se->offset, ed2->num_bytes); 1989 1990 if (clone_offset % context->Vcb->superblock.sector_size == 0 && clone_len % context->Vcb->superblock.sector_size == 0) { 1991 ULONG pos = context->datalen; 1992 1993 send_command(context, BTRFS_SEND_CMD_CLONE); 1994 1995 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t)); 1996 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_LENGTH, &clone_len, sizeof(uint64_t)); 1997 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 1998 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID)); 1999 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &r->root_item.ctransid, sizeof(uint64_t)); 2000 2001 if (!send_add_tlv_clone_path(context, r, tp.item->key.obj_id)) 2002 context->datalen = pos; 2003 else { 2004 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_OFFSET, &clone_offset, sizeof(uint64_t)); 2005 2006 send_command_finish(context, pos); 2007 2008 return true; 2009 } 2010 } 2011 } 2012 } 2013 } 2014 } 2015 } else if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type)) 2016 break; 2017 2018 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 2019 tp = next_tp; 2020 else 2021 break; 2022 } 2023 2024 return false; 2025 } 2026 2027 static bool try_clone(send_context* context, send_ext* se) { 2028 NTSTATUS Status; 2029 KEY searchkey; 2030 traverse_ptr tp; 2031 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)se->data.data; 2032 EXTENT_ITEM* ei; 2033 uint64_t rc = 0; 2034 2035 searchkey.obj_id = ed2->address; 2036 searchkey.obj_type = TYPE_EXTENT_ITEM; 2037 searchkey.offset = ed2->size; 2038 2039 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL); 2040 if (!NT_SUCCESS(Status)) { 2041 ERR("find_item returned %08x\n", Status); 2042 return false; 2043 } 2044 2045 if (keycmp(tp.item->key, searchkey)) { 2046 ERR("(%I64x,%x,%I64x) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 2047 return false; 2048 } 2049 2050 if (tp.item->size < sizeof(EXTENT_ITEM)) { 2051 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM)); 2052 return false; 2053 } 2054 2055 ei = (EXTENT_ITEM*)tp.item->data; 2056 2057 if (tp.item->size > sizeof(EXTENT_ITEM)) { 2058 uint32_t len = tp.item->size - sizeof(EXTENT_ITEM); 2059 uint8_t* ptr = (uint8_t*)&ei[1]; 2060 2061 while (len > 0) { 2062 uint8_t secttype = *ptr; 2063 ULONG sectlen = get_extent_data_len(secttype); 2064 uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t)); 2065 2066 len--; 2067 2068 if (sectlen > len) { 2069 ERR("(%I64x,%x,%I64x): %x bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen); 2070 return false; 2071 } 2072 2073 if (sectlen == 0) { 2074 ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype); 2075 return false; 2076 } 2077 2078 rc += sectcount; 2079 2080 if (secttype == TYPE_EXTENT_DATA_REF) { 2081 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t)); 2082 2083 if (try_clone_edr(context, se, sectedr)) 2084 return true; 2085 } 2086 2087 len -= sectlen; 2088 ptr += sizeof(uint8_t) + sectlen; 2089 } 2090 } 2091 2092 if (rc >= ei->refcount) 2093 return false; 2094 2095 searchkey.obj_type = TYPE_EXTENT_DATA_REF; 2096 searchkey.offset = 0; 2097 2098 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL); 2099 if (!NT_SUCCESS(Status)) { 2100 ERR("find_item returned %08x\n", Status); 2101 return false; 2102 } 2103 2104 while (true) { 2105 traverse_ptr next_tp; 2106 2107 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 2108 if (tp.item->size < sizeof(EXTENT_DATA_REF)) 2109 ERR("(%I64x,%x,%I64x) has size %u, not %u as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF)); 2110 else { 2111 if (try_clone_edr(context, se, (EXTENT_DATA_REF*)tp.item->data)) 2112 return true; 2113 } 2114 } else if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type)) 2115 break; 2116 2117 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 2118 tp = next_tp; 2119 else 2120 break; 2121 } 2122 2123 return false; 2124 } 2125 2126 static NTSTATUS flush_extents(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 2127 NTSTATUS Status; 2128 2129 if ((IsListEmpty(&context->lastinode.exts) && IsListEmpty(&context->lastinode.oldexts)) || context->lastinode.size == 0) 2130 return STATUS_SUCCESS; 2131 2132 if (context->parent) { 2133 Status = add_ext_holes(context->Vcb, &context->lastinode.exts, context->lastinode.size); 2134 if (!NT_SUCCESS(Status)) { 2135 ERR("add_ext_holes returned %08x\n", Status); 2136 return Status; 2137 } 2138 2139 Status = add_ext_holes(context->Vcb, &context->lastinode.oldexts, context->lastinode.size); 2140 if (!NT_SUCCESS(Status)) { 2141 ERR("add_ext_holes returned %08x\n", Status); 2142 return Status; 2143 } 2144 2145 Status = sync_ext_cutoff_points(context); 2146 if (!NT_SUCCESS(Status)) { 2147 ERR("sync_ext_cutoff_points returned %08x\n", Status); 2148 return Status; 2149 } 2150 } 2151 2152 while (!IsListEmpty(&context->lastinode.exts)) { 2153 send_ext* se = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry); 2154 send_ext* se2 = context->parent ? CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry) : NULL; 2155 ULONG pos; 2156 EXTENT_DATA2* ed2; 2157 2158 if (se2) { 2159 if (se->data.type == EXTENT_TYPE_INLINE && se2->data.type == EXTENT_TYPE_INLINE && 2160 RtlCompareMemory(se->data.data, se2->data.data, (ULONG)se->data.decoded_size) == (ULONG)se->data.decoded_size) { 2161 ExFreePool(se); 2162 ExFreePool(se2); 2163 continue; 2164 } 2165 2166 if (se->data.type == EXTENT_TYPE_REGULAR && se2->data.type == EXTENT_TYPE_REGULAR) { 2167 EXTENT_DATA2 *ed2a, *ed2b; 2168 2169 ed2a = (EXTENT_DATA2*)se->data.data; 2170 ed2b = (EXTENT_DATA2*)se2->data.data; 2171 2172 if (RtlCompareMemory(ed2a, ed2b, sizeof(EXTENT_DATA2)) == sizeof(EXTENT_DATA2)) { 2173 ExFreePool(se); 2174 ExFreePool(se2); 2175 continue; 2176 } 2177 } 2178 } 2179 2180 if (se->data.type == EXTENT_TYPE_INLINE) { 2181 pos = context->datalen; 2182 2183 send_command(context, BTRFS_SEND_CMD_WRITE); 2184 2185 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2186 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t)); 2187 2188 if (se->data.compression == BTRFS_COMPRESSION_NONE) 2189 send_add_tlv(context, BTRFS_SEND_TLV_DATA, se->data.data, (uint16_t)se->data.decoded_size); 2190 else if (se->data.compression == BTRFS_COMPRESSION_ZLIB || se->data.compression == BTRFS_COMPRESSION_LZO || se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2191 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]); 2192 2193 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, (uint16_t)se->data.decoded_size); 2194 RtlZeroMemory(&context->data[context->datalen - se->data.decoded_size], (ULONG)se->data.decoded_size); 2195 2196 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) { 2197 Status = zlib_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size); 2198 if (!NT_SUCCESS(Status)) { 2199 ERR("zlib_decompress returned %08x\n", Status); 2200 ExFreePool(se); 2201 if (se2) ExFreePool(se2); 2202 return Status; 2203 } 2204 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) { 2205 if (inlen < sizeof(uint32_t)) { 2206 ERR("extent data was truncated\n"); 2207 ExFreePool(se); 2208 if (se2) ExFreePool(se2); 2209 return STATUS_INTERNAL_ERROR; 2210 } else 2211 inlen -= sizeof(uint32_t); 2212 2213 Status = lzo_decompress(se->data.data + sizeof(uint32_t), inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size, sizeof(uint32_t)); 2214 if (!NT_SUCCESS(Status)) { 2215 ERR("lzo_decompress returned %08x\n", Status); 2216 ExFreePool(se); 2217 if (se2) ExFreePool(se2); 2218 return Status; 2219 } 2220 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2221 Status = zstd_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size); 2222 if (!NT_SUCCESS(Status)) { 2223 ERR("zlib_decompress returned %08x\n", Status); 2224 ExFreePool(se); 2225 if (se2) ExFreePool(se2); 2226 return Status; 2227 } 2228 } 2229 } else { 2230 ERR("unhandled compression type %x\n", se->data.compression); 2231 ExFreePool(se); 2232 if (se2) ExFreePool(se2); 2233 return STATUS_NOT_IMPLEMENTED; 2234 } 2235 2236 send_command_finish(context, pos); 2237 2238 ExFreePool(se); 2239 if (se2) ExFreePool(se2); 2240 continue; 2241 } 2242 2243 ed2 = (EXTENT_DATA2*)se->data.data; 2244 2245 if (ed2->size != 0 && (context->parent || context->num_clones > 0)) { 2246 if (try_clone(context, se)) { 2247 ExFreePool(se); 2248 if (se2) ExFreePool(se2); 2249 continue; 2250 } 2251 } 2252 2253 if (ed2->size == 0) { // write sparse 2254 uint64_t off, offset; 2255 2256 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2257 uint16_t length = (uint16_t)min(min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE), context->lastinode.size - se->offset - off); 2258 2259 if (context->datalen > SEND_BUFFER_LENGTH) { 2260 Status = wait_for_flush(context, tp1, tp2); 2261 if (!NT_SUCCESS(Status)) { 2262 ERR("wait_for_flush returned %08x\n", Status); 2263 ExFreePool(se); 2264 if (se2) ExFreePool(se2); 2265 return Status; 2266 } 2267 2268 if (context->send->cancelling) { 2269 ExFreePool(se); 2270 if (se2) ExFreePool(se2); 2271 return STATUS_SUCCESS; 2272 } 2273 } 2274 2275 pos = context->datalen; 2276 2277 send_command(context, BTRFS_SEND_CMD_WRITE); 2278 2279 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2280 2281 offset = se->offset + off; 2282 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2283 2284 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, length); 2285 RtlZeroMemory(&context->data[context->datalen - length], length); 2286 2287 send_command_finish(context, pos); 2288 } 2289 } else if (se->data.compression == BTRFS_COMPRESSION_NONE) { 2290 uint64_t off, offset; 2291 uint8_t* buf; 2292 2293 buf = ExAllocatePoolWithTag(NonPagedPool, MAX_SEND_WRITE + (2 * context->Vcb->superblock.sector_size), ALLOC_TAG); 2294 if (!buf) { 2295 ERR("out of memory\n"); 2296 ExFreePool(se); 2297 if (se2) ExFreePool(se2); 2298 return STATUS_INSUFFICIENT_RESOURCES; 2299 } 2300 2301 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2302 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE); 2303 ULONG skip_start; 2304 uint64_t addr = ed2->address + off; 2305 uint32_t* csum; 2306 2307 if (context->datalen > SEND_BUFFER_LENGTH) { 2308 Status = wait_for_flush(context, tp1, tp2); 2309 if (!NT_SUCCESS(Status)) { 2310 ERR("wait_for_flush returned %08x\n", Status); 2311 ExFreePool(buf); 2312 ExFreePool(se); 2313 if (se2) ExFreePool(se2); 2314 return Status; 2315 } 2316 2317 if (context->send->cancelling) { 2318 ExFreePool(buf); 2319 ExFreePool(se); 2320 if (se2) ExFreePool(se2); 2321 return STATUS_SUCCESS; 2322 } 2323 } 2324 2325 skip_start = addr % context->Vcb->superblock.sector_size; 2326 addr -= skip_start; 2327 2328 if (context->lastinode.flags & BTRFS_INODE_NODATASUM) 2329 csum = NULL; 2330 else { 2331 uint32_t len; 2332 2333 len = (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size) / context->Vcb->superblock.sector_size; 2334 2335 csum = ExAllocatePoolWithTag(PagedPool, len * sizeof(uint32_t), ALLOC_TAG); 2336 if (!csum) { 2337 ERR("out of memory\n"); 2338 ExFreePool(buf); 2339 ExFreePool(se); 2340 if (se2) ExFreePool(se2); 2341 return STATUS_INSUFFICIENT_RESOURCES; 2342 } 2343 2344 Status = load_csum(context->Vcb, csum, addr, len, NULL); 2345 if (!NT_SUCCESS(Status)) { 2346 ERR("load_csum returned %08x\n", Status); 2347 ExFreePool(csum); 2348 ExFreePool(buf); 2349 ExFreePool(se); 2350 if (se2) ExFreePool(se2); 2351 return STATUS_INSUFFICIENT_RESOURCES; 2352 } 2353 } 2354 2355 Status = read_data(context->Vcb, addr, (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size), 2356 csum, false, buf, NULL, NULL, NULL, 0, false, NormalPagePriority); 2357 if (!NT_SUCCESS(Status)) { 2358 ERR("read_data returned %08x\n", Status); 2359 ExFreePool(buf); 2360 ExFreePool(se); 2361 if (se2) ExFreePool(se2); 2362 if (csum) ExFreePool(csum); 2363 return Status; 2364 } 2365 2366 if (csum) 2367 ExFreePool(csum); 2368 2369 pos = context->datalen; 2370 2371 send_command(context, BTRFS_SEND_CMD_WRITE); 2372 2373 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2374 2375 offset = se->offset + off; 2376 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2377 2378 length = (uint16_t)min(context->lastinode.size - se->offset - off, length); 2379 send_add_tlv(context, BTRFS_SEND_TLV_DATA, buf + skip_start, length); 2380 2381 send_command_finish(context, pos); 2382 } 2383 2384 ExFreePool(buf); 2385 } else { 2386 uint8_t *buf, *compbuf; 2387 uint64_t off; 2388 uint32_t* csum; 2389 2390 buf = ExAllocatePoolWithTag(PagedPool, (ULONG)se->data.decoded_size, ALLOC_TAG); 2391 if (!buf) { 2392 ERR("out of memory\n"); 2393 ExFreePool(se); 2394 if (se2) ExFreePool(se2); 2395 return STATUS_INSUFFICIENT_RESOURCES; 2396 } 2397 2398 compbuf = ExAllocatePoolWithTag(PagedPool, (ULONG)ed2->size, ALLOC_TAG); 2399 if (!compbuf) { 2400 ERR("out of memory\n"); 2401 ExFreePool(buf); 2402 ExFreePool(se); 2403 if (se2) ExFreePool(se2); 2404 return STATUS_INSUFFICIENT_RESOURCES; 2405 } 2406 2407 if (context->lastinode.flags & BTRFS_INODE_NODATASUM) 2408 csum = NULL; 2409 else { 2410 uint32_t len; 2411 2412 len = (uint32_t)(ed2->size / context->Vcb->superblock.sector_size); 2413 2414 csum = ExAllocatePoolWithTag(PagedPool, len * sizeof(uint32_t), ALLOC_TAG); 2415 if (!csum) { 2416 ERR("out of memory\n"); 2417 ExFreePool(compbuf); 2418 ExFreePool(buf); 2419 ExFreePool(se); 2420 if (se2) ExFreePool(se2); 2421 return STATUS_INSUFFICIENT_RESOURCES; 2422 } 2423 2424 Status = load_csum(context->Vcb, csum, ed2->address, len, NULL); 2425 if (!NT_SUCCESS(Status)) { 2426 ERR("load_csum returned %08x\n", Status); 2427 ExFreePool(csum); 2428 ExFreePool(compbuf); 2429 ExFreePool(buf); 2430 ExFreePool(se); 2431 if (se2) ExFreePool(se2); 2432 return Status; 2433 } 2434 } 2435 2436 Status = read_data(context->Vcb, ed2->address, (uint32_t)ed2->size, csum, false, compbuf, NULL, NULL, NULL, 0, false, NormalPagePriority); 2437 if (!NT_SUCCESS(Status)) { 2438 ERR("read_data returned %08x\n", Status); 2439 ExFreePool(compbuf); 2440 ExFreePool(buf); 2441 ExFreePool(se); 2442 if (se2) ExFreePool(se2); 2443 if (csum) ExFreePool(csum); 2444 return Status; 2445 } 2446 2447 if (csum) 2448 ExFreePool(csum); 2449 2450 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) { 2451 Status = zlib_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size); 2452 if (!NT_SUCCESS(Status)) { 2453 ERR("zlib_decompress returned %08x\n", Status); 2454 ExFreePool(compbuf); 2455 ExFreePool(buf); 2456 ExFreePool(se); 2457 if (se2) ExFreePool(se2); 2458 return Status; 2459 } 2460 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) { 2461 Status = lzo_decompress(&compbuf[sizeof(uint32_t)], (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size, sizeof(uint32_t)); 2462 if (!NT_SUCCESS(Status)) { 2463 ERR("lzo_decompress returned %08x\n", Status); 2464 ExFreePool(compbuf); 2465 ExFreePool(buf); 2466 ExFreePool(se); 2467 if (se2) ExFreePool(se2); 2468 return Status; 2469 } 2470 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2471 Status = zstd_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size); 2472 if (!NT_SUCCESS(Status)) { 2473 ERR("zstd_decompress returned %08x\n", Status); 2474 ExFreePool(compbuf); 2475 ExFreePool(buf); 2476 ExFreePool(se); 2477 if (se2) ExFreePool(se2); 2478 return Status; 2479 } 2480 } 2481 2482 ExFreePool(compbuf); 2483 2484 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2485 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE); 2486 uint64_t offset; 2487 2488 if (context->datalen > SEND_BUFFER_LENGTH) { 2489 Status = wait_for_flush(context, tp1, tp2); 2490 if (!NT_SUCCESS(Status)) { 2491 ERR("wait_for_flush returned %08x\n", Status); 2492 ExFreePool(buf); 2493 ExFreePool(se); 2494 if (se2) ExFreePool(se2); 2495 return Status; 2496 } 2497 2498 if (context->send->cancelling) { 2499 ExFreePool(buf); 2500 ExFreePool(se); 2501 if (se2) ExFreePool(se2); 2502 return STATUS_SUCCESS; 2503 } 2504 } 2505 2506 pos = context->datalen; 2507 2508 send_command(context, BTRFS_SEND_CMD_WRITE); 2509 2510 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2511 2512 offset = se->offset + off; 2513 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2514 2515 length = (uint16_t)min(context->lastinode.size - se->offset - off, length); 2516 send_add_tlv(context, BTRFS_SEND_TLV_DATA, &buf[off], length); 2517 2518 send_command_finish(context, pos); 2519 } 2520 2521 ExFreePool(buf); 2522 } 2523 2524 ExFreePool(se); 2525 if (se2) ExFreePool(se2); 2526 } 2527 2528 return STATUS_SUCCESS; 2529 } 2530 2531 static NTSTATUS finish_inode(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 2532 LIST_ENTRY* le; 2533 2534 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2535 NTSTATUS Status = flush_refs(context, tp1, tp2); 2536 if (!NT_SUCCESS(Status)) { 2537 ERR("flush_refs returned %08x\n", Status); 2538 return Status; 2539 } 2540 2541 if (context->send->cancelling) 2542 return STATUS_SUCCESS; 2543 } 2544 2545 if (!context->lastinode.deleting) { 2546 if (context->lastinode.file) { 2547 NTSTATUS Status = flush_extents(context, tp1, tp2); 2548 if (!NT_SUCCESS(Status)) { 2549 ERR("flush_extents returned %08x\n", Status); 2550 return Status; 2551 } 2552 2553 if (context->send->cancelling) 2554 return STATUS_SUCCESS; 2555 2556 send_truncate_command(context, context->lastinode.path, context->lastinode.size); 2557 } 2558 2559 if (context->lastinode.new || context->lastinode.uid != context->lastinode.olduid || context->lastinode.gid != context->lastinode.oldgid) 2560 send_chown_command(context, context->lastinode.path, context->lastinode.uid, context->lastinode.gid); 2561 2562 if (((context->lastinode.mode & __S_IFLNK) != __S_IFLNK || ((context->lastinode.mode & 07777) != 0777)) && 2563 (context->lastinode.new || context->lastinode.mode != context->lastinode.oldmode)) 2564 send_chmod_command(context, context->lastinode.path, context->lastinode.mode); 2565 2566 send_utimes_command(context, context->lastinode.path, &context->lastinode.atime, &context->lastinode.mtime, &context->lastinode.ctime); 2567 } 2568 2569 while (!IsListEmpty(&context->lastinode.exts)) { 2570 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry)); 2571 } 2572 2573 while (!IsListEmpty(&context->lastinode.oldexts)) { 2574 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry)); 2575 } 2576 2577 if (context->parent) { 2578 le = context->pending_rmdirs.Flink; 2579 2580 while (le != &context->pending_rmdirs) { 2581 pending_rmdir* pr = CONTAINING_RECORD(le, pending_rmdir, list_entry); 2582 2583 if (pr->last_child_inode <= context->lastinode.inode) { 2584 le = le->Flink; 2585 2586 send_rmdir_command(context, pr->sd->namelen, pr->sd->name); 2587 2588 RemoveEntryList(&pr->sd->list_entry); 2589 2590 if (pr->sd->name) 2591 ExFreePool(pr->sd->name); 2592 2593 while (!IsListEmpty(&pr->sd->deleted_children)) { 2594 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&pr->sd->deleted_children), deleted_child, list_entry); 2595 ExFreePool(dc); 2596 } 2597 2598 ExFreePool(pr->sd); 2599 2600 RemoveEntryList(&pr->list_entry); 2601 ExFreePool(pr); 2602 } else 2603 break; 2604 } 2605 } 2606 2607 context->lastinode.inode = 0; 2608 context->lastinode.o = NULL; 2609 2610 if (context->lastinode.path) { 2611 ExFreePool(context->lastinode.path); 2612 context->lastinode.path = NULL; 2613 } 2614 2615 return STATUS_SUCCESS; 2616 } 2617 2618 static NTSTATUS send_extent_data(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 2619 NTSTATUS Status; 2620 2621 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size) 2622 return STATUS_SUCCESS; 2623 2624 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2625 Status = flush_refs(context, tp, tp2); 2626 if (!NT_SUCCESS(Status)) { 2627 ERR("flush_refs returned %08x\n", Status); 2628 return Status; 2629 } 2630 2631 if (context->send->cancelling) 2632 return STATUS_SUCCESS; 2633 } 2634 2635 if ((context->lastinode.mode & __S_IFLNK) == __S_IFLNK) 2636 return STATUS_SUCCESS; 2637 2638 if (tp) { 2639 EXTENT_DATA* ed; 2640 EXTENT_DATA2* ed2 = NULL; 2641 2642 if (tp->item->size < sizeof(EXTENT_DATA)) { 2643 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2644 tp->item->size, sizeof(EXTENT_DATA)); 2645 return STATUS_INTERNAL_ERROR; 2646 } 2647 2648 ed = (EXTENT_DATA*)tp->item->data; 2649 2650 if (ed->encryption != BTRFS_ENCRYPTION_NONE) { 2651 ERR("unknown encryption type %u\n", ed->encryption); 2652 return STATUS_INTERNAL_ERROR; 2653 } 2654 2655 if (ed->encoding != BTRFS_ENCODING_NONE) { 2656 ERR("unknown encoding type %u\n", ed->encoding); 2657 return STATUS_INTERNAL_ERROR; 2658 } 2659 2660 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && 2661 ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) { 2662 ERR("unknown compression type %u\n", ed->compression); 2663 return STATUS_INTERNAL_ERROR; 2664 } 2665 2666 if (ed->type == EXTENT_TYPE_REGULAR) { 2667 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) { 2668 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2669 tp->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 2670 return STATUS_INTERNAL_ERROR; 2671 } 2672 2673 ed2 = (EXTENT_DATA2*)ed->data; 2674 } else if (ed->type == EXTENT_TYPE_INLINE) { 2675 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size && ed->compression == BTRFS_COMPRESSION_NONE) { 2676 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2677 tp->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 2678 return STATUS_INTERNAL_ERROR; 2679 } 2680 } 2681 2682 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) { 2683 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp->item->size, ALLOC_TAG); 2684 2685 if (!se) { 2686 ERR("out of memory\n"); 2687 return STATUS_INSUFFICIENT_RESOURCES; 2688 } 2689 2690 se->offset = tp->item->key.offset; 2691 se->datalen = tp->item->size; 2692 RtlCopyMemory(&se->data, tp->item->data, tp->item->size); 2693 InsertTailList(&context->lastinode.exts, &se->list_entry); 2694 } 2695 } 2696 2697 if (tp2) { 2698 EXTENT_DATA* ed; 2699 EXTENT_DATA2* ed2 = NULL; 2700 2701 if (tp2->item->size < sizeof(EXTENT_DATA)) { 2702 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2703 tp2->item->size, sizeof(EXTENT_DATA)); 2704 return STATUS_INTERNAL_ERROR; 2705 } 2706 2707 ed = (EXTENT_DATA*)tp2->item->data; 2708 2709 if (ed->encryption != BTRFS_ENCRYPTION_NONE) { 2710 ERR("unknown encryption type %u\n", ed->encryption); 2711 return STATUS_INTERNAL_ERROR; 2712 } 2713 2714 if (ed->encoding != BTRFS_ENCODING_NONE) { 2715 ERR("unknown encoding type %u\n", ed->encoding); 2716 return STATUS_INTERNAL_ERROR; 2717 } 2718 2719 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && 2720 ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) { 2721 ERR("unknown compression type %u\n", ed->compression); 2722 return STATUS_INTERNAL_ERROR; 2723 } 2724 2725 if (ed->type == EXTENT_TYPE_REGULAR) { 2726 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) { 2727 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2728 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 2729 return STATUS_INTERNAL_ERROR; 2730 } 2731 2732 ed2 = (EXTENT_DATA2*)ed->data; 2733 } else if (ed->type == EXTENT_TYPE_INLINE) { 2734 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) { 2735 ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2736 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 2737 return STATUS_INTERNAL_ERROR; 2738 } 2739 } 2740 2741 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) { 2742 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp2->item->size, ALLOC_TAG); 2743 2744 if (!se) { 2745 ERR("out of memory\n"); 2746 return STATUS_INSUFFICIENT_RESOURCES; 2747 } 2748 2749 se->offset = tp2->item->key.offset; 2750 se->datalen = tp2->item->size; 2751 RtlCopyMemory(&se->data, tp2->item->data, tp2->item->size); 2752 InsertTailList(&context->lastinode.oldexts, &se->list_entry); 2753 } 2754 } 2755 2756 return STATUS_SUCCESS; 2757 } 2758 2759 typedef struct { 2760 uint16_t namelen; 2761 char* name; 2762 uint16_t value1len; 2763 char* value1; 2764 uint16_t value2len; 2765 char* value2; 2766 LIST_ENTRY list_entry; 2767 } xattr_cmp; 2768 2769 static NTSTATUS send_xattr(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 2770 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size) 2771 return STATUS_SUCCESS; 2772 2773 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2774 NTSTATUS Status = flush_refs(context, tp, tp2); 2775 if (!NT_SUCCESS(Status)) { 2776 ERR("flush_refs returned %08x\n", Status); 2777 return Status; 2778 } 2779 2780 if (context->send->cancelling) 2781 return STATUS_SUCCESS; 2782 } 2783 2784 if (tp && tp->item->size < sizeof(DIR_ITEM)) { 2785 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2786 tp->item->size, sizeof(DIR_ITEM)); 2787 return STATUS_INTERNAL_ERROR; 2788 } 2789 2790 if (tp2 && tp2->item->size < sizeof(DIR_ITEM)) { 2791 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2792 tp2->item->size, sizeof(DIR_ITEM)); 2793 return STATUS_INTERNAL_ERROR; 2794 } 2795 2796 if (tp && !tp2) { 2797 ULONG len; 2798 DIR_ITEM* di; 2799 2800 len = tp->item->size; 2801 di = (DIR_ITEM*)tp->item->data; 2802 2803 do { 2804 ULONG pos; 2805 2806 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2807 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 2808 return STATUS_INTERNAL_ERROR; 2809 } 2810 2811 pos = context->datalen; 2812 send_command(context, BTRFS_SEND_CMD_SET_XATTR); 2813 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2814 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n); 2815 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, &di->name[di->n], di->m); 2816 send_command_finish(context, pos); 2817 2818 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2819 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2820 } while (len > 0); 2821 } else if (!tp && tp2) { 2822 ULONG len; 2823 DIR_ITEM* di; 2824 2825 len = tp2->item->size; 2826 di = (DIR_ITEM*)tp2->item->data; 2827 2828 do { 2829 ULONG pos; 2830 2831 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2832 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset); 2833 return STATUS_INTERNAL_ERROR; 2834 } 2835 2836 pos = context->datalen; 2837 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR); 2838 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2839 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n); 2840 send_command_finish(context, pos); 2841 2842 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2843 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2844 } while (len > 0); 2845 } else { 2846 ULONG len; 2847 DIR_ITEM* di; 2848 LIST_ENTRY xattrs; 2849 2850 InitializeListHead(&xattrs); 2851 2852 len = tp->item->size; 2853 di = (DIR_ITEM*)tp->item->data; 2854 2855 do { 2856 xattr_cmp* xa; 2857 2858 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2859 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 2860 return STATUS_INTERNAL_ERROR; 2861 } 2862 2863 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG); 2864 if (!xa) { 2865 ERR("out of memory\n"); 2866 2867 while (!IsListEmpty(&xattrs)) { 2868 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry)); 2869 } 2870 2871 return STATUS_INSUFFICIENT_RESOURCES; 2872 } 2873 2874 xa->namelen = di->n; 2875 xa->name = di->name; 2876 xa->value1len = di->m; 2877 xa->value1 = di->name + di->n; 2878 xa->value2len = 0; 2879 xa->value2 = NULL; 2880 2881 InsertTailList(&xattrs, &xa->list_entry); 2882 2883 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2884 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2885 } while (len > 0); 2886 2887 len = tp2->item->size; 2888 di = (DIR_ITEM*)tp2->item->data; 2889 2890 do { 2891 xattr_cmp* xa; 2892 LIST_ENTRY* le; 2893 bool found = false; 2894 2895 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2896 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset); 2897 return STATUS_INTERNAL_ERROR; 2898 } 2899 2900 le = xattrs.Flink; 2901 while (le != &xattrs) { 2902 xa = CONTAINING_RECORD(le, xattr_cmp, list_entry); 2903 2904 if (xa->namelen == di->n && RtlCompareMemory(xa->name, di->name, di->n) == di->n) { 2905 xa->value2len = di->m; 2906 xa->value2 = di->name + di->n; 2907 found = true; 2908 break; 2909 } 2910 2911 le = le->Flink; 2912 } 2913 2914 if (!found) { 2915 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG); 2916 if (!xa) { 2917 ERR("out of memory\n"); 2918 2919 while (!IsListEmpty(&xattrs)) { 2920 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry)); 2921 } 2922 2923 return STATUS_INSUFFICIENT_RESOURCES; 2924 } 2925 2926 xa->namelen = di->n; 2927 xa->name = di->name; 2928 xa->value1len = 0; 2929 xa->value1 = NULL; 2930 xa->value2len = di->m; 2931 xa->value2 = di->name + di->n; 2932 2933 InsertTailList(&xattrs, &xa->list_entry); 2934 } 2935 2936 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2937 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2938 } while (len > 0); 2939 2940 while (!IsListEmpty(&xattrs)) { 2941 xattr_cmp* xa = CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry); 2942 2943 if (xa->value1len != xa->value2len || !xa->value1 || !xa->value2 || RtlCompareMemory(xa->value1, xa->value2, xa->value1len) != xa->value1len) { 2944 ULONG pos; 2945 2946 if (!xa->value1) { 2947 pos = context->datalen; 2948 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR); 2949 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2950 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen); 2951 send_command_finish(context, pos); 2952 } else { 2953 pos = context->datalen; 2954 send_command(context, BTRFS_SEND_CMD_SET_XATTR); 2955 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2956 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen); 2957 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, xa->value1, xa->value1len); 2958 send_command_finish(context, pos); 2959 } 2960 } 2961 2962 ExFreePool(xa); 2963 } 2964 } 2965 2966 return STATUS_SUCCESS; 2967 } 2968 2969 _Function_class_(KSTART_ROUTINE) 2970 static void __stdcall send_thread(void* ctx) { 2971 send_context* context = (send_context*)ctx; 2972 NTSTATUS Status; 2973 KEY searchkey; 2974 traverse_ptr tp, tp2; 2975 2976 InterlockedIncrement(&context->root->send_ops); 2977 2978 if (context->parent) 2979 InterlockedIncrement(&context->parent->send_ops); 2980 2981 if (context->clones) { 2982 ULONG i; 2983 2984 for (i = 0; i < context->num_clones; i++) { 2985 InterlockedIncrement(&context->clones[i]->send_ops); 2986 } 2987 } 2988 2989 ExAcquireResourceExclusiveLite(&context->Vcb->tree_lock, true); 2990 2991 flush_subvol_fcbs(context->root); 2992 2993 if (context->parent) 2994 flush_subvol_fcbs(context->parent); 2995 2996 if (context->Vcb->need_write) 2997 Status = do_write(context->Vcb, NULL); 2998 else 2999 Status = STATUS_SUCCESS; 3000 3001 free_trees(context->Vcb); 3002 3003 if (!NT_SUCCESS(Status)) { 3004 ERR("do_write returned %08x\n", Status); 3005 ExReleaseResourceLite(&context->Vcb->tree_lock); 3006 goto end; 3007 } 3008 3009 ExConvertExclusiveToSharedLite(&context->Vcb->tree_lock); 3010 3011 searchkey.obj_id = searchkey.offset = 0; 3012 searchkey.obj_type = 0; 3013 3014 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 3015 if (!NT_SUCCESS(Status)) { 3016 ERR("find_item returned %08x\n", Status); 3017 ExReleaseResourceLite(&context->Vcb->tree_lock); 3018 goto end; 3019 } 3020 3021 if (context->parent) { 3022 bool ended1 = false, ended2 = false; 3023 Status = find_item(context->Vcb, context->parent, &tp2, &searchkey, false, NULL); 3024 if (!NT_SUCCESS(Status)) { 3025 ERR("find_item returned %08x\n", Status); 3026 ExReleaseResourceLite(&context->Vcb->tree_lock); 3027 goto end; 3028 } 3029 3030 do { 3031 traverse_ptr next_tp; 3032 3033 if (context->datalen > SEND_BUFFER_LENGTH) { 3034 KEY key1 = tp.item->key, key2 = tp2.item->key; 3035 3036 ExReleaseResourceLite(&context->Vcb->tree_lock); 3037 3038 KeClearEvent(&context->send->cleared_event); 3039 KeSetEvent(&context->buffer_event, 0, true); 3040 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3041 3042 if (context->send->cancelling) 3043 goto end; 3044 3045 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 3046 3047 if (!ended1) { 3048 Status = find_item(context->Vcb, context->root, &tp, &key1, false, NULL); 3049 if (!NT_SUCCESS(Status)) { 3050 ERR("find_item returned %08x\n", Status); 3051 ExReleaseResourceLite(&context->Vcb->tree_lock); 3052 goto end; 3053 } 3054 3055 if (keycmp(tp.item->key, key1)) { 3056 ERR("readonly subvolume changed\n"); 3057 ExReleaseResourceLite(&context->Vcb->tree_lock); 3058 Status = STATUS_INTERNAL_ERROR; 3059 goto end; 3060 } 3061 } 3062 3063 if (!ended2) { 3064 Status = find_item(context->Vcb, context->parent, &tp2, &key2, false, NULL); 3065 if (!NT_SUCCESS(Status)) { 3066 ERR("find_item returned %08x\n", Status); 3067 ExReleaseResourceLite(&context->Vcb->tree_lock); 3068 goto end; 3069 } 3070 3071 if (keycmp(tp2.item->key, key2)) { 3072 ERR("readonly subvolume changed\n"); 3073 ExReleaseResourceLite(&context->Vcb->tree_lock); 3074 Status = STATUS_INTERNAL_ERROR; 3075 goto end; 3076 } 3077 } 3078 } 3079 3080 while (!ended1 && !ended2 && tp.tree->header.address == tp2.tree->header.address) { 3081 Status = skip_to_difference(context->Vcb, &tp, &tp2, &ended1, &ended2); 3082 if (!NT_SUCCESS(Status)) { 3083 ERR("skip_to_difference returned %08x\n", Status); 3084 ExReleaseResourceLite(&context->Vcb->tree_lock); 3085 goto end; 3086 } 3087 } 3088 3089 if (!ended1 && !ended2 && !keycmp(tp.item->key, tp2.item->key)) { 3090 bool no_next = false, no_next2 = false; 3091 3092 TRACE("~ %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 3093 3094 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3095 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3096 if (!NT_SUCCESS(Status)) { 3097 ERR("finish_inode returned %08x\n", Status); 3098 ExReleaseResourceLite(&context->Vcb->tree_lock); 3099 goto end; 3100 } 3101 3102 if (context->send->cancelling) { 3103 ExReleaseResourceLite(&context->Vcb->tree_lock); 3104 goto end; 3105 } 3106 } 3107 3108 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3109 if (tp.item->size == tp2.item->size && tp.item->size > 0 && RtlCompareMemory(tp.item->data, tp2.item->data, tp.item->size) == tp.item->size) { 3110 uint64_t inode = tp.item->key.obj_id; 3111 3112 while (true) { 3113 if (!find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) { 3114 ended1 = true; 3115 break; 3116 } 3117 3118 tp = next_tp; 3119 3120 if (tp.item->key.obj_id != inode) 3121 break; 3122 } 3123 3124 while (true) { 3125 if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) { 3126 ended2 = true; 3127 break; 3128 } 3129 3130 tp2 = next_tp; 3131 3132 if (tp2.item->key.obj_id != inode) 3133 break; 3134 } 3135 3136 no_next = true; 3137 } else if (tp.item->size > sizeof(uint64_t) && tp2.item->size > sizeof(uint64_t) && *(uint64_t*)tp.item->data != *(uint64_t*)tp2.item->data) { 3138 uint64_t inode = tp.item->key.obj_id; 3139 3140 Status = send_inode(context, NULL, &tp2); 3141 if (!NT_SUCCESS(Status)) { 3142 ERR("send_inode returned %08x\n", Status); 3143 ExReleaseResourceLite(&context->Vcb->tree_lock); 3144 goto end; 3145 } 3146 3147 while (true) { 3148 if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) { 3149 ended2 = true; 3150 break; 3151 } 3152 3153 tp2 = next_tp; 3154 3155 if (tp2.item->key.obj_id != inode) 3156 break; 3157 3158 if (tp2.item->key.obj_type == TYPE_INODE_REF) { 3159 Status = send_inode_ref(context, &tp2, true); 3160 if (!NT_SUCCESS(Status)) { 3161 ERR("send_inode_ref returned %08x\n", Status); 3162 ExReleaseResourceLite(&context->Vcb->tree_lock); 3163 goto end; 3164 } 3165 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) { 3166 Status = send_inode_extref(context, &tp2, true); 3167 if (!NT_SUCCESS(Status)) { 3168 ERR("send_inode_extref returned %08x\n", Status); 3169 ExReleaseResourceLite(&context->Vcb->tree_lock); 3170 goto end; 3171 } 3172 } 3173 } 3174 3175 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3176 if (!NT_SUCCESS(Status)) { 3177 ERR("finish_inode returned %08x\n", Status); 3178 ExReleaseResourceLite(&context->Vcb->tree_lock); 3179 goto end; 3180 } 3181 3182 if (context->send->cancelling) { 3183 ExReleaseResourceLite(&context->Vcb->tree_lock); 3184 goto end; 3185 } 3186 3187 no_next2 = true; 3188 3189 Status = send_inode(context, &tp, NULL); 3190 if (!NT_SUCCESS(Status)) { 3191 ERR("send_inode returned %08x\n", Status); 3192 ExReleaseResourceLite(&context->Vcb->tree_lock); 3193 goto end; 3194 } 3195 } else { 3196 Status = send_inode(context, &tp, &tp2); 3197 if (!NT_SUCCESS(Status)) { 3198 ERR("send_inode returned %08x\n", Status); 3199 ExReleaseResourceLite(&context->Vcb->tree_lock); 3200 goto end; 3201 } 3202 } 3203 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3204 Status = send_inode_ref(context, &tp, false); 3205 if (!NT_SUCCESS(Status)) { 3206 ERR("send_inode_ref returned %08x\n", Status); 3207 ExReleaseResourceLite(&context->Vcb->tree_lock); 3208 goto end; 3209 } 3210 3211 Status = send_inode_ref(context, &tp2, true); 3212 if (!NT_SUCCESS(Status)) { 3213 ERR("send_inode_ref returned %08x\n", Status); 3214 ExReleaseResourceLite(&context->Vcb->tree_lock); 3215 goto end; 3216 } 3217 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3218 Status = send_inode_extref(context, &tp, false); 3219 if (!NT_SUCCESS(Status)) { 3220 ERR("send_inode_extref returned %08x\n", Status); 3221 ExReleaseResourceLite(&context->Vcb->tree_lock); 3222 goto end; 3223 } 3224 3225 Status = send_inode_extref(context, &tp2, true); 3226 if (!NT_SUCCESS(Status)) { 3227 ERR("send_inode_extref returned %08x\n", Status); 3228 ExReleaseResourceLite(&context->Vcb->tree_lock); 3229 goto end; 3230 } 3231 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3232 Status = send_extent_data(context, &tp, &tp2); 3233 if (!NT_SUCCESS(Status)) { 3234 ERR("send_extent_data returned %08x\n", Status); 3235 ExReleaseResourceLite(&context->Vcb->tree_lock); 3236 goto end; 3237 } 3238 3239 if (context->send->cancelling) { 3240 ExReleaseResourceLite(&context->Vcb->tree_lock); 3241 goto end; 3242 } 3243 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3244 Status = send_xattr(context, &tp, &tp2); 3245 if (!NT_SUCCESS(Status)) { 3246 ERR("send_xattr returned %08x\n", Status); 3247 ExReleaseResourceLite(&context->Vcb->tree_lock); 3248 goto end; 3249 } 3250 3251 if (context->send->cancelling) { 3252 ExReleaseResourceLite(&context->Vcb->tree_lock); 3253 goto end; 3254 } 3255 } 3256 3257 if (!no_next) { 3258 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3259 tp = next_tp; 3260 else 3261 ended1 = true; 3262 3263 if (!no_next2) { 3264 if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) 3265 tp2 = next_tp; 3266 else 3267 ended2 = true; 3268 } 3269 } 3270 } else if (ended2 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == -1)) { 3271 TRACE("A %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 3272 3273 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3274 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3275 if (!NT_SUCCESS(Status)) { 3276 ERR("finish_inode returned %08x\n", Status); 3277 ExReleaseResourceLite(&context->Vcb->tree_lock); 3278 goto end; 3279 } 3280 3281 if (context->send->cancelling) { 3282 ExReleaseResourceLite(&context->Vcb->tree_lock); 3283 goto end; 3284 } 3285 } 3286 3287 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3288 Status = send_inode(context, &tp, NULL); 3289 if (!NT_SUCCESS(Status)) { 3290 ERR("send_inode returned %08x\n", Status); 3291 ExReleaseResourceLite(&context->Vcb->tree_lock); 3292 goto end; 3293 } 3294 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3295 Status = send_inode_ref(context, &tp, false); 3296 if (!NT_SUCCESS(Status)) { 3297 ERR("send_inode_ref returned %08x\n", Status); 3298 ExReleaseResourceLite(&context->Vcb->tree_lock); 3299 goto end; 3300 } 3301 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3302 Status = send_inode_extref(context, &tp, false); 3303 if (!NT_SUCCESS(Status)) { 3304 ERR("send_inode_extref returned %08x\n", Status); 3305 ExReleaseResourceLite(&context->Vcb->tree_lock); 3306 goto end; 3307 } 3308 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3309 Status = send_extent_data(context, &tp, NULL); 3310 if (!NT_SUCCESS(Status)) { 3311 ERR("send_extent_data returned %08x\n", Status); 3312 ExReleaseResourceLite(&context->Vcb->tree_lock); 3313 goto end; 3314 } 3315 3316 if (context->send->cancelling) { 3317 ExReleaseResourceLite(&context->Vcb->tree_lock); 3318 goto end; 3319 } 3320 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3321 Status = send_xattr(context, &tp, NULL); 3322 if (!NT_SUCCESS(Status)) { 3323 ERR("send_xattr returned %08x\n", Status); 3324 ExReleaseResourceLite(&context->Vcb->tree_lock); 3325 goto end; 3326 } 3327 3328 if (context->send->cancelling) { 3329 ExReleaseResourceLite(&context->Vcb->tree_lock); 3330 goto end; 3331 } 3332 } 3333 3334 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3335 tp = next_tp; 3336 else 3337 ended1 = true; 3338 } else if (ended1 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == 1)) { 3339 TRACE("B %I64x,%x,%I64x\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset); 3340 3341 if (context->lastinode.inode != 0 && tp2.item->key.obj_id > context->lastinode.inode) { 3342 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3343 if (!NT_SUCCESS(Status)) { 3344 ERR("finish_inode returned %08x\n", Status); 3345 ExReleaseResourceLite(&context->Vcb->tree_lock); 3346 goto end; 3347 } 3348 3349 if (context->send->cancelling) { 3350 ExReleaseResourceLite(&context->Vcb->tree_lock); 3351 goto end; 3352 } 3353 } 3354 3355 if (tp2.item->key.obj_type == TYPE_INODE_ITEM) { 3356 Status = send_inode(context, NULL, &tp2); 3357 if (!NT_SUCCESS(Status)) { 3358 ERR("send_inode returned %08x\n", Status); 3359 ExReleaseResourceLite(&context->Vcb->tree_lock); 3360 goto end; 3361 } 3362 } else if (tp2.item->key.obj_type == TYPE_INODE_REF) { 3363 Status = send_inode_ref(context, &tp2, true); 3364 if (!NT_SUCCESS(Status)) { 3365 ERR("send_inode_ref returned %08x\n", Status); 3366 ExReleaseResourceLite(&context->Vcb->tree_lock); 3367 goto end; 3368 } 3369 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) { 3370 Status = send_inode_extref(context, &tp2, true); 3371 if (!NT_SUCCESS(Status)) { 3372 ERR("send_inode_extref returned %08x\n", Status); 3373 ExReleaseResourceLite(&context->Vcb->tree_lock); 3374 goto end; 3375 } 3376 } else if (tp2.item->key.obj_type == TYPE_EXTENT_DATA && !context->lastinode.deleting) { 3377 Status = send_extent_data(context, NULL, &tp2); 3378 if (!NT_SUCCESS(Status)) { 3379 ERR("send_extent_data returned %08x\n", Status); 3380 ExReleaseResourceLite(&context->Vcb->tree_lock); 3381 goto end; 3382 } 3383 3384 if (context->send->cancelling) { 3385 ExReleaseResourceLite(&context->Vcb->tree_lock); 3386 goto end; 3387 } 3388 } else if (tp2.item->key.obj_type == TYPE_XATTR_ITEM && !context->lastinode.deleting) { 3389 Status = send_xattr(context, NULL, &tp2); 3390 if (!NT_SUCCESS(Status)) { 3391 ERR("send_xattr returned %08x\n", Status); 3392 ExReleaseResourceLite(&context->Vcb->tree_lock); 3393 goto end; 3394 } 3395 3396 if (context->send->cancelling) { 3397 ExReleaseResourceLite(&context->Vcb->tree_lock); 3398 goto end; 3399 } 3400 } 3401 3402 if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) 3403 tp2 = next_tp; 3404 else 3405 ended2 = true; 3406 } 3407 } while (!ended1 || !ended2); 3408 } else { 3409 do { 3410 traverse_ptr next_tp; 3411 3412 if (context->datalen > SEND_BUFFER_LENGTH) { 3413 KEY key = tp.item->key; 3414 3415 ExReleaseResourceLite(&context->Vcb->tree_lock); 3416 3417 KeClearEvent(&context->send->cleared_event); 3418 KeSetEvent(&context->buffer_event, 0, true); 3419 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3420 3421 if (context->send->cancelling) 3422 goto end; 3423 3424 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 3425 3426 Status = find_item(context->Vcb, context->root, &tp, &key, false, NULL); 3427 if (!NT_SUCCESS(Status)) { 3428 ERR("find_item returned %08x\n", Status); 3429 ExReleaseResourceLite(&context->Vcb->tree_lock); 3430 goto end; 3431 } 3432 3433 if (keycmp(tp.item->key, key)) { 3434 ERR("readonly subvolume changed\n"); 3435 ExReleaseResourceLite(&context->Vcb->tree_lock); 3436 Status = STATUS_INTERNAL_ERROR; 3437 goto end; 3438 } 3439 } 3440 3441 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3442 Status = finish_inode(context, &tp, NULL); 3443 if (!NT_SUCCESS(Status)) { 3444 ERR("finish_inode returned %08x\n", Status); 3445 ExReleaseResourceLite(&context->Vcb->tree_lock); 3446 goto end; 3447 } 3448 3449 if (context->send->cancelling) { 3450 ExReleaseResourceLite(&context->Vcb->tree_lock); 3451 goto end; 3452 } 3453 } 3454 3455 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3456 Status = send_inode(context, &tp, NULL); 3457 if (!NT_SUCCESS(Status)) { 3458 ERR("send_inode returned %08x\n", Status); 3459 ExReleaseResourceLite(&context->Vcb->tree_lock); 3460 goto end; 3461 } 3462 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3463 Status = send_inode_ref(context, &tp, false); 3464 if (!NT_SUCCESS(Status)) { 3465 ERR("send_inode_ref returned %08x\n", Status); 3466 ExReleaseResourceLite(&context->Vcb->tree_lock); 3467 goto end; 3468 } 3469 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3470 Status = send_inode_extref(context, &tp, false); 3471 if (!NT_SUCCESS(Status)) { 3472 ERR("send_inode_extref returned %08x\n", Status); 3473 ExReleaseResourceLite(&context->Vcb->tree_lock); 3474 goto end; 3475 } 3476 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3477 Status = send_extent_data(context, &tp, NULL); 3478 if (!NT_SUCCESS(Status)) { 3479 ERR("send_extent_data returned %08x\n", Status); 3480 ExReleaseResourceLite(&context->Vcb->tree_lock); 3481 goto end; 3482 } 3483 3484 if (context->send->cancelling) { 3485 ExReleaseResourceLite(&context->Vcb->tree_lock); 3486 goto end; 3487 } 3488 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3489 Status = send_xattr(context, &tp, NULL); 3490 if (!NT_SUCCESS(Status)) { 3491 ERR("send_xattr returned %08x\n", Status); 3492 ExReleaseResourceLite(&context->Vcb->tree_lock); 3493 goto end; 3494 } 3495 3496 if (context->send->cancelling) { 3497 ExReleaseResourceLite(&context->Vcb->tree_lock); 3498 goto end; 3499 } 3500 } 3501 3502 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3503 tp = next_tp; 3504 else 3505 break; 3506 } while (true); 3507 } 3508 3509 if (context->lastinode.inode != 0) { 3510 Status = finish_inode(context, NULL, NULL); 3511 if (!NT_SUCCESS(Status)) { 3512 ERR("finish_inode returned %08x\n", Status); 3513 ExReleaseResourceLite(&context->Vcb->tree_lock); 3514 goto end; 3515 } 3516 3517 ExReleaseResourceLite(&context->Vcb->tree_lock); 3518 3519 if (context->send->cancelling) 3520 goto end; 3521 } else 3522 ExReleaseResourceLite(&context->Vcb->tree_lock); 3523 3524 KeClearEvent(&context->send->cleared_event); 3525 KeSetEvent(&context->buffer_event, 0, true); 3526 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3527 3528 Status = STATUS_SUCCESS; 3529 3530 end: 3531 if (!NT_SUCCESS(Status)) { 3532 KeSetEvent(&context->buffer_event, 0, false); 3533 3534 if (context->send->ccb) 3535 context->send->ccb->send_status = Status; 3536 } 3537 3538 ExAcquireResourceExclusiveLite(&context->Vcb->send_load_lock, true); 3539 3540 while (!IsListEmpty(&context->orphans)) { 3541 orphan* o = CONTAINING_RECORD(RemoveHeadList(&context->orphans), orphan, list_entry); 3542 ExFreePool(o); 3543 } 3544 3545 while (!IsListEmpty(&context->dirs)) { 3546 send_dir* sd = CONTAINING_RECORD(RemoveHeadList(&context->dirs), send_dir, list_entry); 3547 3548 if (sd->name) 3549 ExFreePool(sd->name); 3550 3551 while (!IsListEmpty(&sd->deleted_children)) { 3552 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&sd->deleted_children), deleted_child, list_entry); 3553 ExFreePool(dc); 3554 } 3555 3556 ExFreePool(sd); 3557 } 3558 3559 ZwClose(context->send->thread); 3560 context->send->thread = NULL; 3561 3562 if (context->send->ccb) 3563 context->send->ccb->send = NULL; 3564 3565 RemoveEntryList(&context->send->list_entry); 3566 ExFreePool(context->send); 3567 ExFreePool(context->data); 3568 3569 InterlockedDecrement(&context->Vcb->running_sends); 3570 InterlockedDecrement(&context->root->send_ops); 3571 3572 if (context->parent) 3573 InterlockedDecrement(&context->parent->send_ops); 3574 3575 ExReleaseResourceLite(&context->Vcb->send_load_lock); 3576 3577 if (context->clones) { 3578 ULONG i; 3579 3580 for (i = 0; i < context->num_clones; i++) { 3581 InterlockedDecrement(&context->clones[i]->send_ops); 3582 } 3583 3584 ExFreePool(context->clones); 3585 } 3586 3587 ExFreePool(context); 3588 3589 PsTerminateSystemThread(STATUS_SUCCESS); 3590 } 3591 3592 NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJECT FileObject, PIRP Irp) { 3593 NTSTATUS Status; 3594 fcb* fcb; 3595 ccb* ccb; 3596 root* parsubvol = NULL; 3597 send_context* context; 3598 send_info* send; 3599 ULONG num_clones = 0; 3600 root** clones = NULL; 3601 OBJECT_ATTRIBUTES oa; 3602 3603 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 3604 return STATUS_INVALID_PARAMETER; 3605 3606 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 3607 return STATUS_PRIVILEGE_NOT_HELD; 3608 3609 fcb = FileObject->FsContext; 3610 ccb = FileObject->FsContext2; 3611 3612 if (fcb->inode != SUBVOL_ROOT_INODE || fcb == Vcb->root_fileref->fcb) 3613 return STATUS_INVALID_PARAMETER; 3614 3615 if (!Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) 3616 return STATUS_INVALID_PARAMETER; 3617 3618 if (data) { 3619 btrfs_send_subvol* bss = (btrfs_send_subvol*)data; 3620 HANDLE parent; 3621 3622 #if defined(_WIN64) 3623 if (IoIs32bitProcess(Irp)) { 3624 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data; 3625 3626 if (datalen < offsetof(btrfs_send_subvol32, num_clones)) 3627 return STATUS_INVALID_PARAMETER; 3628 3629 parent = Handle32ToHandle(bss32->parent); 3630 3631 if (datalen >= offsetof(btrfs_send_subvol32, clones[0])) 3632 num_clones = bss32->num_clones; 3633 3634 if (datalen < offsetof(btrfs_send_subvol32, clones[0]) + (num_clones * sizeof(uint32_t))) 3635 return STATUS_INVALID_PARAMETER; 3636 } else { 3637 #endif 3638 if (datalen < offsetof(btrfs_send_subvol, num_clones)) 3639 return STATUS_INVALID_PARAMETER; 3640 3641 parent = bss->parent; 3642 3643 if (datalen >= offsetof(btrfs_send_subvol, clones[0])) 3644 num_clones = bss->num_clones; 3645 3646 if (datalen < offsetof(btrfs_send_subvol, clones[0]) + (num_clones * sizeof(HANDLE))) 3647 return STATUS_INVALID_PARAMETER; 3648 #if defined(_WIN64) 3649 } 3650 #endif 3651 3652 if (parent) { 3653 PFILE_OBJECT fileobj; 3654 struct _fcb* parfcb; 3655 3656 Status = ObReferenceObjectByHandle(parent, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 3657 if (!NT_SUCCESS(Status)) { 3658 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 3659 return Status; 3660 } 3661 3662 if (fileobj->DeviceObject != FileObject->DeviceObject) { 3663 ObDereferenceObject(fileobj); 3664 return STATUS_INVALID_PARAMETER; 3665 } 3666 3667 parfcb = fileobj->FsContext; 3668 3669 if (!parfcb || parfcb == Vcb->root_fileref->fcb || parfcb == Vcb->volume_fcb || parfcb->inode != SUBVOL_ROOT_INODE) { 3670 ObDereferenceObject(fileobj); 3671 return STATUS_INVALID_PARAMETER; 3672 } 3673 3674 parsubvol = parfcb->subvol; 3675 ObDereferenceObject(fileobj); 3676 3677 if (!Vcb->readonly && !(parsubvol->root_item.flags & BTRFS_SUBVOL_READONLY)) 3678 return STATUS_INVALID_PARAMETER; 3679 3680 if (parsubvol == fcb->subvol) 3681 return STATUS_INVALID_PARAMETER; 3682 } 3683 3684 if (num_clones > 0) { 3685 ULONG i; 3686 3687 clones = ExAllocatePoolWithTag(PagedPool, sizeof(root*) * num_clones, ALLOC_TAG); 3688 if (!clones) { 3689 ERR("out of memory\n"); 3690 return STATUS_INSUFFICIENT_RESOURCES; 3691 } 3692 3693 for (i = 0; i < num_clones; i++) { 3694 HANDLE h; 3695 PFILE_OBJECT fileobj; 3696 struct _fcb* clonefcb; 3697 3698 #if defined(_WIN64) 3699 if (IoIs32bitProcess(Irp)) { 3700 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data; 3701 3702 h = Handle32ToHandle(bss32->clones[i]); 3703 } else 3704 #endif 3705 h = bss->clones[i]; 3706 3707 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 3708 if (!NT_SUCCESS(Status)) { 3709 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 3710 ExFreePool(clones); 3711 return Status; 3712 } 3713 3714 if (fileobj->DeviceObject != FileObject->DeviceObject) { 3715 ObDereferenceObject(fileobj); 3716 ExFreePool(clones); 3717 return STATUS_INVALID_PARAMETER; 3718 } 3719 3720 clonefcb = fileobj->FsContext; 3721 3722 if (!clonefcb || clonefcb == Vcb->root_fileref->fcb || clonefcb == Vcb->volume_fcb || clonefcb->inode != SUBVOL_ROOT_INODE) { 3723 ObDereferenceObject(fileobj); 3724 ExFreePool(clones); 3725 return STATUS_INVALID_PARAMETER; 3726 } 3727 3728 clones[i] = clonefcb->subvol; 3729 ObDereferenceObject(fileobj); 3730 3731 if (!Vcb->readonly && !(clones[i]->root_item.flags & BTRFS_SUBVOL_READONLY)) { 3732 ExFreePool(clones); 3733 return STATUS_INVALID_PARAMETER; 3734 } 3735 } 3736 } 3737 } 3738 3739 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true); 3740 3741 if (ccb->send) { 3742 WARN("send operation already running\n"); 3743 ExReleaseResourceLite(&Vcb->send_load_lock); 3744 return STATUS_DEVICE_NOT_READY; 3745 } 3746 3747 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_context), ALLOC_TAG); 3748 if (!context) { 3749 ERR("out of memory\n"); 3750 3751 if (clones) 3752 ExFreePool(clones); 3753 3754 ExReleaseResourceLite(&Vcb->send_load_lock); 3755 return STATUS_INSUFFICIENT_RESOURCES; 3756 } 3757 3758 context->Vcb = Vcb; 3759 context->root = fcb->subvol; 3760 context->parent = parsubvol; 3761 InitializeListHead(&context->orphans); 3762 InitializeListHead(&context->dirs); 3763 InitializeListHead(&context->pending_rmdirs); 3764 context->lastinode.inode = 0; 3765 context->lastinode.path = NULL; 3766 context->lastinode.sd = NULL; 3767 context->root_dir = NULL; 3768 context->num_clones = num_clones; 3769 context->clones = clones; 3770 InitializeListHead(&context->lastinode.refs); 3771 InitializeListHead(&context->lastinode.oldrefs); 3772 InitializeListHead(&context->lastinode.exts); 3773 InitializeListHead(&context->lastinode.oldexts); 3774 3775 context->data = ExAllocatePoolWithTag(PagedPool, SEND_BUFFER_LENGTH + (2 * MAX_SEND_WRITE), ALLOC_TAG); // give ourselves some wiggle room 3776 if (!context->data) { 3777 ExFreePool(context); 3778 ExReleaseResourceLite(&Vcb->send_load_lock); 3779 return STATUS_INSUFFICIENT_RESOURCES; 3780 } 3781 3782 context->datalen = 0; 3783 3784 send_subvol_header(context, fcb->subvol, ccb->fileref); // FIXME - fileref needs some sort of lock here 3785 3786 KeInitializeEvent(&context->buffer_event, NotificationEvent, false); 3787 3788 send = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_info), ALLOC_TAG); 3789 if (!send) { 3790 ERR("out of memory\n"); 3791 ExFreePool(context->data); 3792 ExFreePool(context); 3793 3794 if (clones) 3795 ExFreePool(clones); 3796 3797 ExReleaseResourceLite(&Vcb->send_load_lock); 3798 return STATUS_INSUFFICIENT_RESOURCES; 3799 } 3800 3801 KeInitializeEvent(&send->cleared_event, NotificationEvent, false); 3802 3803 send->context = context; 3804 context->send = send; 3805 3806 ccb->send = send; 3807 send->ccb = ccb; 3808 ccb->send_status = STATUS_SUCCESS; 3809 3810 send->cancelling = false; 3811 3812 InterlockedIncrement(&Vcb->running_sends); 3813 3814 InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); 3815 3816 Status = PsCreateSystemThread(&send->thread, 0, &oa, NULL, NULL, send_thread, context); 3817 if (!NT_SUCCESS(Status)) { 3818 ERR("PsCreateSystemThread returned %08x\n", Status); 3819 ccb->send = NULL; 3820 InterlockedDecrement(&Vcb->running_sends); 3821 ExFreePool(send); 3822 ExFreePool(context->data); 3823 ExFreePool(context); 3824 3825 if (clones) 3826 ExFreePool(clones); 3827 3828 ExReleaseResourceLite(&Vcb->send_load_lock); 3829 return Status; 3830 } 3831 3832 InsertTailList(&Vcb->send_ops, &send->list_entry); 3833 ExReleaseResourceLite(&Vcb->send_load_lock); 3834 3835 return STATUS_SUCCESS; 3836 } 3837 3838 NTSTATUS read_send_buffer(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, ULONG_PTR* retlen, KPROCESSOR_MODE processor_mode) { 3839 ccb* ccb; 3840 send_context* context; 3841 3842 ccb = FileObject ? FileObject->FsContext2 : NULL; 3843 if (!ccb) 3844 return STATUS_INVALID_PARAMETER; 3845 3846 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode)) 3847 return STATUS_PRIVILEGE_NOT_HELD; 3848 3849 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true); 3850 3851 if (!ccb->send) { 3852 ExReleaseResourceLite(&Vcb->send_load_lock); 3853 return !NT_SUCCESS(ccb->send_status) ? ccb->send_status : STATUS_END_OF_FILE; 3854 } 3855 3856 context = (send_context*)ccb->send->context; 3857 3858 KeWaitForSingleObject(&context->buffer_event, Executive, KernelMode, false, NULL); 3859 3860 if (datalen == 0) { 3861 ExReleaseResourceLite(&Vcb->send_load_lock); 3862 return STATUS_SUCCESS; 3863 } 3864 3865 RtlCopyMemory(data, context->data, min(datalen, context->datalen)); 3866 3867 if (datalen < context->datalen) { // not empty yet 3868 *retlen = datalen; 3869 RtlMoveMemory(context->data, &context->data[datalen], context->datalen - datalen); 3870 context->datalen -= datalen; 3871 ExReleaseResourceLite(&Vcb->send_load_lock); 3872 } else { 3873 *retlen = context->datalen; 3874 context->datalen = 0; 3875 ExReleaseResourceLite(&Vcb->send_load_lock); 3876 3877 KeClearEvent(&context->buffer_event); 3878 KeSetEvent(&ccb->send->cleared_event, 0, false); 3879 } 3880 3881 return STATUS_SUCCESS; 3882 } 3883