[USETUP][SETUPLIB] Added support for formatting partition in BTRFS and installing...
[reactos.git] / drivers / filesystems / btrfs / send.c
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 inode;
23 BOOL dummy;
24 BTRFS_TIME atime;
25 BTRFS_TIME mtime;
26 BTRFS_TIME ctime;
27 struct send_dir* parent;
28 UINT16 namelen;
29 char* name;
30 LIST_ENTRY deleted_children;
31 } send_dir;
32
33 typedef struct {
34 LIST_ENTRY list_entry;
35 UINT64 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 namelen;
51 char name[1];
52 } ref;
53
54 typedef struct {
55 send_dir* sd;
56 UINT64 last_child_inode;
57 LIST_ENTRY list_entry;
58 } pending_rmdir;
59
60 typedef struct {
61 UINT64 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* 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 inode;
84 BOOL deleting;
85 BOOL new;
86 UINT64 gen;
87 UINT64 uid;
88 UINT64 olduid;
89 UINT64 gid;
90 UINT64 oldgid;
91 UINT64 mode;
92 UINT64 oldmode;
93 UINT64 size;
94 UINT64 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 dir, UINT64 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 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*)bsc, context->datalen - pos);
129 }
130
131 static void send_add_tlv(send_context* context, UINT16 type, void* data, UINT16 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 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 inode, UINT64 generation, char* name) {
164 char *ptr, *ptr2;
165 UINT64 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*)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 inode, char** link, UINT16* 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 (%llx,%x,%llx)\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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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)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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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 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)strlen(name));
435 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &tp->item->key.obj_id, sizeof(UINT64));
436
437 if (cmd == BTRFS_SEND_CMD_MKNOD || cmd == BTRFS_SEND_CMD_MKFIFO || cmd == BTRFS_SEND_CMD_MKSOCK) {
438 UINT64 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));
441 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(UINT64));
442 } else if (cmd == BTRFS_SEND_CMD_SYMLINK && ii->st_size > 0) {
443 char* link;
444 UINT16 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 inode, send_dir* parent, char* name, UINT16 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 find_path_len(send_dir* parent, UINT16 namelen) {
560 UINT16 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 type, send_dir* parent, char* name, UINT16 namelen) {
586 UINT16 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 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)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)strlen(context->lastinode.path) : 0);
611
612 send_command_finish(context, pos);
613 }
614
615 if (context->lastinode.o) {
616 UINT16 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 dir, UINT64 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("(%llx,%x,%llx) 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)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 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 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("(%llx,%x,%llx) 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));
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("(%llx,%x,%llx) 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)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 len;
874
875 if (tp->item->size < sizeof(INODE_EXTREF)) {
876 ERR("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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));
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)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));
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));
982 }
983
984 send_command_finish(context, pos);
985 }
986
987 static void send_chown_command(send_context* context, char* path, UINT64 uid, UINT64 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)strlen(path) : 0);
993 send_add_tlv(context, BTRFS_SEND_TLV_UID, &uid, sizeof(UINT64));
994 send_add_tlv(context, BTRFS_SEND_TLV_GID, &gid, sizeof(UINT64));
995
996 send_command_finish(context, pos);
997 }
998
999 static void send_chmod_command(send_context* context, char* path, UINT64 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)strlen(path) : 0);
1007 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(UINT64));
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)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 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)strlen(path) : 0);
1031 send_add_tlv(context, BTRFS_SEND_TLV_SIZE, &size, sizeof(UINT64));
1032
1033 send_command_finish(context, pos);
1034 }
1035
1036 static NTSTATUS send_unlink_command(send_context* context, send_dir* parent, UINT16 namelen, char* name) {
1037 ULONG pos = context->datalen;
1038 UINT16 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 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* 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("(%llx,%x,%llx) 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 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* inode, BOOL* dir) {
1133 NTSTATUS Status;
1134 KEY searchkey;
1135 traverse_ptr tp;
1136 DIR_ITEM* di;
1137 UINT16 len;
1138
1139 searchkey.obj_id = sd->inode;
1140 searchkey.obj_type = TYPE_DIR_ITEM;
1141 searchkey.offset = calc_crc32c(0xfffffffe, (UINT8*)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("(%llx,%x,%llx) 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)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 inode, BOOL dir, UINT64 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)strlen(name));
1233
1234 send_command_finish(context, pos);
1235
1236 if (sd->name)
1237 ExFreePool(sd->name);
1238
1239 sd->namelen = (UINT16)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)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 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)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 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)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)strlen(context->lastinode.path));
1404 send_add_tlv(context, BTRFS_SEND_TLV_PATH_TO, name, (UINT16)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)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 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 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(LIST_ENTRY* exts, UINT64 size) {
1652 UINT64 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 = size - lastoff;
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 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 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 inode) {
1841 NTSTATUS Status;
1842 KEY searchkey;
1843 traverse_ptr tp;
1844 UINT16 len = 0;
1845 UINT64 num;
1846 UINT8* 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 %llx\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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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 %llx\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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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 clone_offset = tp.item->key.offset + ed2->offset - seed2->offset;
1988 UINT64 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));
1996 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_LENGTH, &clone_len, sizeof(UINT64));
1997 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)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));
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));
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 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("(%llx,%x,%llx) 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("(%llx,%x,%llx) 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 len = tp.item->size - sizeof(EXTENT_ITEM);
2059 UINT8* ptr = (UINT8*)&ei[1];
2060
2061 while (len > 0) {
2062 UINT8 secttype = *ptr;
2063 ULONG sectlen = get_extent_data_len(secttype);
2064 UINT64 sectcount = get_extent_data_refcount(secttype, ptr + sizeof(UINT8));
2065
2066 len--;
2067
2068 if (sectlen > len) {
2069 ERR("(%llx,%x,%llx): %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("(%llx,%x,%llx): 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));
2082
2083 if (try_clone_edr(context, se, sectedr))
2084 return TRUE;
2085 }
2086
2087 len -= sectlen;
2088 ptr += sizeof(UINT8) + 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("(%llx,%x,%llx) 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->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->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)strlen(context->lastinode.path) : 0);
2186 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(UINT64));
2187
2188 if (se->data.compression == BTRFS_COMPRESSION_NONE)
2189 send_add_tlv(context, BTRFS_SEND_TLV_DATA, se->data.data, (UINT16)se->data.decoded_size);
2190 else if (se->data.compression == BTRFS_COMPRESSION_ZLIB || se->data.compression == BTRFS_COMPRESSION_LZO) {
2191 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]);
2192
2193 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, (UINT16)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)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)) {
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);
2212
2213 Status = lzo_decompress(se->data.data + sizeof(UINT32), inlen, &context->data[context->datalen - se->data.decoded_size], (UINT32)se->data.decoded_size, sizeof(UINT32));
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 }
2221 } else {
2222 ERR("unhandled compression type %x\n", se->data.compression);
2223 ExFreePool(se);
2224 if (se2) ExFreePool(se2);
2225 return STATUS_NOT_IMPLEMENTED;
2226 }
2227
2228 send_command_finish(context, pos);
2229
2230 ExFreePool(se);
2231 if (se2) ExFreePool(se2);
2232 continue;
2233 }
2234
2235 ed2 = (EXTENT_DATA2*)se->data.data;
2236
2237 if (ed2->size != 0 && (context->parent || context->num_clones > 0)) {
2238 if (try_clone(context, se)) {
2239 ExFreePool(se);
2240 if (se2) ExFreePool(se2);
2241 continue;
2242 }
2243 }
2244
2245 if (ed2->size == 0) { // write sparse
2246 UINT64 off, offset;
2247
2248 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2249 UINT16 length = (UINT16)min(min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE), context->lastinode.size - se->offset - off);
2250
2251 if (context->datalen > SEND_BUFFER_LENGTH) {
2252 Status = wait_for_flush(context, tp1, tp2);
2253 if (!NT_SUCCESS(Status)) {
2254 ERR("wait_for_flush returned %08x\n", Status);
2255 ExFreePool(se);
2256 if (se2) ExFreePool(se2);
2257 return Status;
2258 }
2259
2260 if (context->send->cancelling) {
2261 ExFreePool(se);
2262 if (se2) ExFreePool(se2);
2263 return STATUS_SUCCESS;
2264 }
2265 }
2266
2267 pos = context->datalen;
2268
2269 send_command(context, BTRFS_SEND_CMD_WRITE);
2270
2271 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2272
2273 offset = se->offset + off;
2274 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(UINT64));
2275
2276 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, length);
2277 RtlZeroMemory(&context->data[context->datalen - length], length);
2278
2279 send_command_finish(context, pos);
2280 }
2281 } else if (se->data.compression == BTRFS_COMPRESSION_NONE) {
2282 UINT64 off, offset;
2283 UINT8* buf;
2284
2285 buf = ExAllocatePoolWithTag(NonPagedPool, MAX_SEND_WRITE + (2 * context->Vcb->superblock.sector_size), ALLOC_TAG);
2286 if (!buf) {
2287 ERR("out of memory\n");
2288 ExFreePool(se);
2289 if (se2) ExFreePool(se2);
2290 return STATUS_INSUFFICIENT_RESOURCES;
2291 }
2292
2293 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2294 UINT16 length = (UINT16)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2295 ULONG skip_start;
2296 UINT64 addr = ed2->address + off;
2297 UINT32* csum;
2298
2299 if (context->datalen > SEND_BUFFER_LENGTH) {
2300 Status = wait_for_flush(context, tp1, tp2);
2301 if (!NT_SUCCESS(Status)) {
2302 ERR("wait_for_flush returned %08x\n", Status);
2303 ExFreePool(buf);
2304 ExFreePool(se);
2305 if (se2) ExFreePool(se2);
2306 return Status;
2307 }
2308
2309 if (context->send->cancelling) {
2310 ExFreePool(buf);
2311 ExFreePool(se);
2312 if (se2) ExFreePool(se2);
2313 return STATUS_SUCCESS;
2314 }
2315 }
2316
2317 skip_start = addr % context->Vcb->superblock.sector_size;
2318 addr -= skip_start;
2319
2320 if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2321 csum = NULL;
2322 else {
2323 UINT32 len;
2324
2325 len = (UINT32)sector_align(length + skip_start, context->Vcb->superblock.sector_size) / context->Vcb->superblock.sector_size;
2326
2327 csum = ExAllocatePoolWithTag(PagedPool, len * sizeof(UINT32), ALLOC_TAG);
2328 if (!csum) {
2329 ERR("out of memory\n");
2330 ExFreePool(buf);
2331 ExFreePool(se);
2332 if (se2) ExFreePool(se2);
2333 return STATUS_INSUFFICIENT_RESOURCES;
2334 }
2335
2336 Status = load_csum(context->Vcb, csum, addr, len, NULL);
2337 if (!NT_SUCCESS(Status)) {
2338 ERR("load_csum returned %08x\n", Status);
2339 ExFreePool(csum);
2340 ExFreePool(buf);
2341 ExFreePool(se);
2342 if (se2) ExFreePool(se2);
2343 return STATUS_INSUFFICIENT_RESOURCES;
2344 }
2345 }
2346
2347 Status = read_data(context->Vcb, addr, (UINT32)sector_align(length + skip_start, context->Vcb->superblock.sector_size),
2348 csum, FALSE, buf, NULL, NULL, NULL, 0, FALSE, NormalPagePriority);
2349 if (!NT_SUCCESS(Status)) {
2350 ERR("read_data returned %08x\n", Status);
2351 ExFreePool(buf);
2352 ExFreePool(se);
2353 if (se2) ExFreePool(se2);
2354 if (csum) ExFreePool(csum);
2355 return Status;
2356 }
2357
2358 if (csum)
2359 ExFreePool(csum);
2360
2361 pos = context->datalen;
2362
2363 send_command(context, BTRFS_SEND_CMD_WRITE);
2364
2365 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2366
2367 offset = se->offset + off;
2368 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(UINT64));
2369
2370 length = min((UINT16)(context->lastinode.size - se->offset - off), length);
2371 send_add_tlv(context, BTRFS_SEND_TLV_DATA, buf + skip_start, length);
2372
2373 send_command_finish(context, pos);
2374 }
2375
2376 ExFreePool(buf);
2377 } else {
2378 UINT8 *buf, *compbuf;
2379 UINT64 off;
2380 UINT32* csum;
2381
2382 buf = ExAllocatePoolWithTag(PagedPool, (ULONG)se->data.decoded_size, ALLOC_TAG);
2383 if (!buf) {
2384 ERR("out of memory\n");
2385 ExFreePool(se);
2386 if (se2) ExFreePool(se2);
2387 return STATUS_INSUFFICIENT_RESOURCES;
2388 }
2389
2390 compbuf = ExAllocatePoolWithTag(PagedPool, (ULONG)ed2->size, ALLOC_TAG);
2391 if (!compbuf) {
2392 ERR("out of memory\n");
2393 ExFreePool(buf);
2394 ExFreePool(se);
2395 if (se2) ExFreePool(se2);
2396 return STATUS_INSUFFICIENT_RESOURCES;
2397 }
2398
2399 if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2400 csum = NULL;
2401 else {
2402 UINT32 len;
2403
2404 len = (UINT32)(ed2->size / context->Vcb->superblock.sector_size);
2405
2406 csum = ExAllocatePoolWithTag(PagedPool, len * sizeof(UINT32), ALLOC_TAG);
2407 if (!csum) {
2408 ERR("out of memory\n");
2409 ExFreePool(compbuf);
2410 ExFreePool(buf);
2411 ExFreePool(se);
2412 if (se2) ExFreePool(se2);
2413 return STATUS_INSUFFICIENT_RESOURCES;
2414 }
2415
2416 Status = load_csum(context->Vcb, csum, ed2->address, len, NULL);
2417 if (!NT_SUCCESS(Status)) {
2418 ERR("load_csum returned %08x\n", Status);
2419 ExFreePool(csum);
2420 ExFreePool(compbuf);
2421 ExFreePool(buf);
2422 ExFreePool(se);
2423 if (se2) ExFreePool(se2);
2424 return Status;
2425 }
2426 }
2427
2428 Status = read_data(context->Vcb, ed2->address, (UINT32)ed2->size, csum, FALSE, compbuf, NULL, NULL, NULL, 0, FALSE, NormalPagePriority);
2429 if (!NT_SUCCESS(Status)) {
2430 ERR("read_data returned %08x\n", Status);
2431 ExFreePool(compbuf);
2432 ExFreePool(buf);
2433 ExFreePool(se);
2434 if (se2) ExFreePool(se2);
2435 if (csum) ExFreePool(csum);
2436 return Status;
2437 }
2438
2439 if (csum)
2440 ExFreePool(csum);
2441
2442 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) {
2443 Status = zlib_decompress(compbuf, (UINT32)ed2->size, buf, (UINT32)se->data.decoded_size);
2444 if (!NT_SUCCESS(Status)) {
2445 ERR("zlib_decompress returned %08x\n", Status);
2446 ExFreePool(compbuf);
2447 ExFreePool(buf);
2448 ExFreePool(se);
2449 if (se2) ExFreePool(se2);
2450 return Status;
2451 }
2452 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) {
2453 Status = lzo_decompress(&compbuf[sizeof(UINT32)], (UINT32)ed2->size, buf, (UINT32)se->data.decoded_size, sizeof(UINT32));
2454 if (!NT_SUCCESS(Status)) {
2455 ERR("lzo_decompress returned %08x\n", Status);
2456 ExFreePool(compbuf);
2457 ExFreePool(buf);
2458 ExFreePool(se);
2459 if (se2) ExFreePool(se2);
2460 return Status;
2461 }
2462 }
2463
2464 ExFreePool(compbuf);
2465
2466 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2467 UINT16 length = (UINT16)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2468 UINT64 offset;
2469
2470 if (context->datalen > SEND_BUFFER_LENGTH) {
2471 Status = wait_for_flush(context, tp1, tp2);
2472 if (!NT_SUCCESS(Status)) {
2473 ERR("wait_for_flush returned %08x\n", Status);
2474 ExFreePool(buf);
2475 ExFreePool(se);
2476 if (se2) ExFreePool(se2);
2477 return Status;
2478 }
2479
2480 if (context->send->cancelling) {
2481 ExFreePool(buf);
2482 ExFreePool(se);
2483 if (se2) ExFreePool(se2);
2484 return STATUS_SUCCESS;
2485 }
2486 }
2487
2488 pos = context->datalen;
2489
2490 send_command(context, BTRFS_SEND_CMD_WRITE);
2491
2492 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2493
2494 offset = se->offset + off;
2495 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(UINT64));
2496
2497 length = min((UINT16)(context->lastinode.size - se->offset - off), length);
2498 send_add_tlv(context, BTRFS_SEND_TLV_DATA, &buf[off], length);
2499
2500 send_command_finish(context, pos);
2501 }
2502
2503 ExFreePool(buf);
2504 }
2505
2506 ExFreePool(se);
2507 if (se2) ExFreePool(se2);
2508 }
2509
2510 return STATUS_SUCCESS;
2511 }
2512
2513 static NTSTATUS finish_inode(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) {
2514 LIST_ENTRY* le;
2515
2516 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2517 NTSTATUS Status = flush_refs(context, tp1, tp2);
2518 if (!NT_SUCCESS(Status)) {
2519 ERR("flush_refs returned %08x\n", Status);
2520 return Status;
2521 }
2522
2523 if (context->send->cancelling)
2524 return STATUS_SUCCESS;
2525 }
2526
2527 if (!context->lastinode.deleting) {
2528 if (context->lastinode.file) {
2529 NTSTATUS Status = flush_extents(context, tp1, tp2);
2530 if (!NT_SUCCESS(Status)) {
2531 ERR("flush_extents returned %08x\n", Status);
2532 return Status;
2533 }
2534
2535 if (context->send->cancelling)
2536 return STATUS_SUCCESS;
2537
2538 send_truncate_command(context, context->lastinode.path, context->lastinode.size);
2539 }
2540
2541 if (context->lastinode.new || context->lastinode.uid != context->lastinode.olduid || context->lastinode.gid != context->lastinode.oldgid)
2542 send_chown_command(context, context->lastinode.path, context->lastinode.uid, context->lastinode.gid);
2543
2544 if (((context->lastinode.mode & __S_IFLNK) != __S_IFLNK || ((context->lastinode.mode & 07777) != 0777)) &&
2545 (context->lastinode.new || context->lastinode.mode != context->lastinode.oldmode))
2546 send_chmod_command(context, context->lastinode.path, context->lastinode.mode);
2547
2548 send_utimes_command(context, context->lastinode.path, &context->lastinode.atime, &context->lastinode.mtime, &context->lastinode.ctime);
2549 }
2550
2551 while (!IsListEmpty(&context->lastinode.exts)) {
2552 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry));
2553 }
2554
2555 while (!IsListEmpty(&context->lastinode.oldexts)) {
2556 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry));
2557 }
2558
2559 if (context->parent) {
2560 le = context->pending_rmdirs.Flink;
2561
2562 while (le != &context->pending_rmdirs) {
2563 pending_rmdir* pr = CONTAINING_RECORD(le, pending_rmdir, list_entry);
2564
2565 if (pr->last_child_inode <= context->lastinode.inode) {
2566 le = le->Flink;
2567
2568 send_rmdir_command(context, pr->sd->namelen, pr->sd->name);
2569
2570 RemoveEntryList(&pr->sd->list_entry);
2571
2572 if (pr->sd->name)
2573 ExFreePool(pr->sd->name);
2574
2575 while (!IsListEmpty(&pr->sd->deleted_children)) {
2576 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&pr->sd->deleted_children), deleted_child, list_entry);
2577 ExFreePool(dc);
2578 }
2579
2580 ExFreePool(pr->sd);
2581
2582 RemoveEntryList(&pr->list_entry);
2583 ExFreePool(pr);
2584 } else
2585 break;
2586 }
2587 }
2588
2589 context->lastinode.inode = 0;
2590 context->lastinode.o = NULL;
2591
2592 if (context->lastinode.path) {
2593 ExFreePool(context->lastinode.path);
2594 context->lastinode.path = NULL;
2595 }
2596
2597 return STATUS_SUCCESS;
2598 }
2599
2600 static NTSTATUS send_extent_data(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) {
2601 NTSTATUS Status;
2602
2603 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2604 return STATUS_SUCCESS;
2605
2606 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2607 Status = flush_refs(context, tp, tp2);
2608 if (!NT_SUCCESS(Status)) {
2609 ERR("flush_refs returned %08x\n", Status);
2610 return Status;
2611 }
2612
2613 if (context->send->cancelling)
2614 return STATUS_SUCCESS;
2615 }
2616
2617 if ((context->lastinode.mode & __S_IFLNK) == __S_IFLNK)
2618 return STATUS_SUCCESS;
2619
2620 if (tp) {
2621 EXTENT_DATA* ed;
2622 EXTENT_DATA2* ed2 = NULL;
2623
2624 if (tp->item->size < sizeof(EXTENT_DATA)) {
2625 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2626 tp->item->size, sizeof(EXTENT_DATA));
2627 return STATUS_INTERNAL_ERROR;
2628 }
2629
2630 ed = (EXTENT_DATA*)tp->item->data;
2631
2632 if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
2633 ERR("unknown encryption type %u\n", ed->encryption);
2634 return STATUS_INTERNAL_ERROR;
2635 }
2636
2637 if (ed->encoding != BTRFS_ENCODING_NONE) {
2638 ERR("unknown encoding type %u\n", ed->encoding);
2639 return STATUS_INTERNAL_ERROR;
2640 }
2641
2642 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && ed->compression != BTRFS_COMPRESSION_LZO) {
2643 ERR("unknown compression type %u\n", ed->compression);
2644 return STATUS_INTERNAL_ERROR;
2645 }
2646
2647 if (ed->type == EXTENT_TYPE_REGULAR) {
2648 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2649 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2650 tp->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2651 return STATUS_INTERNAL_ERROR;
2652 }
2653
2654 ed2 = (EXTENT_DATA2*)ed->data;
2655 } else if (ed->type == EXTENT_TYPE_INLINE) {
2656 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size && ed->compression == BTRFS_COMPRESSION_NONE) {
2657 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2658 tp->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
2659 return STATUS_INTERNAL_ERROR;
2660 }
2661 }
2662
2663 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2664 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp->item->size, ALLOC_TAG);
2665
2666 if (!se) {
2667 ERR("out of memory\n");
2668 return STATUS_INSUFFICIENT_RESOURCES;
2669 }
2670
2671 se->offset = tp->item->key.offset;
2672 se->datalen = tp->item->size;
2673 RtlCopyMemory(&se->data, tp->item->data, tp->item->size);
2674 InsertTailList(&context->lastinode.exts, &se->list_entry);
2675 }
2676 }
2677
2678 if (tp2) {
2679 EXTENT_DATA* ed;
2680 EXTENT_DATA2* ed2 = NULL;
2681
2682 if (tp2->item->size < sizeof(EXTENT_DATA)) {
2683 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2684 tp2->item->size, sizeof(EXTENT_DATA));
2685 return STATUS_INTERNAL_ERROR;
2686 }
2687
2688 ed = (EXTENT_DATA*)tp2->item->data;
2689
2690 if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
2691 ERR("unknown encryption type %u\n", ed->encryption);
2692 return STATUS_INTERNAL_ERROR;
2693 }
2694
2695 if (ed->encoding != BTRFS_ENCODING_NONE) {
2696 ERR("unknown encoding type %u\n", ed->encoding);
2697 return STATUS_INTERNAL_ERROR;
2698 }
2699
2700 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && ed->compression != BTRFS_COMPRESSION_LZO) {
2701 ERR("unknown compression type %u\n", ed->compression);
2702 return STATUS_INTERNAL_ERROR;
2703 }
2704
2705 if (ed->type == EXTENT_TYPE_REGULAR) {
2706 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2707 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2708 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2709 return STATUS_INTERNAL_ERROR;
2710 }
2711
2712 ed2 = (EXTENT_DATA2*)ed->data;
2713 } else if (ed->type == EXTENT_TYPE_INLINE) {
2714 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) {
2715 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2716 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
2717 return STATUS_INTERNAL_ERROR;
2718 }
2719 }
2720
2721 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2722 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp2->item->size, ALLOC_TAG);
2723
2724 if (!se) {
2725 ERR("out of memory\n");
2726 return STATUS_INSUFFICIENT_RESOURCES;
2727 }
2728
2729 se->offset = tp2->item->key.offset;
2730 se->datalen = tp2->item->size;
2731 RtlCopyMemory(&se->data, tp2->item->data, tp2->item->size);
2732 InsertTailList(&context->lastinode.oldexts, &se->list_entry);
2733 }
2734 }
2735
2736 return STATUS_SUCCESS;
2737 }
2738
2739 typedef struct {
2740 UINT16 namelen;
2741 char* name;
2742 UINT16 value1len;
2743 char* value1;
2744 UINT16 value2len;
2745 char* value2;
2746 LIST_ENTRY list_entry;
2747 } xattr_cmp;
2748
2749 static NTSTATUS send_xattr(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) {
2750 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2751 return STATUS_SUCCESS;
2752
2753 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2754 NTSTATUS Status = flush_refs(context, tp, tp2);
2755 if (!NT_SUCCESS(Status)) {
2756 ERR("flush_refs returned %08x\n", Status);
2757 return Status;
2758 }
2759
2760 if (context->send->cancelling)
2761 return STATUS_SUCCESS;
2762 }
2763
2764 if (tp && tp->item->size < sizeof(DIR_ITEM)) {
2765 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2766 tp->item->size, sizeof(DIR_ITEM));
2767 return STATUS_INTERNAL_ERROR;
2768 }
2769
2770 if (tp2 && tp2->item->size < sizeof(DIR_ITEM)) {
2771 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2772 tp2->item->size, sizeof(DIR_ITEM));
2773 return STATUS_INTERNAL_ERROR;
2774 }
2775
2776 if (tp && !tp2) {
2777 ULONG len;
2778 DIR_ITEM* di;
2779
2780 len = tp->item->size;
2781 di = (DIR_ITEM*)tp->item->data;
2782
2783 do {
2784 ULONG pos;
2785
2786 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2787 ERR("(%llx,%x,%llx) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2788 return STATUS_INTERNAL_ERROR;
2789 }
2790
2791 pos = context->datalen;
2792 send_command(context, BTRFS_SEND_CMD_SET_XATTR);
2793 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2794 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n);
2795 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, &di->name[di->n], di->m);
2796 send_command_finish(context, pos);
2797
2798 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2799 di = (DIR_ITEM*)&di->name[di->m + di->n];
2800 } while (len > 0);
2801 } else if (!tp && tp2) {
2802 ULONG len;
2803 DIR_ITEM* di;
2804
2805 len = tp2->item->size;
2806 di = (DIR_ITEM*)tp2->item->data;
2807
2808 do {
2809 ULONG pos;
2810
2811 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2812 ERR("(%llx,%x,%llx) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2813 return STATUS_INTERNAL_ERROR;
2814 }
2815
2816 pos = context->datalen;
2817 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR);
2818 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2819 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n);
2820 send_command_finish(context, pos);
2821
2822 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2823 di = (DIR_ITEM*)&di->name[di->m + di->n];
2824 } while (len > 0);
2825 } else {
2826 ULONG len;
2827 DIR_ITEM* di;
2828 LIST_ENTRY xattrs;
2829
2830 InitializeListHead(&xattrs);
2831
2832 len = tp->item->size;
2833 di = (DIR_ITEM*)tp->item->data;
2834
2835 do {
2836 xattr_cmp* xa;
2837
2838 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2839 ERR("(%llx,%x,%llx) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2840 return STATUS_INTERNAL_ERROR;
2841 }
2842
2843 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG);
2844 if (!xa) {
2845 ERR("out of memory\n");
2846
2847 while (!IsListEmpty(&xattrs)) {
2848 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry));
2849 }
2850
2851 return STATUS_INSUFFICIENT_RESOURCES;
2852 }
2853
2854 xa->namelen = di->n;
2855 xa->name = di->name;
2856 xa->value1len = di->m;
2857 xa->value1 = di->name + di->n;
2858 xa->value2len = 0;
2859 xa->value2 = NULL;
2860
2861 InsertTailList(&xattrs, &xa->list_entry);
2862
2863 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2864 di = (DIR_ITEM*)&di->name[di->m + di->n];
2865 } while (len > 0);
2866
2867 len = tp2->item->size;
2868 di = (DIR_ITEM*)tp2->item->data;
2869
2870 do {
2871 xattr_cmp* xa;
2872 LIST_ENTRY* le;
2873 BOOL found = FALSE;
2874
2875 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2876 ERR("(%llx,%x,%llx) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2877 return STATUS_INTERNAL_ERROR;
2878 }
2879
2880 le = xattrs.Flink;
2881 while (le != &xattrs) {
2882 xa = CONTAINING_RECORD(le, xattr_cmp, list_entry);
2883
2884 if (xa->namelen == di->n && RtlCompareMemory(xa->name, di->name, di->n) == di->n) {
2885 xa->value2len = di->m;
2886 xa->value2 = di->name + di->n;
2887 found = TRUE;
2888 break;
2889 }
2890
2891 le = le->Flink;
2892 }
2893
2894 if (!found) {
2895 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG);
2896 if (!xa) {
2897 ERR("out of memory\n");
2898
2899 while (!IsListEmpty(&xattrs)) {
2900 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry));
2901 }
2902
2903 return STATUS_INSUFFICIENT_RESOURCES;
2904 }
2905
2906 xa->namelen = di->n;
2907 xa->name = di->name;
2908 xa->value1len = 0;
2909 xa->value1 = NULL;
2910 xa->value2len = di->m;
2911 xa->value2 = di->name + di->n;
2912
2913 InsertTailList(&xattrs, &xa->list_entry);
2914 }
2915
2916 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2917 di = (DIR_ITEM*)&di->name[di->m + di->n];
2918 } while (len > 0);
2919
2920 while (!IsListEmpty(&xattrs)) {
2921 xattr_cmp* xa = CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry);
2922
2923 if (xa->value1len != xa->value2len || !xa->value1 || !xa->value2 || RtlCompareMemory(xa->value1, xa->value2, xa->value1len) != xa->value1len) {
2924 ULONG pos;
2925
2926 if (!xa->value1) {
2927 pos = context->datalen;
2928 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR);
2929 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2930 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen);
2931 send_command_finish(context, pos);
2932 } else {
2933 pos = context->datalen;
2934 send_command(context, BTRFS_SEND_CMD_SET_XATTR);
2935 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (UINT16)strlen(context->lastinode.path) : 0);
2936 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen);
2937 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, xa->value1, xa->value1len);
2938 send_command_finish(context, pos);
2939 }
2940 }
2941
2942 ExFreePool(xa);
2943 }
2944 }
2945
2946 return STATUS_SUCCESS;
2947 }
2948
2949 _Function_class_(KSTART_ROUTINE)
2950 #ifdef __REACTOS__
2951 static void NTAPI send_thread(void* ctx) {
2952 #else
2953 static void send_thread(void* ctx) {
2954 #endif
2955 send_context* context = (send_context*)ctx;
2956 NTSTATUS Status;
2957 KEY searchkey;
2958 traverse_ptr tp, tp2;
2959
2960 InterlockedIncrement(&context->root->send_ops);
2961
2962 if (context->parent)
2963 InterlockedIncrement(&context->parent->send_ops);
2964
2965 if (context->clones) {
2966 ULONG i;
2967
2968 for (i = 0; i < context->num_clones; i++) {
2969 InterlockedIncrement(&context->clones[i]->send_ops);
2970 }
2971 }
2972
2973 ExAcquireResourceExclusiveLite(&context->Vcb->tree_lock, TRUE);
2974
2975 flush_subvol_fcbs(context->root);
2976
2977 if (context->parent)
2978 flush_subvol_fcbs(context->parent);
2979
2980 if (context->Vcb->need_write)
2981 Status = do_write(context->Vcb, NULL);
2982 else
2983 Status = STATUS_SUCCESS;
2984
2985 free_trees(context->Vcb);
2986
2987 if (!NT_SUCCESS(Status)) {
2988 ERR("do_write returned %08x\n", Status);
2989 ExReleaseResourceLite(&context->Vcb->tree_lock);
2990 goto end;
2991 }
2992
2993 ExConvertExclusiveToSharedLite(&context->Vcb->tree_lock);
2994
2995 searchkey.obj_id = searchkey.offset = 0;
2996 searchkey.obj_type = 0;
2997
2998 Status = find_item(context->Vcb, context->root, &tp, &searchkey, FALSE, NULL);
2999 if (!NT_SUCCESS(Status)) {
3000 ERR("find_item returned %08x\n", Status);
3001 ExReleaseResourceLite(&context->Vcb->tree_lock);
3002 goto end;
3003 }
3004
3005 if (context->parent) {
3006 BOOL ended1 = FALSE, ended2 = FALSE;
3007 Status = find_item(context->Vcb, context->parent, &tp2, &searchkey, FALSE, NULL);
3008 if (!NT_SUCCESS(Status)) {
3009 ERR("find_item returned %08x\n", Status);
3010 ExReleaseResourceLite(&context->Vcb->tree_lock);
3011 goto end;
3012 }
3013
3014 do {
3015 traverse_ptr next_tp;
3016
3017 if (context->datalen > SEND_BUFFER_LENGTH) {
3018 KEY key1 = tp.item->key, key2 = tp2.item->key;
3019
3020 ExReleaseResourceLite(&context->Vcb->tree_lock);
3021
3022 KeClearEvent(&context->send->cleared_event);
3023 KeSetEvent(&context->buffer_event, 0, TRUE);
3024 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, FALSE, NULL);
3025
3026 if (context->send->cancelling)
3027 goto end;
3028
3029 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, TRUE);
3030
3031 if (!ended1) {
3032 Status = find_item(context->Vcb, context->root, &tp, &key1, FALSE, NULL);
3033 if (!NT_SUCCESS(Status)) {
3034 ERR("find_item returned %08x\n", Status);
3035 ExReleaseResourceLite(&context->Vcb->tree_lock);
3036 goto end;
3037 }
3038
3039 if (keycmp(tp.item->key, key1)) {
3040 ERR("readonly subvolume changed\n");
3041 ExReleaseResourceLite(&context->Vcb->tree_lock);
3042 Status = STATUS_INTERNAL_ERROR;
3043 goto end;
3044 }
3045 }
3046
3047 if (!ended2) {
3048 Status = find_item(context->Vcb, context->parent, &tp2, &key2, 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(tp2.item->key, key2)) {
3056 ERR("readonly subvolume changed\n");
3057 ExReleaseResourceLite(&context->Vcb->tree_lock);
3058 Status = STATUS_INTERNAL_ERROR;
3059 goto end;
3060 }
3061 }
3062 }
3063
3064 while (!ended1 && !ended2 && tp.tree->header.address == tp2.tree->header.address) {
3065 Status = skip_to_difference(context->Vcb, &tp, &tp2, &ended1, &ended2);
3066 if (!NT_SUCCESS(Status)) {
3067 ERR("skip_to_difference returned %08x\n", Status);
3068 ExReleaseResourceLite(&context->Vcb->tree_lock);
3069 goto end;
3070 }
3071 }
3072
3073 if (!ended1 && !ended2 && !keycmp(tp.item->key, tp2.item->key)) {
3074 BOOL no_next = FALSE, no_next2 = FALSE;
3075
3076 TRACE("~ %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3077
3078 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3079 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3080 if (!NT_SUCCESS(Status)) {
3081 ERR("finish_inode returned %08x\n", Status);
3082 ExReleaseResourceLite(&context->Vcb->tree_lock);
3083 goto end;
3084 }
3085
3086 if (context->send->cancelling) {
3087 ExReleaseResourceLite(&context->Vcb->tree_lock);
3088 goto end;
3089 }
3090 }
3091
3092 if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3093 if (tp.item->size == tp2.item->size && tp.item->size > 0 && RtlCompareMemory(tp.item->data, tp2.item->data, tp.item->size) == tp.item->size) {
3094 UINT64 inode = tp.item->key.obj_id;
3095
3096 while (TRUE) {
3097 if (!find_next_item(context->Vcb, &tp, &next_tp, FALSE, NULL)) {
3098 ended1 = TRUE;
3099 break;
3100 }
3101
3102 tp = next_tp;
3103
3104 if (tp.item->key.obj_id != inode)
3105 break;
3106 }
3107
3108 while (TRUE) {
3109 if (!find_next_item(context->Vcb, &tp2, &next_tp, FALSE, NULL)) {
3110 ended2 = TRUE;
3111 break;
3112 }
3113
3114 tp2 = next_tp;
3115
3116 if (tp2.item->key.obj_id != inode)
3117 break;
3118 }
3119
3120 no_next = TRUE;
3121 } else if (tp.item->size > sizeof(UINT64) && tp2.item->size > sizeof(UINT64) && *(UINT64*)tp.item->data != *(UINT64*)tp2.item->data) {
3122 UINT64 inode = tp.item->key.obj_id;
3123
3124 Status = send_inode(context, NULL, &tp2);
3125 if (!NT_SUCCESS(Status)) {
3126 ERR("send_inode returned %08x\n", Status);
3127 ExReleaseResourceLite(&context->Vcb->tree_lock);
3128 goto end;
3129 }
3130
3131 while (TRUE) {
3132 if (!find_next_item(context->Vcb, &tp2, &next_tp, FALSE, NULL)) {
3133 ended2 = TRUE;
3134 break;
3135 }
3136
3137 tp2 = next_tp;
3138
3139 if (tp2.item->key.obj_id != inode)
3140 break;
3141
3142 if (tp2.item->key.obj_type == TYPE_INODE_REF) {
3143 Status = send_inode_ref(context, &tp2, TRUE);
3144 if (!NT_SUCCESS(Status)) {
3145 ERR("send_inode_ref returned %08x\n", Status);
3146 ExReleaseResourceLite(&context->Vcb->tree_lock);
3147 goto end;
3148 }
3149 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) {
3150 Status = send_inode_extref(context, &tp2, TRUE);
3151 if (!NT_SUCCESS(Status)) {
3152 ERR("send_inode_extref returned %08x\n", Status);
3153 ExReleaseResourceLite(&context->Vcb->tree_lock);
3154 goto end;
3155 }
3156 }
3157 }
3158
3159 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3160 if (!NT_SUCCESS(Status)) {
3161 ERR("finish_inode returned %08x\n", Status);
3162 ExReleaseResourceLite(&context->Vcb->tree_lock);
3163 goto end;
3164 }
3165
3166 if (context->send->cancelling) {
3167 ExReleaseResourceLite(&context->Vcb->tree_lock);
3168 goto end;
3169 }
3170
3171 no_next2 = TRUE;
3172
3173 Status = send_inode(context, &tp, NULL);
3174 if (!NT_SUCCESS(Status)) {
3175 ERR("send_inode returned %08x\n", Status);
3176 ExReleaseResourceLite(&context->Vcb->tree_lock);
3177 goto end;
3178 }
3179 } else {
3180 Status = send_inode(context, &tp, &tp2);
3181 if (!NT_SUCCESS(Status)) {
3182 ERR("send_inode returned %08x\n", Status);
3183 ExReleaseResourceLite(&context->Vcb->tree_lock);
3184 goto end;
3185 }
3186 }
3187 } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3188 Status = send_inode_ref(context, &tp, FALSE);
3189 if (!NT_SUCCESS(Status)) {
3190 ERR("send_inode_ref returned %08x\n", Status);
3191 ExReleaseResourceLite(&context->Vcb->tree_lock);
3192 goto end;
3193 }
3194
3195 Status = send_inode_ref(context, &tp2, TRUE);
3196 if (!NT_SUCCESS(Status)) {
3197 ERR("send_inode_ref returned %08x\n", Status);
3198 ExReleaseResourceLite(&context->Vcb->tree_lock);
3199 goto end;
3200 }
3201 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3202 Status = send_inode_extref(context, &tp, FALSE);
3203 if (!NT_SUCCESS(Status)) {
3204 ERR("send_inode_extref returned %08x\n", Status);
3205 ExReleaseResourceLite(&context->Vcb->tree_lock);
3206 goto end;
3207 }
3208
3209 Status = send_inode_extref(context, &tp2, TRUE);
3210 if (!NT_SUCCESS(Status)) {
3211 ERR("send_inode_extref returned %08x\n", Status);
3212 ExReleaseResourceLite(&context->Vcb->tree_lock);
3213 goto end;
3214 }
3215 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3216 Status = send_extent_data(context, &tp, &tp2);
3217 if (!NT_SUCCESS(Status)) {
3218 ERR("send_extent_data returned %08x\n", Status);
3219 ExReleaseResourceLite(&context->Vcb->tree_lock);
3220 goto end;
3221 }
3222
3223 if (context->send->cancelling) {
3224 ExReleaseResourceLite(&context->Vcb->tree_lock);
3225 goto end;
3226 }
3227 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3228 Status = send_xattr(context, &tp, &tp2);
3229 if (!NT_SUCCESS(Status)) {
3230 ERR("send_xattr returned %08x\n", Status);
3231 ExReleaseResourceLite(&context->Vcb->tree_lock);
3232 goto end;
3233 }
3234
3235 if (context->send->cancelling) {
3236 ExReleaseResourceLite(&context->Vcb->tree_lock);
3237 goto end;
3238 }
3239 }
3240
3241 if (!no_next) {
3242 if (find_next_item(context->Vcb, &tp, &next_tp, FALSE, NULL))
3243 tp = next_tp;
3244 else
3245 ended1 = TRUE;
3246
3247 if (!no_next2) {
3248 if (find_next_item(context->Vcb, &tp2, &next_tp, FALSE, NULL))
3249 tp2 = next_tp;
3250 else
3251 ended2 = TRUE;
3252 }
3253 }
3254 } else if (ended2 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == -1)) {
3255 TRACE("A %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3256
3257 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3258 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3259 if (!NT_SUCCESS(Status)) {
3260 ERR("finish_inode returned %08x\n", Status);
3261 ExReleaseResourceLite(&context->Vcb->tree_lock);
3262 goto end;
3263 }
3264
3265 if (context->send->cancelling) {
3266 ExReleaseResourceLite(&context->Vcb->tree_lock);
3267 goto end;
3268 }
3269 }
3270
3271 if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3272 Status = send_inode(context, &tp, NULL);
3273 if (!NT_SUCCESS(Status)) {
3274 ERR("send_inode returned %08x\n", Status);
3275 ExReleaseResourceLite(&context->Vcb->tree_lock);
3276 goto end;
3277 }
3278 } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3279 Status = send_inode_ref(context, &tp, FALSE);
3280 if (!NT_SUCCESS(Status)) {
3281 ERR("send_inode_ref returned %08x\n", Status);
3282 ExReleaseResourceLite(&context->Vcb->tree_lock);
3283 goto end;
3284 }
3285 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3286 Status = send_inode_extref(context, &tp, FALSE);
3287 if (!NT_SUCCESS(Status)) {
3288 ERR("send_inode_extref returned %08x\n", Status);
3289 ExReleaseResourceLite(&context->Vcb->tree_lock);
3290 goto end;
3291 }
3292 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3293 Status = send_extent_data(context, &tp, NULL);
3294 if (!NT_SUCCESS(Status)) {
3295 ERR("send_extent_data returned %08x\n", Status);
3296 ExReleaseResourceLite(&context->Vcb->tree_lock);
3297 goto end;
3298 }
3299
3300 if (context->send->cancelling) {
3301 ExReleaseResourceLite(&context->Vcb->tree_lock);
3302 goto end;
3303 }
3304 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3305 Status = send_xattr(context, &tp, NULL);
3306 if (!NT_SUCCESS(Status)) {
3307 ERR("send_xattr returned %08x\n", Status);
3308 ExReleaseResourceLite(&context->Vcb->tree_lock);
3309 goto end;
3310 }
3311
3312 if (context->send->cancelling) {
3313 ExReleaseResourceLite(&context->Vcb->tree_lock);
3314 goto end;
3315 }
3316 }
3317
3318 if (find_next_item(context->Vcb, &tp, &next_tp, FALSE, NULL))
3319 tp = next_tp;
3320 else
3321 ended1 = TRUE;
3322 } else if (ended1 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == 1)) {
3323 TRACE("B %llx,%x,%llx\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset);
3324
3325 if (context->lastinode.inode != 0 && tp2.item->key.obj_id > context->lastinode.inode) {
3326 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3327 if (!NT_SUCCESS(Status)) {
3328 ERR("finish_inode returned %08x\n", Status);
3329 ExReleaseResourceLite(&context->Vcb->tree_lock);
3330 goto end;
3331 }
3332
3333 if (context->send->cancelling) {
3334 ExReleaseResourceLite(&context->Vcb->tree_lock);
3335 goto end;
3336 }
3337 }
3338
3339 if (tp2.item->key.obj_type == TYPE_INODE_ITEM) {
3340 Status = send_inode(context, NULL, &tp2);
3341 if (!NT_SUCCESS(Status)) {
3342 ERR("send_inode returned %08x\n", Status);
3343 ExReleaseResourceLite(&context->Vcb->tree_lock);
3344 goto end;
3345 }
3346 } else if (tp2.item->key.obj_type == TYPE_INODE_REF) {
3347 Status = send_inode_ref(context, &tp2, TRUE);
3348 if (!NT_SUCCESS(Status)) {
3349 ERR("send_inode_ref returned %08x\n", Status);
3350 ExReleaseResourceLite(&context->Vcb->tree_lock);
3351 goto end;
3352 }
3353 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) {
3354 Status = send_inode_extref(context, &tp2, TRUE);
3355 if (!NT_SUCCESS(Status)) {
3356 ERR("send_inode_extref returned %08x\n", Status);
3357 ExReleaseResourceLite(&context->Vcb->tree_lock);
3358 goto end;
3359 }
3360 } else if (tp2.item->key.obj_type == TYPE_EXTENT_DATA && !context->lastinode.deleting) {
3361 Status = send_extent_data(context, NULL, &tp2);
3362 if (!NT_SUCCESS(Status)) {
3363 ERR("send_extent_data returned %08x\n", Status);
3364 ExReleaseResourceLite(&context->Vcb->tree_lock);
3365 goto end;
3366 }
3367
3368 if (context->send->cancelling) {
3369 ExReleaseResourceLite(&context->Vcb->tree_lock);
3370 goto end;
3371 }
3372 } else if (tp2.item->key.obj_type == TYPE_XATTR_ITEM && !context->lastinode.deleting) {
3373 Status = send_xattr(context, NULL, &tp2);
3374 if (!NT_SUCCESS(Status)) {
3375 ERR("send_xattr returned %08x\n", Status);
3376 ExReleaseResourceLite(&context->Vcb->tree_lock);
3377 goto end;
3378 }
3379
3380 if (context->send->cancelling) {
3381 ExReleaseResourceLite(&context->Vcb->tree_lock);
3382 goto end;
3383 }
3384 }
3385
3386 if (find_next_item(context->Vcb, &tp2, &next_tp, FALSE, NULL))
3387 tp2 = next_tp;
3388 else
3389 ended2 = TRUE;
3390 }
3391 } while (!ended1 || !ended2);
3392 } else {
3393 do {
3394 traverse_ptr next_tp;
3395
3396 if (context->datalen > SEND_BUFFER_LENGTH) {
3397 KEY key = tp.item->key;
3398
3399 ExReleaseResourceLite(&context->Vcb->tree_lock);
3400
3401 KeClearEvent(&context->send->cleared_event);
3402 KeSetEvent(&context->buffer_event, 0, TRUE);
3403 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, FALSE, NULL);
3404
3405 if (context->send->cancelling)
3406 goto end;
3407
3408 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, TRUE);
3409
3410 Status = find_item(context->Vcb, context->root, &tp, &key, FALSE, NULL);
3411 if (!NT_SUCCESS(Status)) {
3412 ERR("find_item returned %08x\n", Status);
3413 ExReleaseResourceLite(&context->Vcb->tree_lock);
3414 goto end;
3415 }
3416
3417 if (keycmp(tp.item->key, key)) {
3418 ERR("readonly subvolume changed\n");
3419 ExReleaseResourceLite(&context->Vcb->tree_lock);
3420 Status = STATUS_INTERNAL_ERROR;
3421 goto end;
3422 }
3423 }
3424
3425 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3426 Status = finish_inode(context, &tp, NULL);
3427 if (!NT_SUCCESS(Status)) {
3428 ERR("finish_inode returned %08x\n", Status);
3429 ExReleaseResourceLite(&context->Vcb->tree_lock);
3430 goto end;
3431 }
3432
3433 if (context->send->cancelling) {
3434 ExReleaseResourceLite(&context->Vcb->tree_lock);
3435 goto end;
3436 }
3437 }
3438
3439 if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3440 Status = send_inode(context, &tp, NULL);
3441 if (!NT_SUCCESS(Status)) {
3442 ERR("send_inode returned %08x\n", Status);
3443 ExReleaseResourceLite(&context->Vcb->tree_lock);
3444 goto end;
3445 }
3446 } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3447 Status = send_inode_ref(context, &tp, FALSE);
3448 if (!NT_SUCCESS(Status)) {
3449 ERR("send_inode_ref returned %08x\n", Status);
3450 ExReleaseResourceLite(&context->Vcb->tree_lock);
3451 goto end;
3452 }
3453 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3454 Status = send_inode_extref(context, &tp, FALSE);
3455 if (!NT_SUCCESS(Status)) {
3456 ERR("send_inode_extref returned %08x\n", Status);
3457 ExReleaseResourceLite(&context->Vcb->tree_lock);
3458 goto end;
3459 }
3460 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3461 Status = send_extent_data(context, &tp, NULL);
3462 if (!NT_SUCCESS(Status)) {
3463 ERR("send_extent_data returned %08x\n", Status);
3464 ExReleaseResourceLite(&context->Vcb->tree_lock);
3465 goto end;
3466 }
3467
3468 if (context->send->cancelling) {
3469 ExReleaseResourceLite(&context->Vcb->tree_lock);
3470 goto end;
3471 }
3472 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3473 Status = send_xattr(context, &tp, NULL);
3474 if (!NT_SUCCESS(Status)) {
3475 ERR("send_xattr returned %08x\n", Status);
3476 ExReleaseResourceLite(&context->Vcb->tree_lock);
3477 goto end;
3478 }
3479
3480 if (context->send->cancelling) {
3481 ExReleaseResourceLite(&context->Vcb->tree_lock);
3482 goto end;
3483 }
3484 }
3485
3486 if (find_next_item(context->Vcb, &tp, &next_tp, FALSE, NULL))
3487 tp = next_tp;
3488 else
3489 break;
3490 } while (TRUE);
3491 }
3492
3493 if (context->lastinode.inode != 0) {
3494 Status = finish_inode(context, NULL, NULL);
3495 if (!NT_SUCCESS(Status)) {
3496 ERR("finish_inode returned %08x\n", Status);
3497 ExReleaseResourceLite(&context->Vcb->tree_lock);
3498 goto end;
3499 }
3500
3501 ExReleaseResourceLite(&context->Vcb->tree_lock);
3502
3503 if (context->send->cancelling)
3504 goto end;
3505 } else
3506 ExReleaseResourceLite(&context->Vcb->tree_lock);
3507
3508 KeClearEvent(&context->send->cleared_event);
3509 KeSetEvent(&context->buffer_event, 0, TRUE);
3510 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, FALSE, NULL);
3511
3512 Status = STATUS_SUCCESS;
3513
3514 end:
3515 if (!NT_SUCCESS(Status)) {
3516 KeSetEvent(&context->buffer_event, 0, FALSE);
3517
3518 if (context->send->ccb)
3519 context->send->ccb->send_status = Status;
3520 }
3521
3522 ExAcquireResourceExclusiveLite(&context->Vcb->send_load_lock, TRUE);
3523
3524 while (!IsListEmpty(&context->orphans)) {
3525 orphan* o = CONTAINING_RECORD(RemoveHeadList(&context->orphans), orphan, list_entry);
3526 ExFreePool(o);
3527 }
3528
3529 while (!IsListEmpty(&context->dirs)) {
3530 send_dir* sd = CONTAINING_RECORD(RemoveHeadList(&context->dirs), send_dir, list_entry);
3531
3532 if (sd->name)
3533 ExFreePool(sd->name);
3534
3535 while (!IsListEmpty(&sd->deleted_children)) {
3536 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&sd->deleted_children), deleted_child, list_entry);
3537 ExFreePool(dc);
3538 }
3539
3540 ExFreePool(sd);
3541 }
3542
3543 ZwClose(context->send->thread);
3544 context->send->thread = NULL;
3545
3546 if (context->send->ccb)
3547 context->send->ccb->send = NULL;
3548
3549 RemoveEntryList(&context->send->list_entry);
3550 ExFreePool(context->send);
3551 ExFreePool(context->data);
3552
3553 InterlockedDecrement(&context->Vcb->running_sends);
3554 InterlockedDecrement(&context->root->send_ops);
3555
3556 if (context->parent)
3557 InterlockedDecrement(&context->parent->send_ops);
3558
3559 ExReleaseResourceLite(&context->Vcb->send_load_lock);
3560
3561 if (context->clones) {
3562 ULONG i;
3563
3564 for (i = 0; i < context->num_clones; i++) {
3565 InterlockedDecrement(&context->clones[i]->send_ops);
3566 }
3567
3568 ExFreePool(context->clones);
3569 }
3570
3571 ExFreePool(context);
3572
3573 PsTerminateSystemThread(STATUS_SUCCESS);
3574 }
3575
3576 NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJECT FileObject, PIRP Irp) {
3577 NTSTATUS Status;
3578 fcb* fcb;
3579 ccb* ccb;
3580 root* parsubvol = NULL;
3581 send_context* context;
3582 send_info* send;
3583 ULONG num_clones = 0;
3584 root** clones = NULL;
3585
3586 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3587 return STATUS_INVALID_PARAMETER;
3588
3589 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3590 return STATUS_PRIVILEGE_NOT_HELD;
3591
3592 fcb = FileObject->FsContext;
3593 ccb = FileObject->FsContext2;
3594
3595 if (fcb->inode != SUBVOL_ROOT_INODE || fcb == Vcb->root_fileref->fcb)
3596 return STATUS_INVALID_PARAMETER;
3597
3598 if (!Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY))
3599 return STATUS_INVALID_PARAMETER;
3600
3601 if (data) {
3602 btrfs_send_subvol* bss = (btrfs_send_subvol*)data;
3603 HANDLE parent;
3604
3605 #if defined(_WIN64)
3606 if (IoIs32bitProcess(Irp)) {
3607 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data;
3608
3609 if (datalen < offsetof(btrfs_send_subvol32, num_clones))
3610 return STATUS_INVALID_PARAMETER;
3611
3612 parent = Handle32ToHandle(bss32->parent);
3613
3614 if (datalen >= offsetof(btrfs_send_subvol32, clones[0]))
3615 num_clones = bss32->num_clones;
3616
3617 if (datalen < offsetof(btrfs_send_subvol32, clones[0]) + (num_clones * sizeof(UINT32)))
3618 return STATUS_INVALID_PARAMETER;
3619 } else {
3620 #endif
3621 if (datalen < offsetof(btrfs_send_subvol, num_clones))
3622 return STATUS_INVALID_PARAMETER;
3623
3624 parent = bss->parent;
3625
3626 if (datalen >= offsetof(btrfs_send_subvol, clones[0]))
3627 num_clones = bss->num_clones;
3628
3629 if (datalen < offsetof(btrfs_send_subvol, clones[0]) + (num_clones * sizeof(HANDLE)))
3630 return STATUS_INVALID_PARAMETER;
3631 #if defined(_WIN64)
3632 }
3633 #endif
3634
3635 if (parent) {
3636 PFILE_OBJECT fileobj;
3637 struct _fcb* parfcb;
3638
3639 Status = ObReferenceObjectByHandle(parent, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
3640 if (!NT_SUCCESS(Status)) {
3641 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3642 return Status;
3643 }
3644
3645 if (fileobj->DeviceObject != FileObject->DeviceObject) {
3646 ObDereferenceObject(fileobj);
3647 return STATUS_INVALID_PARAMETER;
3648 }
3649
3650 parfcb = fileobj->FsContext;
3651
3652 if (!parfcb || parfcb == Vcb->root_fileref->fcb || parfcb == Vcb->volume_fcb || parfcb->inode != SUBVOL_ROOT_INODE) {
3653 ObDereferenceObject(fileobj);
3654 return STATUS_INVALID_PARAMETER;
3655 }
3656
3657 parsubvol = parfcb->subvol;
3658 ObDereferenceObject(fileobj);
3659
3660 if (!Vcb->readonly && !(parsubvol->root_item.flags & BTRFS_SUBVOL_READONLY))
3661 return STATUS_INVALID_PARAMETER;
3662
3663 if (parsubvol == fcb->subvol)
3664 return STATUS_INVALID_PARAMETER;
3665 }
3666
3667 if (num_clones > 0) {
3668 ULONG i;
3669
3670 clones = ExAllocatePoolWithTag(PagedPool, sizeof(root*) * num_clones, ALLOC_TAG);
3671 if (!clones) {
3672 ERR("out of memory\n");
3673 return STATUS_INSUFFICIENT_RESOURCES;
3674 }
3675
3676 for (i = 0; i < num_clones; i++) {
3677 HANDLE h;
3678 PFILE_OBJECT fileobj;
3679 struct _fcb* clonefcb;
3680
3681 #if defined(_WIN64)
3682 if (IoIs32bitProcess(Irp)) {
3683 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data;
3684
3685 h = Handle32ToHandle(bss32->clones[i]);
3686 } else
3687 #endif
3688 h = bss->clones[i];
3689
3690 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
3691 if (!NT_SUCCESS(Status)) {
3692 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3693 ExFreePool(clones);
3694 return Status;
3695 }
3696
3697 if (fileobj->DeviceObject != FileObject->DeviceObject) {
3698 ObDereferenceObject(fileobj);
3699 ExFreePool(clones);
3700 return STATUS_INVALID_PARAMETER;
3701 }
3702
3703 clonefcb = fileobj->FsContext;
3704
3705 if (!clonefcb || clonefcb == Vcb->root_fileref->fcb || clonefcb == Vcb->volume_fcb || clonefcb->inode != SUBVOL_ROOT_INODE) {
3706 ObDereferenceObject(fileobj);
3707 ExFreePool(clones);
3708 return STATUS_INVALID_PARAMETER;
3709 }
3710
3711 clones[i] = clonefcb->subvol;
3712 ObDereferenceObject(fileobj);
3713
3714 if (!Vcb->readonly && !(clones[i]->root_item.flags & BTRFS_SUBVOL_READONLY)) {
3715 ExFreePool(clones);
3716 return STATUS_INVALID_PARAMETER;
3717 }
3718 }
3719 }
3720 }
3721
3722 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
3723
3724 if (ccb->send) {
3725 WARN("send operation already running\n");
3726 ExReleaseResourceLite(&Vcb->send_load_lock);
3727 return STATUS_DEVICE_NOT_READY;
3728 }
3729
3730 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_context), ALLOC_TAG);
3731 if (!context) {
3732 ERR("out of memory\n");
3733 ExReleaseResourceLite(&Vcb->send_load_lock);
3734 return STATUS_INSUFFICIENT_RESOURCES;
3735 }
3736
3737 context->Vcb = Vcb;
3738 context->root = fcb->subvol;
3739 context->parent = parsubvol;
3740 InitializeListHead(&context->orphans);
3741 InitializeListHead(&context->dirs);
3742 InitializeListHead(&context->pending_rmdirs);
3743 context->lastinode.inode = 0;
3744 context->lastinode.path = NULL;
3745 context->lastinode.sd = NULL;
3746 context->root_dir = NULL;
3747 context->num_clones = num_clones;
3748 context->clones = clones;
3749 InitializeListHead(&context->lastinode.refs);
3750 InitializeListHead(&context->lastinode.oldrefs);
3751 InitializeListHead(&context->lastinode.exts);
3752 InitializeListHead(&context->lastinode.oldexts);
3753
3754 context->data = ExAllocatePoolWithTag(PagedPool, SEND_BUFFER_LENGTH + (2 * MAX_SEND_WRITE), ALLOC_TAG); // give ourselves some wiggle room
3755 if (!context->data) {
3756 ExFreePool(context);
3757 ExReleaseResourceLite(&Vcb->send_load_lock);
3758 return STATUS_INSUFFICIENT_RESOURCES;
3759 }
3760
3761 context->datalen = 0;
3762
3763 send_subvol_header(context, fcb->subvol, ccb->fileref); // FIXME - fileref needs some sort of lock here
3764
3765 KeInitializeEvent(&context->buffer_event, NotificationEvent, FALSE);
3766
3767 send = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_info), ALLOC_TAG);
3768 if (!send) {
3769 ERR("out of memory\n");
3770 ExFreePool(context->data);
3771 ExFreePool(context);
3772 ExReleaseResourceLite(&Vcb->send_load_lock);
3773 return STATUS_INSUFFICIENT_RESOURCES;
3774 }
3775
3776 KeInitializeEvent(&send->cleared_event, NotificationEvent, FALSE);
3777
3778 send->context = context;
3779 context->send = send;
3780
3781 ccb->send = send;
3782 send->ccb = ccb;
3783 ccb->send_status = STATUS_SUCCESS;
3784
3785 send->cancelling = FALSE;
3786
3787 InterlockedIncrement(&Vcb->running_sends);
3788
3789 Status = PsCreateSystemThread(&send->thread, 0, NULL, NULL, NULL, send_thread, context);
3790 if (!NT_SUCCESS(Status)) {
3791 ERR("PsCreateSystemThread returned %08x\n", Status);
3792 ccb->send = NULL;
3793 InterlockedDecrement(&Vcb->running_sends);
3794 ExFreePool(send);
3795 ExFreePool(context->data);
3796 ExFreePool(context);
3797 ExReleaseResourceLite(&Vcb->send_load_lock);
3798 return Status;
3799 }
3800
3801 InsertTailList(&Vcb->send_ops, &send->list_entry);
3802 ExReleaseResourceLite(&Vcb->send_load_lock);
3803
3804 return STATUS_SUCCESS;
3805 }
3806
3807 NTSTATUS read_send_buffer(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, ULONG_PTR* retlen, KPROCESSOR_MODE processor_mode) {
3808 ccb* ccb;
3809 send_context* context;
3810
3811 ccb = FileObject ? FileObject->FsContext2 : NULL;
3812 if (!ccb)
3813 return STATUS_INVALID_PARAMETER;
3814
3815 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3816 return STATUS_PRIVILEGE_NOT_HELD;
3817
3818 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
3819
3820 if (!ccb->send) {
3821 ExReleaseResourceLite(&Vcb->send_load_lock);
3822 return !NT_SUCCESS(ccb->send_status) ? ccb->send_status : STATUS_END_OF_FILE;
3823 }
3824
3825 context = (send_context*)ccb->send->context;
3826
3827 KeWaitForSingleObject(&context->buffer_event, Executive, KernelMode, FALSE, NULL);
3828
3829 if (datalen == 0) {
3830 ExReleaseResourceLite(&Vcb->send_load_lock);
3831 return STATUS_SUCCESS;
3832 }
3833
3834 RtlCopyMemory(data, context->data, min(datalen, context->datalen));
3835
3836 if (datalen < context->datalen) { // not empty yet
3837 *retlen = datalen;
3838 RtlMoveMemory(context->data, &context->data[datalen], context->datalen - datalen);
3839 context->datalen -= datalen;
3840 ExReleaseResourceLite(&Vcb->send_load_lock);
3841 } else {
3842 *retlen = context->datalen;
3843 context->datalen = 0;
3844 ExReleaseResourceLite(&Vcb->send_load_lock);
3845
3846 KeClearEvent(&context->buffer_event);
3847 KeSetEvent(&ccb->send->cleared_event, 0, FALSE);
3848 }
3849
3850 return STATUS_SUCCESS;
3851 }