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