ReactOS 0.4.16-dev-61-ge128cbc
send.c
Go to the documentation of this file.
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#include "crc32c.h"
20
21typedef struct send_dir {
24 bool dummy;
30 char* name;
33
34typedef struct {
37 bool dir;
39 char tmpname[64];
40} orphan;
41
42typedef struct {
45 char name[1];
47
48typedef struct {
52 char name[1];
53} ref;
54
55typedef struct {
60
61typedef struct {
66} send_ext;
67
68typedef struct {
82
83 struct {
86 bool new;
99 bool file;
100 char* path;
109
110#define MAX_SEND_WRITE 0xc000 // 48 KB
111#define SEND_BUFFER_LENGTH 0x100000 // 1 MB
112
115
118
119 bsc->cmd = cmd;
120 bsc->csum = 0;
121
122 context->datalen += sizeof(btrfs_send_command);
123}
124
127
128 bsc->length = context->datalen - pos - sizeof(btrfs_send_command);
129 bsc->csum = calc_crc32c(0, (uint8_t*)bsc, context->datalen - pos);
130}
131
133 btrfs_send_tlv* tlv = (btrfs_send_tlv*)&context->data[context->datalen];
134
135 tlv->type = type;
136 tlv->length = length;
137
138 if (length > 0 && data)
139 RtlCopyMemory(&tlv[1], data, length);
140
141 context->datalen += sizeof(btrfs_send_tlv) + length;
142}
143
144static char* uint64_to_char(uint64_t num, char* buf) {
145 char *tmp, tmp2[20];
146
147 if (num == 0) {
148 buf[0] = '0';
149 return buf + 1;
150 }
151
152 tmp = &tmp2[20];
153 while (num > 0) {
154 tmp--;
155 *tmp = (num % 10) + '0';
156 num /= 10;
157 }
158
159 RtlCopyMemory(buf, tmp, tmp2 + sizeof(tmp2) - tmp);
160
161 return &buf[tmp2 + sizeof(tmp2) - tmp];
162}
163
165 char *ptr, *ptr2;
166 uint64_t index = 0;
167 KEY searchkey;
168
169 name[0] = 'o';
170
172 *ptr = '-'; ptr++;
174 *ptr = '-'; ptr++;
175 ptr2 = ptr;
176
177 searchkey.obj_id = SUBVOL_ROOT_INODE;
178 searchkey.obj_type = TYPE_DIR_ITEM;
179
180 do {
183
185 *ptr = 0;
186
187 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, (ULONG)(ptr - name));
188
189 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
190 if (!NT_SUCCESS(Status)) {
191 ERR("find_item returned %08lx\n", Status);
192 return Status;
193 }
194
195 if (!keycmp(searchkey, tp.item->key))
196 goto cont;
197
198 if (context->parent) {
199 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
200 if (!NT_SUCCESS(Status)) {
201 ERR("find_item returned %08lx\n", Status);
202 return Status;
203 }
204
205 if (!keycmp(searchkey, tp.item->key))
206 goto cont;
207 }
208
209 return STATUS_SUCCESS;
210
211cont:
212 index++;
213 ptr = ptr2;
214 } while (true);
215}
216
218 LIST_ENTRY* le;
219
220 le = context->orphans.Flink;
221 while (le != &context->orphans) {
223
224 if (o2->inode > o->inode) {
226 return;
227 }
228
229 le = le->Flink;
230 }
231
232 InsertTailList(&context->orphans, &o->list_entry);
233}
234
237 KEY searchkey;
239 EXTENT_DATA* ed;
240
241 searchkey.obj_id = inode;
242 searchkey.obj_type = TYPE_EXTENT_DATA;
243 searchkey.offset = 0;
244
245 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
246 if (!NT_SUCCESS(Status)) {
247 ERR("find_item returned %08lx\n", Status);
248 return Status;
249 }
250
251 if (keycmp(tp.item->key, searchkey)) {
252 ERR("could not find (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
254 }
255
256 if (tp.item->size < sizeof(EXTENT_DATA)) {
257 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
258 tp.item->size, sizeof(EXTENT_DATA));
260 }
261
262 ed = (EXTENT_DATA*)tp.item->data;
263
264 if (ed->type != EXTENT_TYPE_INLINE) {
265 WARN("symlink data was not inline, returning blank string\n");
266 *link = NULL;
267 *linklen = 0;
268 return STATUS_SUCCESS;
269 }
270
271 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) {
272 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
275 }
276
277 *link = (char*)ed->data;
278 *linklen = (uint16_t)ed->decoded_size;
279
280 return STATUS_SUCCESS;
281}
282
285 INODE_ITEM* ii;
286
287 if (tp2 && !tp) {
288 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data;
289
290 if (tp2->item->size < sizeof(INODE_ITEM)) {
291 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
292 tp2->item->size, sizeof(INODE_ITEM));
294 }
295
296 context->lastinode.inode = tp2->item->key.obj_id;
297 context->lastinode.deleting = true;
298 context->lastinode.gen = ii2->generation;
299 context->lastinode.mode = ii2->st_mode;
300 context->lastinode.flags = ii2->flags;
301 context->lastinode.o = NULL;
302 context->lastinode.sd = NULL;
303
304 return STATUS_SUCCESS;
305 }
306
307 ii = (INODE_ITEM*)tp->item->data;
308
309 if (tp->item->size < sizeof(INODE_ITEM)) {
310 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
311 tp->item->size, sizeof(INODE_ITEM));
313 }
314
315 context->lastinode.inode = tp->item->key.obj_id;
316 context->lastinode.deleting = false;
317 context->lastinode.gen = ii->generation;
318 context->lastinode.uid = ii->st_uid;
319 context->lastinode.gid = ii->st_gid;
320 context->lastinode.mode = ii->st_mode;
321 context->lastinode.size = ii->st_size;
322 context->lastinode.atime = ii->st_atime;
323 context->lastinode.mtime = ii->st_mtime;
324 context->lastinode.ctime = ii->st_ctime;
325 context->lastinode.flags = ii->flags;
326 context->lastinode.file = false;
327 context->lastinode.o = NULL;
328 context->lastinode.sd = NULL;
329
330 if (context->lastinode.path) {
331 ExFreePool(context->lastinode.path);
332 context->lastinode.path = NULL;
333 }
334
335 if (tp2) {
336 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data;
337 LIST_ENTRY* le;
338
339 if (tp2->item->size < sizeof(INODE_ITEM)) {
340 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
341 tp2->item->size, sizeof(INODE_ITEM));
343 }
344
345 context->lastinode.oldmode = ii2->st_mode;
346 context->lastinode.olduid = ii2->st_uid;
347 context->lastinode.oldgid = ii2->st_gid;
348
349 if ((ii2->st_mode & __S_IFREG) == __S_IFREG && (ii2->st_mode & __S_IFLNK) != __S_IFLNK && (ii2->st_mode & __S_IFSOCK) != __S_IFSOCK)
350 context->lastinode.file = true;
351
352 context->lastinode.new = false;
353
354 le = context->orphans.Flink;
355 while (le != &context->orphans) {
357
358 if (o2->inode == tp->item->key.obj_id) {
359 context->lastinode.o = o2;
360 break;
361 } else if (o2->inode > tp->item->key.obj_id)
362 break;
363
364 le = le->Flink;
365 }
366 } else
367 context->lastinode.new = true;
368
370 send_dir* sd;
371
373 if (!NT_SUCCESS(Status)) {
374 ERR("find_send_dir returned %08lx\n", Status);
375 return Status;
376 }
377
378 sd->atime = ii->st_atime;
379 sd->mtime = ii->st_mtime;
380 sd->ctime = ii->st_ctime;
381 context->root_dir = sd;
382 } else if (!tp2) {
383 ULONG pos = context->datalen;
385 send_dir* sd;
386
387 char name[64];
388 orphan* o;
389
390 // skip creating orphan directory if we've already done so
391 if (ii->st_mode & __S_IFDIR) {
392 LIST_ENTRY* le;
393
394 le = context->orphans.Flink;
395 while (le != &context->orphans) {
397
398 if (o2->inode == tp->item->key.obj_id) {
399 context->lastinode.o = o2;
400 o2->sd->atime = ii->st_atime;
401 o2->sd->mtime = ii->st_mtime;
402 o2->sd->ctime = ii->st_ctime;
403 o2->sd->dummy = false;
404 return STATUS_SUCCESS;
405 } else if (o2->inode > tp->item->key.obj_id)
406 break;
407
408 le = le->Flink;
409 }
410 }
411
412 if ((ii->st_mode & __S_IFSOCK) == __S_IFSOCK)
414 else if ((ii->st_mode & __S_IFLNK) == __S_IFLNK)
416 else if ((ii->st_mode & __S_IFCHR) == __S_IFCHR || (ii->st_mode & __S_IFBLK) == __S_IFBLK)
418 else if ((ii->st_mode & __S_IFDIR) == __S_IFDIR)
420 else if ((ii->st_mode & __S_IFIFO) == __S_IFIFO)
422 else {
424 context->lastinode.file = true;
425 }
426
428
430 if (!NT_SUCCESS(Status)) {
431 ERR("get_orphan_name returned %08lx\n", Status);
432 return Status;
433 }
434
437
439 uint64_t rdev = makedev((ii->st_rdev & 0xFFFFFFFFFFF) >> 20, ii->st_rdev & 0xFFFFF), mode = ii->st_mode;
440
443 } else if (cmd == BTRFS_SEND_CMD_SYMLINK && ii->st_size > 0) {
444 char* link;
445 uint16_t linklen;
446
448 if (!NT_SUCCESS(Status)) {
449 ERR("send_read_symlink returned %08lx\n", Status);
450 return Status;
451 }
452
454 }
455
457
458 if (ii->st_mode & __S_IFDIR) {
460 if (!NT_SUCCESS(Status)) {
461 ERR("find_send_dir returned %08lx\n", Status);
462 return Status;
463 }
464
465 sd->dummy = false;
466 } else
467 sd = NULL;
468
469 context->lastinode.sd = sd;
470
472 if (!o) {
473 ERR("out of memory\n");
475 }
476
477 o->inode = tp->item->key.obj_id;
478 o->dir = (ii->st_mode & __S_IFDIR && ii->st_size > 0) ? true : false;
479 strcpy(o->tmpname, name);
480 o->sd = sd;
482
483 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, strlen(o->tmpname) + 1, ALLOC_TAG);
484 if (!context->lastinode.path) {
485 ERR("out of memory\n");
487 }
488
489 strcpy(context->lastinode.path, o->tmpname);
490
491 context->lastinode.o = o;
492 }
493
494 return STATUS_SUCCESS;
495}
496
498 LIST_ENTRY* le;
500
501 if (!sd) {
502 ERR("out of memory\n");
504 }
505
506 sd->inode = inode;
507 sd->dummy = dummy;
508 sd->parent = parent;
509
510 if (!dummy) {
511 sd->atime = context->lastinode.atime;
512 sd->mtime = context->lastinode.mtime;
513 sd->ctime = context->lastinode.ctime;
514 }
515
516 if (namelen > 0) {
518 if (!sd->name) {
519 ERR("out of memory\n");
520 ExFreePool(sd);
522 }
523
524 memcpy(sd->name, name, namelen);
525 } else
526 sd->name = NULL;
527
528 sd->namelen = namelen;
529
530 InitializeListHead(&sd->deleted_children);
531
532 if (lastentry)
533 InsertHeadList(lastentry, &sd->list_entry);
534 else {
535 le = context->dirs.Flink;
536 while (le != &context->dirs) {
538
539 if (sd2->inode > sd->inode) {
540 InsertHeadList(sd2->list_entry.Blink, &sd->list_entry);
541
542 if (psd)
543 *psd = sd;
544
545 return STATUS_SUCCESS;
546 }
547
548 le = le->Flink;
549 }
550
551 InsertTailList(&context->dirs, &sd->list_entry);
552 }
553
554 if (psd)
555 *psd = sd;
556
557 return STATUS_SUCCESS;
558}
559
562
563 while (parent && parent->namelen > 0) {
564 len += parent->namelen + 1;
565 parent = parent->parent;
566 }
567
568 return len;
569}
570
571static void find_path(char* path, send_dir* parent, char* name, ULONG namelen) {
572 ULONG len = namelen;
573
575
576 while (parent && parent->namelen > 0) {
577 RtlMoveMemory(path + parent->namelen + 1, path, len);
578 RtlCopyMemory(path, parent->name, parent->namelen);
579 path[parent->namelen] = '/';
580 len += parent->namelen + 1;
581
582 parent = parent->parent;
583 }
584}
585
588
590
591 if (len > 0)
592 find_path((char*)&context->data[context->datalen - len], parent, name, namelen);
593}
594
596 ULONG pos = context->datalen;
597
598 if (context->lastinode.o) {
600
601 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, context->root_dir, context->lastinode.o->tmpname, (uint16_t)strlen(context->lastinode.o->tmpname));
602
604
606 } else {
608
610
611 send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
612
614 }
615
616 if (context->lastinode.o) {
617 uint16_t pathlen;
618
619 if (context->lastinode.o->sd) {
620 if (context->lastinode.o->sd->name)
621 ExFreePool(context->lastinode.o->sd->name);
622
623 context->lastinode.o->sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG);
624 if (!context->lastinode.o->sd->name) {
625 ERR("out of memory\n");
627 }
628
629 RtlCopyMemory(context->lastinode.o->sd->name, name, namelen);
630 context->lastinode.o->sd->namelen = namelen;
631 context->lastinode.o->sd->parent = parent;
632 }
633
634 if (context->lastinode.path)
635 ExFreePool(context->lastinode.path);
636
637 pathlen = find_path_len(parent, namelen);
638 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, pathlen + 1, ALLOC_TAG);
639 if (!context->lastinode.path) {
640 ERR("out of memory\n");
642 }
643
644 find_path(context->lastinode.path, parent, name, namelen);
645 context->lastinode.path[pathlen] = 0;
646
647 RemoveEntryList(&context->lastinode.o->list_entry);
648 ExFreePool(context->lastinode.o);
649
650 context->lastinode.o = NULL;
651 }
652
653 return STATUS_SUCCESS;
654}
655
657 ULONG pos = context->datalen;
658
660
661 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, sd->parent, sd->name, sd->namelen);
662
666
668}
669
672 LIST_ENTRY* le;
673 char name[64];
674
675 le = context->dirs.Flink;
676 while (le != &context->dirs) {
678
679 if (sd2->inode > dir)
680 break;
681 else if (sd2->inode == dir) {
682 *psd = sd2;
683
684 if (added_dummy)
685 *added_dummy = false;
686
687 return STATUS_SUCCESS;
688 }
689
690 le = le->Flink;
691 }
692
693 if (dir == SUBVOL_ROOT_INODE) {
694 Status = send_add_dir(context, dir, NULL, NULL, 0, false, le, psd);
695 if (!NT_SUCCESS(Status)) {
696 ERR("send_add_dir returned %08lx\n", Status);
697 return Status;
698 }
699
700 if (added_dummy)
701 *added_dummy = false;
702
703 return STATUS_SUCCESS;
704 }
705
706 if (context->parent) {
707 KEY searchkey;
709
710 searchkey.obj_id = dir;
711 searchkey.obj_type = TYPE_INODE_REF; // directories should never have an extiref
712 searchkey.offset = 0xffffffffffffffff;
713
714 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
715 if (!NT_SUCCESS(Status)) {
716 ERR("find_item returned %08lx\n", Status);
717 return Status;
718 }
719
720 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
721 INODE_REF* ir = (INODE_REF*)tp.item->data;
723
724 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
725 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
727 }
728
730 parent = context->root_dir;
731 else {
733 if (!NT_SUCCESS(Status)) {
734 ERR("find_send_dir returned %08lx\n", Status);
735 return Status;
736 }
737 }
738
739 Status = send_add_dir(context, dir, parent, ir->name, ir->n, true, NULL, psd);
740 if (!NT_SUCCESS(Status)) {
741 ERR("send_add_dir returned %08lx\n", Status);
742 return Status;
743 }
744
745 if (added_dummy)
746 *added_dummy = false;
747
748 return STATUS_SUCCESS;
749 }
750 }
751
753 if (!NT_SUCCESS(Status)) {
754 ERR("get_orphan_name returned %08lx\n", Status);
755 return Status;
756 }
757
758 Status = send_add_dir(context, dir, NULL, name, (uint16_t)strlen(name), true, le, psd);
759 if (!NT_SUCCESS(Status)) {
760 ERR("send_add_dir returned %08lx\n", Status);
761 return Status;
762 }
763
764 if (added_dummy)
765 *added_dummy = true;
766
767 return STATUS_SUCCESS;
768}
769
772 uint64_t inode = tp ? tp->item->key.obj_id : 0, dir = tp ? tp->item->key.offset : 0;
773 LIST_ENTRY* le;
774 INODE_REF* ir;
776 send_dir* sd = NULL;
777 orphan* o2 = NULL;
778
779 if (inode == dir) // root
780 return STATUS_SUCCESS;
781
782 if (tp->item->size < sizeof(INODE_REF)) {
783 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
784 tp->item->size, sizeof(INODE_REF));
786 }
787
788 if (dir != SUBVOL_ROOT_INODE) {
789 bool added_dummy;
790
791 Status = find_send_dir(context, dir, context->root->root_item.ctransid, &sd, &added_dummy);
792 if (!NT_SUCCESS(Status)) {
793 ERR("find_send_dir returned %08lx\n", Status);
794 return Status;
795 }
796
797 // directory has higher inode number than file, so might need to be created
798 if (added_dummy) {
799 bool found = false;
800
801 le = context->orphans.Flink;
802 while (le != &context->orphans) {
804
805 if (o2->inode == dir) {
806 found = true;
807 break;
808 } else if (o2->inode > dir)
809 break;
810
811 le = le->Flink;
812 }
813
814 if (!found) {
815 ULONG pos = context->datalen;
816
818
820
822
824
826 if (!o2) {
827 ERR("out of memory\n");
829 }
830
831 o2->inode = dir;
832 o2->dir = true;
833 memcpy(o2->tmpname, sd->name, sd->namelen);
834 o2->tmpname[sd->namelen] = 0;
835 o2->sd = sd;
836 add_orphan(context, o2);
837 }
838 }
839 } else
840 sd = context->root_dir;
841
842 len = tp->item->size;
843 ir = (INODE_REF*)tp->item->data;
844
845 while (len > 0) {
846 ref* r;
847
848 if (len < sizeof(INODE_REF) || len < offsetof(INODE_REF, name[0]) + ir->n) {
849 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
851 }
852
854 if (!r) {
855 ERR("out of memory\n");
857 }
858
859 r->sd = sd;
860 r->namelen = ir->n;
861 RtlCopyMemory(r->name, ir->name, ir->n);
862
863 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry);
864
865 len -= (uint16_t)offsetof(INODE_REF, name[0]) + ir->n;
866 ir = (INODE_REF*)&ir->name[ir->n];
867 }
868
869 return STATUS_SUCCESS;
870}
871
873 INODE_EXTREF* ier;
875
876 if (tp->item->size < sizeof(INODE_EXTREF)) {
877 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
878 tp->item->size, sizeof(INODE_EXTREF));
880 }
881
882 len = tp->item->size;
883 ier = (INODE_EXTREF*)tp->item->data;
884
885 while (len > 0) {
887 send_dir* sd = NULL;
888 orphan* o2 = NULL;
889 ref* r;
890
891 if (len < sizeof(INODE_EXTREF) || len < offsetof(INODE_EXTREF, name[0]) + ier->n) {
892 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
894 }
895
896 if (ier->dir != SUBVOL_ROOT_INODE) {
897 LIST_ENTRY* le;
898 bool added_dummy;
899
900 Status = find_send_dir(context, ier->dir, context->root->root_item.ctransid, &sd, &added_dummy);
901 if (!NT_SUCCESS(Status)) {
902 ERR("find_send_dir returned %08lx\n", Status);
903 return Status;
904 }
905
906 // directory has higher inode number than file, so might need to be created
907 if (added_dummy) {
908 bool found = false;
909
910 le = context->orphans.Flink;
911 while (le != &context->orphans) {
913
914 if (o2->inode == ier->dir) {
915 found = true;
916 break;
917 } else if (o2->inode > ier->dir)
918 break;
919
920 le = le->Flink;
921 }
922
923 if (!found) {
924 ULONG pos = context->datalen;
925
927
930
932
934 if (!o2) {
935 ERR("out of memory\n");
937 }
938
939 o2->inode = ier->dir;
940 o2->dir = true;
941 memcpy(o2->tmpname, sd->name, sd->namelen);
942 o2->tmpname[sd->namelen] = 0;
943 o2->sd = sd;
944 add_orphan(context, o2);
945 }
946 }
947 } else
948 sd = context->root_dir;
949
951 if (!r) {
952 ERR("out of memory\n");
954 }
955
956 r->sd = sd;
957 r->namelen = ier->n;
958 RtlCopyMemory(r->name, ier->name, ier->n);
959
960 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry);
961
962 len -= (uint16_t)offsetof(INODE_EXTREF, name[0]) + ier->n;
963 ier = (INODE_EXTREF*)&ier->name[ier->n];
964 }
965
966 return STATUS_SUCCESS;
967}
968
970 ULONG pos = context->datalen;
971
973
975
976 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));
977 send_add_tlv(context, BTRFS_SEND_TLV_TRANSID, &r->root_item.ctransid, sizeof(uint64_t));
978
979 if (context->parent) {
981 context->parent->root_item.rtransid == 0 ? &context->parent->root_item.uuid : &context->parent->root_item.received_uuid, sizeof(BTRFS_UUID));
982 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &context->parent->root_item.ctransid, sizeof(uint64_t));
983 }
984
986}
987
989 ULONG pos = context->datalen;
990
992
996
998}
999
1001 ULONG pos = context->datalen;
1002
1004
1005 mode &= 07777;
1006
1009
1011}
1012
1014 ULONG pos = context->datalen;
1015
1017
1022
1024}
1025
1027 ULONG pos = context->datalen;
1028
1030
1033
1035}
1036
1038 ULONG pos = context->datalen;
1039 uint16_t pathlen;
1040
1042
1043 pathlen = find_path_len(parent, namelen);
1045
1046 find_path((char*)&context->data[context->datalen - pathlen], parent, name, namelen);
1047
1049
1050 return STATUS_SUCCESS;
1051}
1052
1053static void send_rmdir_command(send_context* context, uint16_t pathlen, char* path) {
1054 ULONG pos = context->datalen;
1055
1059}
1060
1063 KEY searchkey;
1065
1066 *last_inode = 0;
1067
1068 searchkey.obj_id = context->lastinode.inode;
1069 searchkey.obj_type = TYPE_DIR_INDEX;
1070 searchkey.offset = 2;
1071
1072 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
1073 if (!NT_SUCCESS(Status)) {
1074 ERR("find_item returned %08lx\n", Status);
1075 return Status;
1076 }
1077
1078 do {
1079 traverse_ptr next_tp;
1080
1081 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1082 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
1083
1084 if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
1085 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1086 return STATUS_INTERNAL_ERROR;
1087 }
1088
1089 if (di->key.obj_type == TYPE_INODE_ITEM)
1090 *last_inode = max(*last_inode, di->key.obj_id);
1091 } else
1092 break;
1093
1094 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
1095 tp = next_tp;
1096 else
1097 break;
1098 } while (true);
1099
1100 return STATUS_SUCCESS;
1101}
1102
1104 pending_rmdir* pr;
1105 LIST_ENTRY* le;
1106
1108 if (!pr) {
1109 ERR("out of memory\n");
1111 }
1112
1113 pr->sd = context->lastinode.sd;
1114 pr->last_child_inode = last_inode;
1115
1116 le = context->pending_rmdirs.Flink;
1117 while (le != &context->pending_rmdirs) {
1119
1120 if (pr2->last_child_inode > pr->last_child_inode) {
1121 InsertHeadList(pr2->list_entry.Blink, &pr->list_entry);
1122 return STATUS_SUCCESS;
1123 }
1124
1125 le = le->Flink;
1126 }
1127
1128 InsertTailList(&context->pending_rmdirs, &pr->list_entry);
1129
1130 return STATUS_SUCCESS;
1131}
1132
1135 KEY searchkey;
1137 DIR_ITEM* di;
1138 uint16_t len;
1139
1140 searchkey.obj_id = sd->inode;
1141 searchkey.obj_type = TYPE_DIR_ITEM;
1142 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, namelen);
1143
1144 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
1145 if (!NT_SUCCESS(Status)) {
1146 ERR("find_item returned %08lx\n", Status);
1147 return Status;
1148 }
1149
1150 if (keycmp(tp.item->key, searchkey))
1151 return STATUS_SUCCESS;
1152
1153 di = (DIR_ITEM*)tp.item->data;
1154 len = tp.item->size;
1155
1156 do {
1157 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
1158 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1159 return STATUS_INTERNAL_ERROR;
1160 }
1161
1162 if (di->n == namelen && RtlCompareMemory(di->name, name, namelen) == namelen) {
1163 *inode = di->key.obj_type == TYPE_INODE_ITEM ? di->key.obj_id : 0;
1164 *dir = di->type == BTRFS_TYPE_DIRECTORY ? true: false;
1166 }
1167
1168 di = (DIR_ITEM*)&di->name[di->m + di->n];
1169 len -= (uint16_t)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
1170 } while (len > 0);
1171
1172 return STATUS_SUCCESS;
1173}
1174
1177 ULONG pos = context->datalen;
1178 send_dir* sd = NULL;
1179 orphan* o;
1180 LIST_ENTRY* le;
1181 char name[64];
1182
1183 if (!dir) {
1185
1187 if (!dc) {
1188 ERR("out of memory\n");
1190 }
1191
1192 dc->namelen = r->namelen;
1193 RtlCopyMemory(dc->name, r->name, r->namelen);
1194 InsertTailList(&r->sd->deleted_children, &dc->list_entry);
1195 }
1196
1197 le = context->orphans.Flink;
1198 while (le != &context->orphans) {
1200
1201 if (o2->inode == inode) {
1203
1205
1207
1208 return STATUS_SUCCESS;
1209 } else if (o2->inode > inode)
1210 break;
1211
1212 le = le->Flink;
1213 }
1214
1216 if (!NT_SUCCESS(Status)) {
1217 ERR("get_orphan_name returned %08lx\n", Status);
1218 return Status;
1219 }
1220
1221 if (dir) {
1223 if (!NT_SUCCESS(Status)) {
1224 ERR("find_send_dir returned %08lx\n", Status);
1225 return Status;
1226 }
1227
1228 sd->dummy = true;
1229
1231
1234
1236
1237 if (sd->name)
1238 ExFreePool(sd->name);
1239
1240 sd->namelen = (uint16_t)strlen(name);
1241 sd->name = ExAllocatePoolWithTag(PagedPool, sd->namelen, ALLOC_TAG);
1242 if (!sd->name) {
1243 ERR("out of memory\n");
1245 }
1246
1247 RtlCopyMemory(sd->name, name, sd->namelen);
1248 sd->parent = context->root_dir;
1249 } else {
1251
1253
1255
1257 }
1258
1260 if (!o) {
1261 ERR("out of memory\n");
1263 }
1264
1265 o->inode = inode;
1266 o->dir = true;
1267 strcpy(o->tmpname, name);
1268 o->sd = sd;
1269 add_orphan(context, o);
1270
1271 return STATUS_SUCCESS;
1272}
1273
1276 LIST_ENTRY* le;
1277 ref *nameref = NULL, *nameref2 = NULL;
1278
1279 if (context->lastinode.mode & __S_IFDIR) { // directory
1280 ref* r = IsListEmpty(&context->lastinode.refs) ? NULL : CONTAINING_RECORD(context->lastinode.refs.Flink, ref, list_entry);
1281 ref* or = IsListEmpty(&context->lastinode.oldrefs) ? NULL : CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry);
1282
1283 if (or && !context->lastinode.o) {
1284 ULONG len = find_path_len(or->sd, or->namelen);
1285
1286 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1287 if (!context->lastinode.path) {
1288 ERR("out of memory\n");
1290 }
1291
1292 find_path(context->lastinode.path, or->sd, or->name, or->namelen);
1293 context->lastinode.path[len] = 0;
1294
1295 if (!context->lastinode.sd) {
1296 Status = find_send_dir(context, context->lastinode.inode, context->lastinode.gen, &context->lastinode.sd, false);
1297 if (!NT_SUCCESS(Status)) {
1298 ERR("find_send_dir returned %08lx\n", Status);
1299 return Status;
1300 }
1301 }
1302 }
1303
1304 if (r && or) {
1306 bool dir;
1307
1308 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir);
1310 ERR("look_for_collision returned %08lx\n", Status);
1311 return Status;
1312 }
1313
1314 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) {
1315 Status = make_file_orphan(context, inode, dir, context->parent->root_item.ctransid, r);
1316 if (!NT_SUCCESS(Status)) {
1317 ERR("make_file_orphan returned %08lx\n", Status);
1318 return Status;
1319 }
1320 }
1321
1322 if (context->lastinode.o) {
1323 Status = found_path(context, r->sd, r->name, r->namelen);
1324 if (!NT_SUCCESS(Status)) {
1325 ERR("found_path returned %08lx\n", Status);
1326 return Status;
1327 }
1328
1329 if (!r->sd->dummy)
1330 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1331 } else if (r->sd != or->sd || r->namelen != or->namelen || RtlCompareMemory(r->name, or->name, r->namelen) != r->namelen) { // moved or renamed
1332 ULONG pos = context->datalen, len;
1333
1335
1336 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
1337
1339
1341
1342 if (!r->sd->dummy)
1343 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1344
1345 if (context->lastinode.sd->name)
1346 ExFreePool(context->lastinode.sd->name);
1347
1348 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, r->namelen, ALLOC_TAG);
1349 if (!context->lastinode.sd->name) {
1350 ERR("out of memory\n");
1352 }
1353
1354 RtlCopyMemory(context->lastinode.sd->name, r->name, r->namelen);
1355 context->lastinode.sd->parent = r->sd;
1356
1357 if (context->lastinode.path)
1358 ExFreePool(context->lastinode.path);
1359
1360 len = find_path_len(r->sd, r->namelen);
1361 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1362 if (!context->lastinode.path) {
1363 ERR("out of memory\n");
1365 }
1366
1367 find_path(context->lastinode.path, r->sd, r->name, r->namelen);
1368 context->lastinode.path[len] = 0;
1369 }
1370 } else if (r && !or) { // new
1371 Status = found_path(context, r->sd, r->name, r->namelen);
1372 if (!NT_SUCCESS(Status)) {
1373 ERR("found_path returned %08lx\n", Status);
1374 return Status;
1375 }
1376
1377 if (!r->sd->dummy)
1378 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1379 } else { // deleted
1380 uint64_t last_inode;
1381
1382 Status = get_dir_last_child(context, &last_inode);
1383 if (!NT_SUCCESS(Status)) {
1384 ERR("get_dir_last_child returned %08lx\n", Status);
1385 return Status;
1386 }
1387
1388 if (last_inode <= context->lastinode.inode) {
1389 send_rmdir_command(context, (uint16_t)strlen(context->lastinode.path), context->lastinode.path);
1390
1391 if (!or->sd->dummy)
1392 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime);
1393 } else {
1394 char name[64];
1395 ULONG pos = context->datalen;
1396
1397 Status = get_orphan_name(context, context->lastinode.inode, context->lastinode.gen, name);
1398 if (!NT_SUCCESS(Status)) {
1399 ERR("get_orphan_name returned %08lx\n", Status);
1400 return Status;
1401 }
1402
1404 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
1407
1408 if (context->lastinode.sd->name)
1409 ExFreePool(context->lastinode.sd->name);
1410
1411 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, strlen(name), ALLOC_TAG);
1412 if (!context->lastinode.sd->name) {
1413 ERR("out of memory\n");
1415 }
1416
1417 RtlCopyMemory(context->lastinode.sd->name, name, strlen(name));
1418 context->lastinode.sd->namelen = (uint16_t)strlen(name);
1419 context->lastinode.sd->dummy = true;
1420 context->lastinode.sd->parent = NULL;
1421
1422 send_utimes_command(context, NULL, &context->root_dir->atime, &context->root_dir->mtime, &context->root_dir->ctime);
1423
1424 Status = add_pending_rmdir(context, last_inode);
1425 if (!NT_SUCCESS(Status)) {
1426 ERR("add_pending_rmdir returned %08lx\n", Status);
1427 return Status;
1428 }
1429 }
1430 }
1431
1432 while (!IsListEmpty(&context->lastinode.refs)) {
1433 r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry);
1434 ExFreePool(r);
1435 }
1436
1437 while (!IsListEmpty(&context->lastinode.oldrefs)) {
1438 or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry);
1439 ExFreePool(or);
1440 }
1441
1442 return STATUS_SUCCESS;
1443 } else {
1444 if (!IsListEmpty(&context->lastinode.oldrefs)) {
1445 ref* or = CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry);
1446 ULONG len = find_path_len(or->sd, or->namelen);
1447
1448 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1449 if (!context->lastinode.path) {
1450 ERR("out of memory\n");
1452 }
1453
1454 find_path(context->lastinode.path, or->sd, or->name, or->namelen);
1455 context->lastinode.path[len] = 0;
1456 nameref = or;
1457 }
1458
1459 // remove unchanged refs
1460 le = context->lastinode.oldrefs.Flink;
1461 while (le != &context->lastinode.oldrefs) {
1462 ref* or = CONTAINING_RECORD(le, ref, list_entry);
1463 LIST_ENTRY* le2;
1464 bool matched = false;
1465
1466 le2 = context->lastinode.refs.Flink;
1467 while (le2 != &context->lastinode.refs) {
1469
1470 if (r->sd == or->sd && r->namelen == or->namelen && RtlCompareMemory(r->name, or->name, r->namelen) == r->namelen) {
1471 RemoveEntryList(&r->list_entry);
1472 ExFreePool(r);
1473 matched = true;
1474 break;
1475 }
1476
1477 le2 = le2->Flink;
1478 }
1479
1480 if (matched) {
1481 le = le->Flink;
1483 ExFreePool(or);
1484 continue;
1485 }
1486
1487 le = le->Flink;
1488 }
1489
1490 while (!IsListEmpty(&context->lastinode.refs)) {
1491 ref* r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry);
1493 bool dir;
1494
1495 if (context->parent) {
1496 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir);
1498 ERR("look_for_collision returned %08lx\n", Status);
1499 return Status;
1500 }
1501
1502 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) {
1503 Status = make_file_orphan(context, inode, dir, context->lastinode.gen, r);
1504 if (!NT_SUCCESS(Status)) {
1505 ERR("make_file_orphan returned %08lx\n", Status);
1506 return Status;
1507 }
1508 }
1509 }
1510
1511 if (context->datalen > SEND_BUFFER_LENGTH) {
1512 Status = wait_for_flush(context, tp1, tp2);
1513 if (!NT_SUCCESS(Status)) {
1514 ERR("wait_for_flush returned %08lx\n", Status);
1515 return Status;
1516 }
1517
1518 if (context->send->cancelling)
1519 return STATUS_SUCCESS;
1520 }
1521
1522 Status = found_path(context, r->sd, r->name, r->namelen);
1523 if (!NT_SUCCESS(Status)) {
1524 ERR("found_path returned %08lx\n", Status);
1525 return Status;
1526 }
1527
1528 if (!r->sd->dummy)
1529 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1530
1531 if (nameref && !nameref2)
1532 nameref2 = r;
1533 else
1534 ExFreePool(r);
1535 }
1536
1537 while (!IsListEmpty(&context->lastinode.oldrefs)) {
1538 ref* or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry);
1539 bool deleted = false;
1540
1541 le = or->sd->deleted_children.Flink;
1542 while (le != &or->sd->deleted_children) {
1544
1545 if (dc->namelen == or->namelen && RtlCompareMemory(dc->name, or->name, or->namelen) == or->namelen) {
1546 RemoveEntryList(&dc->list_entry);
1547 ExFreePool(dc);
1548 deleted = true;
1549 break;
1550 }
1551
1552 le = le->Flink;
1553 }
1554
1555 if (!deleted) {
1556 if (context->datalen > SEND_BUFFER_LENGTH) {
1557 Status = wait_for_flush(context, tp1, tp2);
1558 if (!NT_SUCCESS(Status)) {
1559 ERR("wait_for_flush returned %08lx\n", Status);
1560 return Status;
1561 }
1562
1563 if (context->send->cancelling)
1564 return STATUS_SUCCESS;
1565 }
1566
1567 Status = send_unlink_command(context, or->sd, or->namelen, or->name);
1568 if (!NT_SUCCESS(Status)) {
1569 ERR("send_unlink_command returned %08lx\n", Status);
1570 return Status;
1571 }
1572
1573 if (!or->sd->dummy)
1574 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime);
1575 }
1576
1577 if (or == nameref && nameref2) {
1578 uint16_t len = find_path_len(nameref2->sd, nameref2->namelen);
1579
1580 if (context->lastinode.path)
1581 ExFreePool(context->lastinode.path);
1582
1583 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1584 if (!context->lastinode.path) {
1585 ERR("out of memory\n");
1587 }
1588
1589 find_path(context->lastinode.path, nameref2->sd, nameref2->name, nameref2->namelen);
1590 context->lastinode.path[len] = 0;
1591
1592 ExFreePool(nameref2);
1593 }
1594
1595 ExFreePool(or);
1596 }
1597 }
1598
1599 return STATUS_SUCCESS;
1600}
1601
1604 KEY key1, key2;
1605
1606 if (tp1)
1607 key1 = tp1->item->key;
1608
1609 if (tp2)
1610 key2 = tp2->item->key;
1611
1612 ExReleaseResourceLite(&context->Vcb->tree_lock);
1613
1614 KeClearEvent(&context->send->cleared_event);
1615 KeSetEvent(&context->buffer_event, 0, true);
1616 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
1617
1618 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true);
1619
1620 if (context->send->cancelling)
1621 return STATUS_SUCCESS;
1622
1623 if (tp1) {
1624 Status = find_item(context->Vcb, context->root, tp1, &key1, false, NULL);
1625 if (!NT_SUCCESS(Status)) {
1626 ERR("find_item returned %08lx\n", Status);
1627 return Status;
1628 }
1629
1630 if (keycmp(tp1->item->key, key1)) {
1631 ERR("readonly subvolume changed\n");
1632 return STATUS_INTERNAL_ERROR;
1633 }
1634 }
1635
1636 if (tp2) {
1637 Status = find_item(context->Vcb, context->parent, tp2, &key2, false, NULL);
1638 if (!NT_SUCCESS(Status)) {
1639 ERR("find_item returned %08lx\n", Status);
1640 return Status;
1641 }
1642
1643 if (keycmp(tp2->item->key, key2)) {
1644 ERR("readonly subvolume changed\n");
1645 return STATUS_INTERNAL_ERROR;
1646 }
1647 }
1648
1649 return STATUS_SUCCESS;
1650}
1651
1653 uint64_t lastoff = 0;
1654 LIST_ENTRY* le;
1655
1656 le = exts->Flink;
1657 while (le != exts) {
1659
1660 if (ext->offset > lastoff) {
1662 EXTENT_DATA2* ed2;
1663
1664 if (!ext2) {
1665 ERR("out of memory\n");
1667 }
1668
1669 ed2 = (EXTENT_DATA2*)ext2->data.data;
1670
1671 ext2->offset = lastoff;
1672 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1673 ext2->data.decoded_size = ed2->num_bytes = ext->offset - lastoff;
1674 ext2->data.type = EXTENT_TYPE_REGULAR;
1675 ed2->address = ed2->size = ed2->offset = 0;
1676
1677 InsertHeadList(le->Blink, &ext2->list_entry);
1678 }
1679
1680 if (ext->data.type == EXTENT_TYPE_INLINE)
1681 lastoff = ext->offset + ext->data.decoded_size;
1682 else {
1683 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data.data;
1684 lastoff = ext->offset + ed2->num_bytes;
1685 }
1686
1687 le = le->Flink;
1688 }
1689
1690 if (size > lastoff) {
1692 EXTENT_DATA2* ed2;
1693
1694 if (!ext2) {
1695 ERR("out of memory\n");
1697 }
1698
1699 ed2 = (EXTENT_DATA2*)ext2->data.data;
1700
1701 ext2->offset = lastoff;
1702 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1703 ext2->data.decoded_size = ed2->num_bytes = sector_align(size - lastoff, Vcb->superblock.sector_size);
1704 ext2->data.type = EXTENT_TYPE_REGULAR;
1705 ed2->address = ed2->size = ed2->offset = 0;
1706
1707 InsertTailList(exts, &ext2->list_entry);
1708 }
1709
1710 return STATUS_SUCCESS;
1711}
1712
1714 send_ext* ext2;
1715 EXTENT_DATA2 *ed2a, *ed2b;
1716
1717 if (ext->data.type == EXTENT_TYPE_INLINE) {
1718 if (!trunc) {
1720
1721 if (!ext2) {
1722 ERR("out of memory\n");
1724 }
1725
1726 ext2->offset = ext->offset + len;
1727 ext2->datalen = (ULONG)(ext->data.decoded_size - len);
1728 ext2->data.decoded_size = ext->data.decoded_size - len;
1729 ext2->data.compression = ext->data.compression;
1730 ext2->data.encryption = ext->data.encryption;
1731 ext2->data.encoding = ext->data.encoding;
1732 ext2->data.type = ext->data.type;
1733 RtlCopyMemory(ext2->data.data, ext->data.data + len, (ULONG)(ext->data.decoded_size - len));
1734
1735 InsertHeadList(&ext->list_entry, &ext2->list_entry);
1736 }
1737
1738 ext->data.decoded_size = len;
1739
1740 return STATUS_SUCCESS;
1741 }
1742
1743 ed2a = (EXTENT_DATA2*)ext->data.data;
1744
1745 if (!trunc) {
1747
1748 if (!ext2) {
1749 ERR("out of memory\n");
1751 }
1752
1753 ed2b = (EXTENT_DATA2*)ext2->data.data;
1754
1755 ext2->offset = ext->offset + len;
1756 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1757
1758 ext2->data.compression = ext->data.compression;
1759 ext2->data.encryption = ext->data.encryption;
1760 ext2->data.encoding = ext->data.encoding;
1761 ext2->data.type = ext->data.type;
1762 ed2b->num_bytes = ed2a->num_bytes - len;
1763
1764 if (ed2a->size == 0) {
1765 ext2->data.decoded_size = ed2b->num_bytes;
1766 ext->data.decoded_size = len;
1767
1768 ed2b->address = ed2b->size = ed2b->offset = 0;
1769 } else {
1770 ext2->data.decoded_size = ext->data.decoded_size;
1771
1772 ed2b->address = ed2a->address;
1773 ed2b->size = ed2a->size;
1774 ed2b->offset = ed2a->offset + len;
1775 }
1776
1777 InsertHeadList(&ext->list_entry, &ext2->list_entry);
1778 }
1779
1780 ed2a->num_bytes = len;
1781
1782 return STATUS_SUCCESS;
1783}
1784
1787 send_ext *ext1, *ext2;
1788
1789 ext1 = CONTAINING_RECORD(context->lastinode.exts.Flink, send_ext, list_entry);
1790 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Flink, send_ext, list_entry);
1791
1792 do {
1793 uint64_t len1, len2;
1794 EXTENT_DATA2 *ed2a, *ed2b;
1795
1796 ed2a = ext1->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext1->data.data;
1797 ed2b = ext2->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext2->data.data;
1798
1799 len1 = ed2a ? ed2a->num_bytes : ext1->data.decoded_size;
1800 len2 = ed2b ? ed2b->num_bytes : ext2->data.decoded_size;
1801
1802 if (len1 < len2) {
1803 Status = divide_ext(ext2, len1, false);
1804 if (!NT_SUCCESS(Status)) {
1805 ERR("divide_ext returned %08lx\n", Status);
1806 return Status;
1807 }
1808 } else if (len2 < len1) {
1809 Status = divide_ext(ext1, len2, false);
1810 if (!NT_SUCCESS(Status)) {
1811 ERR("divide_ext returned %08lx\n", Status);
1812 return Status;
1813 }
1814 }
1815
1816 if (ext1->list_entry.Flink == &context->lastinode.exts || ext2->list_entry.Flink == &context->lastinode.oldexts)
1817 break;
1818
1819 ext1 = CONTAINING_RECORD(ext1->list_entry.Flink, send_ext, list_entry);
1820 ext2 = CONTAINING_RECORD(ext2->list_entry.Flink, send_ext, list_entry);
1821 } while (true);
1822
1823 ext1 = CONTAINING_RECORD(context->lastinode.exts.Blink, send_ext, list_entry);
1824 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Blink, send_ext, list_entry);
1825
1826 Status = divide_ext(ext1, context->lastinode.size - ext1->offset, true);
1827 if (!NT_SUCCESS(Status)) {
1828 ERR("divide_ext returned %08lx\n", Status);
1829 return Status;
1830 }
1831
1832 Status = divide_ext(ext2, context->lastinode.size - ext2->offset, true);
1833 if (!NT_SUCCESS(Status)) {
1834 ERR("divide_ext returned %08lx\n", Status);
1835 return Status;
1836 }
1837
1838 return STATUS_SUCCESS;
1839}
1840
1843 KEY searchkey;
1845 uint16_t len = 0;
1846 uint64_t num;
1847 uint8_t* ptr;
1848
1849 num = inode;
1850
1851 while (num != SUBVOL_ROOT_INODE) {
1852 searchkey.obj_id = num;
1853 searchkey.obj_type = TYPE_INODE_EXTREF;
1854 searchkey.offset = 0xffffffffffffffff;
1855
1856 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1857 if (!NT_SUCCESS(Status)) {
1858 ERR("find_item returned %08lx\n", Status);
1859 return false;
1860 }
1861
1863 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id);
1864 return false;
1865 }
1866
1867 if (len > 0)
1868 len++;
1869
1870 if (tp.item->key.obj_type == TYPE_INODE_REF) {
1871 INODE_REF* ir = (INODE_REF*)tp.item->data;
1872
1873 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
1874 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1875 return false;
1876 }
1877
1878 len += ir->n;
1879 num = tp.item->key.offset;
1880 } else {
1882
1883 if (tp.item->size < sizeof(INODE_EXTREF) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
1884 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1885 return false;
1886 }
1887
1888 len += ier->n;
1889 num = ier->dir;
1890 }
1891 }
1892
1894 ptr = &context->data[context->datalen];
1895
1896 num = inode;
1897
1898 while (num != SUBVOL_ROOT_INODE) {
1899 searchkey.obj_id = num;
1900 searchkey.obj_type = TYPE_INODE_EXTREF;
1901 searchkey.offset = 0xffffffffffffffff;
1902
1903 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1904 if (!NT_SUCCESS(Status)) {
1905 ERR("find_item returned %08lx\n", Status);
1906 return false;
1907 }
1908
1910 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id);
1911 return false;
1912 }
1913
1914 if (num != inode) {
1915 ptr--;
1916 *ptr = '/';
1917 }
1918
1919 if (tp.item->key.obj_type == TYPE_INODE_REF) {
1920 INODE_REF* ir = (INODE_REF*)tp.item->data;
1921
1922 RtlCopyMemory(ptr - ir->n, ir->name, ir->n);
1923 ptr -= ir->n;
1924 num = tp.item->key.offset;
1925 } else {
1927
1928 RtlCopyMemory(ptr - ier->n, ier->name, ier->n);
1929 ptr -= ier->n;
1930 num = ier->dir;
1931 }
1932 }
1933
1934 return true;
1935}
1936
1939 root* r = NULL;
1940 KEY searchkey;
1942 EXTENT_DATA2* seed2 = (EXTENT_DATA2*)se->data.data;
1943
1944 if (context->parent && edr->root == context->parent->id)
1945 r = context->parent;
1946
1947 if (!r && context->num_clones > 0) {
1948 ULONG i;
1949
1950 for (i = 0; i < context->num_clones; i++) {
1951 if (context->clones[i]->id == edr->root && context->clones[i] != context->root) {
1952 r = context->clones[i];
1953 break;
1954 }
1955 }
1956 }
1957
1958 if (!r)
1959 return false;
1960
1961 searchkey.obj_id = edr->objid;
1962 searchkey.obj_type = TYPE_EXTENT_DATA;
1963 searchkey.offset = 0;
1964
1965 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1966 if (!NT_SUCCESS(Status)) {
1967 ERR("find_item returned %08lx\n", Status);
1968 return false;
1969 }
1970
1971 while (true) {
1972 traverse_ptr next_tp;
1973
1974 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1975 if (tp.item->size < sizeof(EXTENT_DATA))
1976 ERR("(%I64x,%x,%I64x) has size %u, not at least %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
1977 else {
1979
1980 if (ed->type == EXTENT_TYPE_REGULAR) {
1981 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2))
1982 ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1983 tp.item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
1984 else {
1985 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
1986
1987 if (ed2->address == seed2->address && ed2->size == seed2->size && seed2->offset <= ed2->offset && seed2->offset + seed2->num_bytes >= ed2->offset + ed2->num_bytes) {
1988 uint64_t clone_offset = tp.item->key.offset + ed2->offset - seed2->offset;
1989 uint64_t clone_len = min(context->lastinode.size - se->offset, ed2->num_bytes);
1990
1991 if ((clone_offset & (context->Vcb->superblock.sector_size - 1)) == 0 && (clone_len & (context->Vcb->superblock.sector_size - 1)) == 0) {
1992 ULONG pos = context->datalen;
1993
1995
1998 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
1999 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));
2000 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &r->root_item.ctransid, sizeof(uint64_t));
2001
2003 context->datalen = pos;
2004 else {
2005 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_OFFSET, &clone_offset, sizeof(uint64_t));
2006
2008
2009 return true;
2010 }
2011 }
2012 }
2013 }
2014 }
2015 }
2016 } 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))
2017 break;
2018
2019 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
2020 tp = next_tp;
2021 else
2022 break;
2023 }
2024
2025 return false;
2026}
2027
2030 KEY searchkey;
2032 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)se->data.data;
2033 EXTENT_ITEM* ei;
2034 uint64_t rc = 0;
2035
2036 searchkey.obj_id = ed2->address;
2037 searchkey.obj_type = TYPE_EXTENT_ITEM;
2038 searchkey.offset = ed2->size;
2039
2040 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL);
2041 if (!NT_SUCCESS(Status)) {
2042 ERR("find_item returned %08lx\n", Status);
2043 return false;
2044 }
2045
2046 if (keycmp(tp.item->key, searchkey)) {
2047 ERR("(%I64x,%x,%I64x) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
2048 return false;
2049 }
2050
2051 if (tp.item->size < sizeof(EXTENT_ITEM)) {
2052 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
2053 return false;
2054 }
2055
2056 ei = (EXTENT_ITEM*)tp.item->data;
2057
2058 if (tp.item->size > sizeof(EXTENT_ITEM)) {
2059 uint32_t len = tp.item->size - sizeof(EXTENT_ITEM);
2060 uint8_t* ptr = (uint8_t*)&ei[1];
2061
2062 while (len > 0) {
2063 uint8_t secttype = *ptr;
2064 ULONG sectlen = get_extent_data_len(secttype);
2065 uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
2066
2067 len--;
2068
2069 if (sectlen > len) {
2070 ERR("(%I64x,%x,%I64x): %x bytes left, expecting at least %lx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
2071 return false;
2072 }
2073
2074 if (sectlen == 0) {
2075 ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
2076 return false;
2077 }
2078
2079 rc += sectcount;
2080
2081 if (secttype == TYPE_EXTENT_DATA_REF) {
2082 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
2083
2084 if (try_clone_edr(context, se, sectedr))
2085 return true;
2086 }
2087
2088 len -= sectlen;
2089 ptr += sizeof(uint8_t) + sectlen;
2090 }
2091 }
2092
2093 if (rc >= ei->refcount)
2094 return false;
2095
2096 searchkey.obj_type = TYPE_EXTENT_DATA_REF;
2097 searchkey.offset = 0;
2098
2099 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL);
2100 if (!NT_SUCCESS(Status)) {
2101 ERR("find_item returned %08lx\n", Status);
2102 return false;
2103 }
2104
2105 while (true) {
2106 traverse_ptr next_tp;
2107
2108 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
2109 if (tp.item->size < sizeof(EXTENT_DATA_REF))
2110 ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF));
2111 else {
2113 return true;
2114 }
2115 } 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))
2116 break;
2117
2118 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
2119 tp = next_tp;
2120 else
2121 break;
2122 }
2123
2124 return false;
2125}
2126
2129
2130 if ((IsListEmpty(&context->lastinode.exts) && IsListEmpty(&context->lastinode.oldexts)) || context->lastinode.size == 0)
2131 return STATUS_SUCCESS;
2132
2133 if (context->parent) {
2134 Status = add_ext_holes(context->Vcb, &context->lastinode.exts, context->lastinode.size);
2135 if (!NT_SUCCESS(Status)) {
2136 ERR("add_ext_holes returned %08lx\n", Status);
2137 return Status;
2138 }
2139
2140 Status = add_ext_holes(context->Vcb, &context->lastinode.oldexts, context->lastinode.size);
2141 if (!NT_SUCCESS(Status)) {
2142 ERR("add_ext_holes returned %08lx\n", Status);
2143 return Status;
2144 }
2145
2147 if (!NT_SUCCESS(Status)) {
2148 ERR("sync_ext_cutoff_points returned %08lx\n", Status);
2149 return Status;
2150 }
2151 }
2152
2153 while (!IsListEmpty(&context->lastinode.exts)) {
2155 send_ext* se2 = context->parent ? CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry) : NULL;
2156 ULONG pos;
2157 EXTENT_DATA2* ed2;
2158
2159 if (se2) {
2160 if (se->data.type == EXTENT_TYPE_INLINE && se2->data.type == EXTENT_TYPE_INLINE &&
2162 ExFreePool(se);
2163 ExFreePool(se2);
2164 continue;
2165 }
2166
2167 if (se->data.type == EXTENT_TYPE_REGULAR && se2->data.type == EXTENT_TYPE_REGULAR) {
2168 EXTENT_DATA2 *ed2a, *ed2b;
2169
2170 ed2a = (EXTENT_DATA2*)se->data.data;
2171 ed2b = (EXTENT_DATA2*)se2->data.data;
2172
2173 if (RtlCompareMemory(ed2a, ed2b, sizeof(EXTENT_DATA2)) == sizeof(EXTENT_DATA2)) {
2174 ExFreePool(se);
2175 ExFreePool(se2);
2176 continue;
2177 }
2178 }
2179 }
2180
2181 if (se->data.type == EXTENT_TYPE_INLINE) {
2182 pos = context->datalen;
2183
2185
2186 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2188
2192 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]);
2193
2195 RtlZeroMemory(&context->data[context->datalen - se->data.decoded_size], (ULONG)se->data.decoded_size);
2196
2198 Status = zlib_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size);
2199 if (!NT_SUCCESS(Status)) {
2200 ERR("zlib_decompress returned %08lx\n", Status);
2201 ExFreePool(se);
2202 if (se2) ExFreePool(se2);
2203 return Status;
2204 }
2205 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) {
2206 if (inlen < sizeof(uint32_t)) {
2207 ERR("extent data was truncated\n");
2208 ExFreePool(se);
2209 if (se2) ExFreePool(se2);
2210 return STATUS_INTERNAL_ERROR;
2211 } else
2212 inlen -= sizeof(uint32_t);
2213
2214 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));
2215 if (!NT_SUCCESS(Status)) {
2216 ERR("lzo_decompress returned %08lx\n", Status);
2217 ExFreePool(se);
2218 if (se2) ExFreePool(se2);
2219 return Status;
2220 }
2221 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
2222 Status = zstd_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size);
2223 if (!NT_SUCCESS(Status)) {
2224 ERR("zlib_decompress returned %08lx\n", Status);
2225 ExFreePool(se);
2226 if (se2) ExFreePool(se2);
2227 return Status;
2228 }
2229 }
2230 } else {
2231 ERR("unhandled compression type %x\n", se->data.compression);
2232 ExFreePool(se);
2233 if (se2) ExFreePool(se2);
2235 }
2236
2238
2239 ExFreePool(se);
2240 if (se2) ExFreePool(se2);
2241 continue;
2242 }
2243
2244 ed2 = (EXTENT_DATA2*)se->data.data;
2245
2246 if (ed2->size != 0 && (context->parent || context->num_clones > 0)) {
2247 if (try_clone(context, se)) {
2248 ExFreePool(se);
2249 if (se2) ExFreePool(se2);
2250 continue;
2251 }
2252 }
2253
2254 if (ed2->size == 0) { // write sparse
2255 uint64_t off, offset;
2256
2257 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2258 uint16_t length = (uint16_t)min(min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE), context->lastinode.size - se->offset - off);
2259
2260 if (context->datalen > SEND_BUFFER_LENGTH) {
2261 Status = wait_for_flush(context, tp1, tp2);
2262 if (!NT_SUCCESS(Status)) {
2263 ERR("wait_for_flush returned %08lx\n", Status);
2264 ExFreePool(se);
2265 if (se2) ExFreePool(se2);
2266 return Status;
2267 }
2268
2269 if (context->send->cancelling) {
2270 ExFreePool(se);
2271 if (se2) ExFreePool(se2);
2272 return STATUS_SUCCESS;
2273 }
2274 }
2275
2276 pos = context->datalen;
2277
2279
2280 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2281
2282 offset = se->offset + off;
2284
2286 RtlZeroMemory(&context->data[context->datalen - length], length);
2287
2289 }
2290 } else if (se->data.compression == BTRFS_COMPRESSION_NONE) {
2291 uint64_t off, offset;
2292 uint8_t* buf;
2293
2294 buf = ExAllocatePoolWithTag(NonPagedPool, MAX_SEND_WRITE + (2 * context->Vcb->superblock.sector_size), ALLOC_TAG);
2295 if (!buf) {
2296 ERR("out of memory\n");
2297 ExFreePool(se);
2298 if (se2) ExFreePool(se2);
2300 }
2301
2302 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2303 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2304 ULONG skip_start;
2305 uint64_t addr = ed2->address + off;
2306 void* csum;
2307
2308 if (context->datalen > SEND_BUFFER_LENGTH) {
2309 Status = wait_for_flush(context, tp1, tp2);
2310 if (!NT_SUCCESS(Status)) {
2311 ERR("wait_for_flush returned %08lx\n", Status);
2312 ExFreePool(buf);
2313 ExFreePool(se);
2314 if (se2) ExFreePool(se2);
2315 return Status;
2316 }
2317
2318 if (context->send->cancelling) {
2319 ExFreePool(buf);
2320 ExFreePool(se);
2321 if (se2) ExFreePool(se2);
2322 return STATUS_SUCCESS;
2323 }
2324 }
2325
2326 skip_start = addr & (context->Vcb->superblock.sector_size - 1);
2327 addr -= skip_start;
2328
2329 if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2330 csum = NULL;
2331 else {
2332 uint32_t len;
2333
2334 len = (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size) >> context->Vcb->sector_shift;
2335
2337 if (!csum) {
2338 ERR("out of memory\n");
2339 ExFreePool(buf);
2340 ExFreePool(se);
2341 if (se2) ExFreePool(se2);
2343 }
2344
2345 Status = load_csum(context->Vcb, csum, addr, len, NULL);
2346 if (!NT_SUCCESS(Status)) {
2347 ERR("load_csum returned %08lx\n", Status);
2349 ExFreePool(buf);
2350 ExFreePool(se);
2351 if (se2) ExFreePool(se2);
2353 }
2354 }
2355
2356 Status = read_data(context->Vcb, addr, (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size),
2357 csum, false, buf, NULL, NULL, NULL, 0, false, NormalPagePriority);
2358 if (!NT_SUCCESS(Status)) {
2359 ERR("read_data returned %08lx\n", Status);
2360 ExFreePool(buf);
2361 ExFreePool(se);
2362 if (se2) ExFreePool(se2);
2363 if (csum) ExFreePool(csum);
2364 return Status;
2365 }
2366
2367 if (csum)
2369
2370 pos = context->datalen;
2371
2373
2374 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2375
2376 offset = se->offset + off;
2378
2379 length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2381
2383 }
2384
2385 ExFreePool(buf);
2386 } else {
2387 uint8_t *buf, *compbuf;
2388 uint64_t off;
2389 void* csum;
2390
2392 if (!buf) {
2393 ERR("out of memory\n");
2394 ExFreePool(se);
2395 if (se2) ExFreePool(se2);
2397 }
2398
2400 if (!compbuf) {
2401 ERR("out of memory\n");
2402 ExFreePool(buf);
2403 ExFreePool(se);
2404 if (se2) ExFreePool(se2);
2406 }
2407
2408 if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2409 csum = NULL;
2410 else {
2411 uint32_t len;
2412
2413 len = (uint32_t)(ed2->size >> context->Vcb->sector_shift);
2414
2416 if (!csum) {
2417 ERR("out of memory\n");
2418 ExFreePool(compbuf);
2419 ExFreePool(buf);
2420 ExFreePool(se);
2421 if (se2) ExFreePool(se2);
2423 }
2424
2425 Status = load_csum(context->Vcb, csum, ed2->address, len, NULL);
2426 if (!NT_SUCCESS(Status)) {
2427 ERR("load_csum returned %08lx\n", Status);
2429 ExFreePool(compbuf);
2430 ExFreePool(buf);
2431 ExFreePool(se);
2432 if (se2) ExFreePool(se2);
2433 return Status;
2434 }
2435 }
2436
2437 Status = read_data(context->Vcb, ed2->address, (uint32_t)ed2->size, csum, false, compbuf, NULL, NULL, NULL, 0, false, NormalPagePriority);
2438 if (!NT_SUCCESS(Status)) {
2439 ERR("read_data returned %08lx\n", Status);
2440 ExFreePool(compbuf);
2441 ExFreePool(buf);
2442 ExFreePool(se);
2443 if (se2) ExFreePool(se2);
2444 if (csum) ExFreePool(csum);
2445 return Status;
2446 }
2447
2448 if (csum)
2450
2453 if (!NT_SUCCESS(Status)) {
2454 ERR("zlib_decompress returned %08lx\n", Status);
2455 ExFreePool(compbuf);
2456 ExFreePool(buf);
2457 ExFreePool(se);
2458 if (se2) ExFreePool(se2);
2459 return Status;
2460 }
2461 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) {
2462 Status = lzo_decompress(&compbuf[sizeof(uint32_t)], (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size, sizeof(uint32_t));
2463 if (!NT_SUCCESS(Status)) {
2464 ERR("lzo_decompress returned %08lx\n", Status);
2465 ExFreePool(compbuf);
2466 ExFreePool(buf);
2467 ExFreePool(se);
2468 if (se2) ExFreePool(se2);
2469 return Status;
2470 }
2471 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
2473 if (!NT_SUCCESS(Status)) {
2474 ERR("zstd_decompress returned %08lx\n", Status);
2475 ExFreePool(compbuf);
2476 ExFreePool(buf);
2477 ExFreePool(se);
2478 if (se2) ExFreePool(se2);
2479 return Status;
2480 }
2481 }
2482
2483 ExFreePool(compbuf);
2484
2485 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2486 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2488
2489 if (context->datalen > SEND_BUFFER_LENGTH) {
2490 Status = wait_for_flush(context, tp1, tp2);
2491 if (!NT_SUCCESS(Status)) {
2492 ERR("wait_for_flush returned %08lx\n", Status);
2493 ExFreePool(buf);
2494 ExFreePool(se);
2495 if (se2) ExFreePool(se2);
2496 return Status;
2497 }
2498
2499 if (context->send->cancelling) {
2500 ExFreePool(buf);
2501 ExFreePool(se);
2502 if (se2) ExFreePool(se2);
2503 return STATUS_SUCCESS;
2504 }
2505 }
2506
2507 pos = context->datalen;
2508
2510
2511 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2512
2513 offset = se->offset + off;
2515
2516 length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2518
2520 }
2521
2522 ExFreePool(buf);
2523 }
2524
2525 ExFreePool(se);
2526 if (se2) ExFreePool(se2);
2527 }
2528
2529 return STATUS_SUCCESS;
2530}
2531
2533 LIST_ENTRY* le;
2534
2535 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2536 NTSTATUS Status = flush_refs(context, tp1, tp2);
2537 if (!NT_SUCCESS(Status)) {
2538 ERR("flush_refs returned %08lx\n", Status);
2539 return Status;
2540 }
2541
2542 if (context->send->cancelling)
2543 return STATUS_SUCCESS;
2544 }
2545
2546 if (!context->lastinode.deleting) {
2547 if (context->lastinode.file) {
2548 NTSTATUS Status = flush_extents(context, tp1, tp2);
2549 if (!NT_SUCCESS(Status)) {
2550 ERR("flush_extents returned %08lx\n", Status);
2551 return Status;
2552 }
2553
2554 if (context->send->cancelling)
2555 return STATUS_SUCCESS;
2556
2557 send_truncate_command(context, context->lastinode.path, context->lastinode.size);
2558 }
2559
2560 if (context->lastinode.new || context->lastinode.uid != context->lastinode.olduid || context->lastinode.gid != context->lastinode.oldgid)
2561 send_chown_command(context, context->lastinode.path, context->lastinode.uid, context->lastinode.gid);
2562
2563 if (((context->lastinode.mode & __S_IFLNK) != __S_IFLNK || ((context->lastinode.mode & 07777) != 0777)) &&
2564 (context->lastinode.new || context->lastinode.mode != context->lastinode.oldmode))
2565 send_chmod_command(context, context->lastinode.path, context->lastinode.mode);
2566
2567 send_utimes_command(context, context->lastinode.path, &context->lastinode.atime, &context->lastinode.mtime, &context->lastinode.ctime);
2568 }
2569
2570 while (!IsListEmpty(&context->lastinode.exts)) {
2572 }
2573
2574 while (!IsListEmpty(&context->lastinode.oldexts)) {
2576 }
2577
2578 if (context->parent) {
2579 le = context->pending_rmdirs.Flink;
2580
2581 while (le != &context->pending_rmdirs) {
2583
2584 if (pr->last_child_inode <= context->lastinode.inode) {
2585 le = le->Flink;
2586
2588
2590
2591 if (pr->sd->name)
2592 ExFreePool(pr->sd->name);
2593
2594 while (!IsListEmpty(&pr->sd->deleted_children)) {
2596 ExFreePool(dc);
2597 }
2598
2599 ExFreePool(pr->sd);
2600
2602 ExFreePool(pr);
2603 } else
2604 break;
2605 }
2606 }
2607
2608 context->lastinode.inode = 0;
2609 context->lastinode.o = NULL;
2610
2611 if (context->lastinode.path) {
2612 ExFreePool(context->lastinode.path);
2613 context->lastinode.path = NULL;
2614 }
2615
2616 return STATUS_SUCCESS;
2617}
2618
2621
2622 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2623 return STATUS_SUCCESS;
2624
2625 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2626 Status = flush_refs(context, tp, tp2);
2627 if (!NT_SUCCESS(Status)) {
2628 ERR("flush_refs returned %08lx\n", Status);
2629 return Status;
2630 }
2631
2632 if (context->send->cancelling)
2633 return STATUS_SUCCESS;
2634 }
2635
2636 if ((context->lastinode.mode & __S_IFLNK) == __S_IFLNK)
2637 return STATUS_SUCCESS;
2638
2639 if (tp) {
2640 EXTENT_DATA* ed;
2641 EXTENT_DATA2* ed2 = NULL;
2642
2643 if (tp->item->size < sizeof(EXTENT_DATA)) {
2644 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2645 tp->item->size, sizeof(EXTENT_DATA));
2646 return STATUS_INTERNAL_ERROR;
2647 }
2648
2649 ed = (EXTENT_DATA*)tp->item->data;
2650
2652 ERR("unknown encryption type %u\n", ed->encryption);
2653 return STATUS_INTERNAL_ERROR;
2654 }
2655
2656 if (ed->encoding != BTRFS_ENCODING_NONE) {
2657 ERR("unknown encoding type %u\n", ed->encoding);
2658 return STATUS_INTERNAL_ERROR;
2659 }
2660
2663 ERR("unknown compression type %u\n", ed->compression);
2664 return STATUS_INTERNAL_ERROR;
2665 }
2666
2667 if (ed->type == EXTENT_TYPE_REGULAR) {
2668 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2669 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2670 tp->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2671 return STATUS_INTERNAL_ERROR;
2672 }
2673
2674 ed2 = (EXTENT_DATA2*)ed->data;
2675 } else if (ed->type == EXTENT_TYPE_INLINE) {
2677 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2679 return STATUS_INTERNAL_ERROR;
2680 }
2681 }
2682
2683 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2685
2686 if (!se) {
2687 ERR("out of memory\n");
2689 }
2690
2691 se->offset = tp->item->key.offset;
2692 se->datalen = tp->item->size;
2693 RtlCopyMemory(&se->data, tp->item->data, tp->item->size);
2694 InsertTailList(&context->lastinode.exts, &se->list_entry);
2695 }
2696 }
2697
2698 if (tp2) {
2699 EXTENT_DATA* ed;
2700 EXTENT_DATA2* ed2 = NULL;
2701
2702 if (tp2->item->size < sizeof(EXTENT_DATA)) {
2703 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2704 tp2->item->size, sizeof(EXTENT_DATA));
2705 return STATUS_INTERNAL_ERROR;
2706 }
2707
2708 ed = (EXTENT_DATA*)tp2->item->data;
2709
2711 ERR("unknown encryption type %u\n", ed->encryption);
2712 return STATUS_INTERNAL_ERROR;
2713 }
2714
2715 if (ed->encoding != BTRFS_ENCODING_NONE) {
2716 ERR("unknown encoding type %u\n", ed->encoding);
2717 return STATUS_INTERNAL_ERROR;
2718 }
2719
2722 ERR("unknown compression type %u\n", ed->compression);
2723 return STATUS_INTERNAL_ERROR;
2724 }
2725
2726 if (ed->type == EXTENT_TYPE_REGULAR) {
2727 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2728 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2729 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2730 return STATUS_INTERNAL_ERROR;
2731 }
2732
2733 ed2 = (EXTENT_DATA2*)ed->data;
2734 } else if (ed->type == EXTENT_TYPE_INLINE) {
2735 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) {
2736 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2737 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
2738 return STATUS_INTERNAL_ERROR;
2739 }
2740 }
2741
2742 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2744
2745 if (!se) {
2746 ERR("out of memory\n");
2748 }
2749
2750 se->offset = tp2->item->key.offset;
2751 se->datalen = tp2->item->size;
2752 RtlCopyMemory(&se->data, tp2->item->data, tp2->item->size);
2753 InsertTailList(&context->lastinode.oldexts, &se->list_entry);
2754 }
2755 }
2756
2757 return STATUS_SUCCESS;
2758}
2759
2760typedef struct {
2762 char* name;
2764 char* value1;
2766 char* value2;
2768} xattr_cmp;
2769
2771 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2772 return STATUS_SUCCESS;
2773
2774 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2776 if (!NT_SUCCESS(Status)) {
2777 ERR("flush_refs returned %08lx\n", Status);
2778 return Status;
2779 }
2780
2781 if (context->send->cancelling)
2782 return STATUS_SUCCESS;
2783 }
2784
2785 if (tp && tp->item->size < sizeof(DIR_ITEM)) {
2786 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2787 tp->item->size, sizeof(DIR_ITEM));
2788 return STATUS_INTERNAL_ERROR;
2789 }
2790
2791 if (tp2 && tp2->item->size < sizeof(DIR_ITEM)) {
2792 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2793 tp2->item->size, sizeof(DIR_ITEM));
2794 return STATUS_INTERNAL_ERROR;
2795 }
2796
2797 if (tp && !tp2) {
2798 ULONG len;
2799 DIR_ITEM* di;
2800
2801 len = tp->item->size;
2802 di = (DIR_ITEM*)tp->item->data;
2803
2804 do {
2805 ULONG pos;
2806
2807 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2808 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2809 return STATUS_INTERNAL_ERROR;
2810 }
2811
2812 pos = context->datalen;
2814 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2818
2819 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2820 di = (DIR_ITEM*)&di->name[di->m + di->n];
2821 } while (len > 0);
2822 } else if (!tp && tp2) {
2823 ULONG len;
2824 DIR_ITEM* di;
2825
2826 len = tp2->item->size;
2827 di = (DIR_ITEM*)tp2->item->data;
2828
2829 do {
2830 ULONG pos;
2831
2832 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2833 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2834 return STATUS_INTERNAL_ERROR;
2835 }
2836
2837 pos = context->datalen;
2839 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2842
2843 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2844 di = (DIR_ITEM*)&di->name[di->m + di->n];
2845 } while (len > 0);
2846 } else {
2847 ULONG len;
2848 DIR_ITEM* di;
2849 LIST_ENTRY xattrs;
2850
2851 InitializeListHead(&xattrs);
2852
2853 len = tp->item->size;
2854 di = (DIR_ITEM*)tp->item->data;
2855
2856 do {
2857 xattr_cmp* xa;
2858
2859 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2860 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2861 return STATUS_INTERNAL_ERROR;
2862 }
2863
2865 if (!xa) {
2866 ERR("out of memory\n");
2867
2868 while (!IsListEmpty(&xattrs)) {
2870 }
2871
2873 }
2874
2875 xa->namelen = di->n;
2876 xa->name = di->name;
2877 xa->value1len = di->m;
2878 xa->value1 = di->name + di->n;
2879 xa->value2len = 0;
2880 xa->value2 = NULL;
2881
2882 InsertTailList(&xattrs, &xa->list_entry);
2883
2884 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2885 di = (DIR_ITEM*)&di->name[di->m + di->n];
2886 } while (len > 0);
2887
2888 len = tp2->item->size;
2889 di = (DIR_ITEM*)tp2->item->data;
2890
2891 do {
2892 xattr_cmp* xa;
2893 LIST_ENTRY* le;
2894 bool found = false;
2895
2896 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2897 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2898 return STATUS_INTERNAL_ERROR;
2899 }
2900
2901 le = xattrs.Flink;
2902 while (le != &xattrs) {
2904
2905 if (xa->namelen == di->n && RtlCompareMemory(xa->name, di->name, di->n) == di->n) {
2906 xa->value2len = di->m;
2907 xa->value2 = di->name + di->n;
2908 found = true;
2909 break;
2910 }
2911
2912 le = le->Flink;
2913 }
2914
2915 if (!found) {
2917 if (!xa) {
2918 ERR("out of memory\n");
2919
2920 while (!IsListEmpty(&xattrs)) {
2922 }
2923
2925 }
2926
2927 xa->namelen = di->n;
2928 xa->name = di->name;
2929 xa->value1len = 0;
2930 xa->value1 = NULL;
2931 xa->value2len = di->m;
2932 xa->value2 = di->name + di->n;
2933
2934 InsertTailList(&xattrs, &xa->list_entry);
2935 }
2936
2937 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2938 di = (DIR_ITEM*)&di->name[di->m + di->n];
2939 } while (len > 0);
2940
2941 while (!IsListEmpty(&xattrs)) {
2943
2944 if (xa->value1len != xa->value2len || !xa->value1 || !xa->value2 || RtlCompareMemory(xa->value1, xa->value2, xa->value1len) != xa->value1len) {
2945 ULONG pos;
2946
2947 if (!xa->value1) {
2948 pos = context->datalen;
2950 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2953 } else {
2954 pos = context->datalen;
2956 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2960 }
2961 }
2962
2963 ExFreePool(xa);
2964 }
2965 }
2966
2967 return STATUS_SUCCESS;
2968}
2969
2970_Function_class_(KSTART_ROUTINE)
2971static void __stdcall send_thread(void* ctx) {
2974 KEY searchkey;
2975 traverse_ptr tp, tp2;
2976
2977 InterlockedIncrement(&context->root->send_ops);
2978
2979 if (context->parent)
2980 InterlockedIncrement(&context->parent->send_ops);
2981
2982 if (context->clones) {
2983 ULONG i;
2984
2985 for (i = 0; i < context->num_clones; i++) {
2986 InterlockedIncrement(&context->clones[i]->send_ops);
2987 }
2988 }
2989
2990 ExAcquireResourceExclusiveLite(&context->Vcb->tree_lock, true);
2991
2993
2994 if (context->parent)
2995 flush_subvol_fcbs(context->parent);
2996
2997 if (context->Vcb->need_write)
2998 Status = do_write(context->Vcb, NULL);
2999 else
3001
3002 free_trees(context->Vcb);
3003
3004 if (!NT_SUCCESS(Status)) {
3005 ERR("do_write returned %08lx\n", Status);
3006 ExReleaseResourceLite(&context->Vcb->tree_lock);
3007 goto end;
3008 }
3009
3010 ExConvertExclusiveToSharedLite(&context->Vcb->tree_lock);
3011
3012 searchkey.obj_id = searchkey.offset = 0;
3013 searchkey.obj_type = 0;
3014
3015 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
3016 if (!NT_SUCCESS(Status)) {
3017 ERR("find_item returned %08lx\n", Status);
3018 ExReleaseResourceLite(&context->Vcb->tree_lock);
3019 goto end;
3020 }
3021
3022 if (context->parent) {
3023 bool ended1 = false, ended2 = false;
3024 Status = find_item(context->Vcb, context->parent, &tp2, &searchkey, false, NULL);
3025 if (!NT_SUCCESS(Status)) {
3026 ERR("find_item returned %08lx\n", Status);
3027 ExReleaseResourceLite(&context->Vcb->tree_lock);
3028 goto end;
3029 }
3030
3031 do {
3032 traverse_ptr next_tp;
3033
3034 if (context->datalen > SEND_BUFFER_LENGTH) {
3035 KEY key1 = tp.item->key, key2 = tp2.item->key;
3036
3037 ExReleaseResourceLite(&context->Vcb->tree_lock);
3038
3039 KeClearEvent(&context->send->cleared_event);
3040 KeSetEvent(&context->buffer_event, 0, true);
3041 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
3042
3043 if (context->send->cancelling)
3044 goto end;
3045
3046 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true);
3047
3048 if (!ended1) {
3049 Status = find_item(context->Vcb, context->root, &tp, &key1, false, NULL);
3050 if (!NT_SUCCESS(Status)) {
3051 ERR("find_item returned %08lx\n", Status);
3052 ExReleaseResourceLite(&context->Vcb->tree_lock);
3053 goto end;
3054 }
3055
3056 if (keycmp(tp.item->key, key1)) {
3057 ERR("readonly subvolume changed\n");
3058 ExReleaseResourceLite(&context->Vcb->tree_lock);
3060 goto end;
3061 }
3062 }
3063
3064 if (!ended2) {
3065 Status = find_item(context->Vcb, context->parent, &tp2, &key2, false, NULL);
3066 if (!NT_SUCCESS(Status)) {
3067 ERR("find_item returned %08lx\n", Status);
3068 ExReleaseResourceLite(&context->Vcb->tree_lock);
3069 goto end;
3070 }
3071
3072 if (keycmp(tp2.item->key, key2)) {
3073 ERR("readonly subvolume changed\n");
3074 ExReleaseResourceLite(&context->Vcb->tree_lock);
3076 goto end;
3077 }
3078 }
3079 }
3080
3081 while (!ended1 && !ended2 && tp.tree->header.address == tp2.tree->header.address) {
3082 Status = skip_to_difference(context->Vcb, &tp, &tp2, &ended1, &ended2);
3083 if (!NT_SUCCESS(Status)) {
3084 ERR("skip_to_difference returned %08lx\n", Status);
3085 ExReleaseResourceLite(&context->Vcb->tree_lock);
3086 goto end;
3087 }
3088 }
3089
3090 if (!ended1 && !ended2 && !keycmp(tp.item->key, tp2.item->key)) {
3091 bool no_next = false, no_next2 = false;
3092
3093 TRACE("~ %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3094
3095 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3096 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3097 if (!NT_SUCCESS(Status)) {
3098 ERR("finish_inode returned %08lx\n", Status);
3099 ExReleaseResourceLite(&context->Vcb->tree_lock);
3100 goto end;
3101 }
3102
3103 if (context->send->cancelling) {
3104 ExReleaseResourceLite(&context->Vcb->tree_lock);
3105 goto end;
3106 }
3107 }
3108
3110 if (tp.item->size == tp2.item->size && tp.item->size > 0 && RtlCompareMemory(tp.item->data, tp2.item->data, tp.item->size) == tp.item->size) {
3112
3113 while (true) {
3114 if (!