1 /* Copyright (c) Mark Harmstone 2017
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #include "btrfs_drv.h"
20 typedef struct send_dir
{
21 LIST_ENTRY list_entry
;
27 struct send_dir
* parent
;
30 LIST_ENTRY deleted_children
;
34 LIST_ENTRY list_entry
;
42 LIST_ENTRY list_entry
;
48 LIST_ENTRY list_entry
;
56 UINT64 last_child_inode
;
57 LIST_ENTRY list_entry
;
62 LIST_ENTRY list_entry
;
68 device_extension
* Vcb
;
77 LIST_ENTRY pending_rmdirs
;
109 #define MAX_SEND_WRITE 0xc000 // 48 KB
110 #define SEND_BUFFER_LENGTH 0x100000 // 1 MB
112 static NTSTATUS
find_send_dir(send_context
* context
, UINT64 dir
, UINT64 generation
, send_dir
** psd
, BOOL
* added_dummy
);
113 static NTSTATUS
wait_for_flush(send_context
* context
, traverse_ptr
* tp1
, traverse_ptr
* tp2
);
115 static void send_command(send_context
* context
, UINT16 cmd
) {
116 btrfs_send_command
* bsc
= (btrfs_send_command
*)&context
->data
[context
->datalen
];
121 context
->datalen
+= sizeof(btrfs_send_command
);
124 static void send_command_finish(send_context
* context
, ULONG pos
) {
125 btrfs_send_command
* bsc
= (btrfs_send_command
*)&context
->data
[pos
];
127 bsc
->length
= context
->datalen
- pos
- sizeof(btrfs_send_command
);
128 bsc
->csum
= calc_crc32c(0, (UINT8
*)bsc
, context
->datalen
- pos
);
131 static void send_add_tlv(send_context
* context
, UINT16 type
, void* data
, UINT16 length
) {
132 btrfs_send_tlv
* tlv
= (btrfs_send_tlv
*)&context
->data
[context
->datalen
];
135 tlv
->length
= length
;
137 if (length
> 0 && data
)
138 RtlCopyMemory(&tlv
[1], data
, length
);
140 context
->datalen
+= sizeof(btrfs_send_tlv
) + length
;
143 static char* uint64_to_char(UINT64 num
, char* buf
) {
154 *tmp
= (num
% 10) + '0';
158 RtlCopyMemory(buf
, tmp
, tmp2
+ sizeof(tmp2
) - tmp
);
160 return &buf
[tmp2
+ sizeof(tmp2
) - tmp
];
163 static NTSTATUS
get_orphan_name(send_context
* context
, UINT64 inode
, UINT64 generation
, char* name
) {
170 ptr
= uint64_to_char(inode
, &name
[1]);
172 ptr
= uint64_to_char(generation
, ptr
);
176 searchkey
.obj_id
= SUBVOL_ROOT_INODE
;
177 searchkey
.obj_type
= TYPE_DIR_ITEM
;
183 ptr
= uint64_to_char(index
, ptr
);
186 searchkey
.offset
= calc_crc32c(0xfffffffe, (UINT8
*)name
, (ULONG
)(ptr
- name
));
188 Status
= find_item(context
->Vcb
, context
->root
, &tp
, &searchkey
, FALSE
, NULL
);
189 if (!NT_SUCCESS(Status
)) {
190 ERR("find_item returned %08x\n", Status
);
194 if (!keycmp(searchkey
, tp
.item
->key
))
197 if (context
->parent
) {
198 Status
= find_item(context
->Vcb
, context
->parent
, &tp
, &searchkey
, FALSE
, NULL
);
199 if (!NT_SUCCESS(Status
)) {
200 ERR("find_item returned %08x\n", Status
);
204 if (!keycmp(searchkey
, tp
.item
->key
))
208 return STATUS_SUCCESS
;
216 static void add_orphan(send_context
* context
, orphan
* o
) {
219 le
= context
->orphans
.Flink
;
220 while (le
!= &context
->orphans
) {
221 orphan
* o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
223 if (o2
->inode
> o
->inode
) {
224 InsertHeadList(o2
->list_entry
.Blink
, &o
->list_entry
);
231 InsertTailList(&context
->orphans
, &o
->list_entry
);
234 static NTSTATUS
send_read_symlink(send_context
* context
, UINT64 inode
, char** link
, UINT16
* linklen
) {
240 searchkey
.obj_id
= inode
;
241 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
242 searchkey
.offset
= 0;
244 Status
= find_item(context
->Vcb
, context
->root
, &tp
, &searchkey
, FALSE
, NULL
);
245 if (!NT_SUCCESS(Status
)) {
246 ERR("find_item returned %08x\n", Status
);
250 if (keycmp(tp
.item
->key
, searchkey
)) {
251 ERR("could not find (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
252 return STATUS_INTERNAL_ERROR
;
255 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
256 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
257 tp
.item
->size
, sizeof(EXTENT_DATA
));
258 return STATUS_INTERNAL_ERROR
;
261 ed
= (EXTENT_DATA
*)tp
.item
->data
;
263 if (ed
->type
!= EXTENT_TYPE_INLINE
) {
264 WARN("symlink data was not inline, returning blank string\n");
267 return STATUS_SUCCESS
;
270 if (tp
.item
->size
< offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
) {
271 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
272 tp
.item
->size
, offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
);
273 return STATUS_INTERNAL_ERROR
;
276 *link
= (char*)ed
->data
;
277 *linklen
= (UINT16
)ed
->decoded_size
;
279 return STATUS_SUCCESS
;
282 static NTSTATUS
send_inode(send_context
* context
, traverse_ptr
* tp
, traverse_ptr
* tp2
) {
287 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp2
->item
->data
;
289 if (tp2
->item
->size
< sizeof(INODE_ITEM
)) {
290 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
291 tp2
->item
->size
, sizeof(INODE_ITEM
));
292 return STATUS_INTERNAL_ERROR
;
295 context
->lastinode
.inode
= tp2
->item
->key
.obj_id
;
296 context
->lastinode
.deleting
= TRUE
;
297 context
->lastinode
.gen
= ii2
->generation
;
298 context
->lastinode
.mode
= ii2
->st_mode
;
299 context
->lastinode
.flags
= ii2
->flags
;
300 context
->lastinode
.o
= NULL
;
301 context
->lastinode
.sd
= NULL
;
303 return STATUS_SUCCESS
;
306 ii
= (INODE_ITEM
*)tp
->item
->data
;
308 if (tp
->item
->size
< sizeof(INODE_ITEM
)) {
309 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
310 tp
->item
->size
, sizeof(INODE_ITEM
));
311 return STATUS_INTERNAL_ERROR
;
314 context
->lastinode
.inode
= tp
->item
->key
.obj_id
;
315 context
->lastinode
.deleting
= FALSE
;
316 context
->lastinode
.gen
= ii
->generation
;
317 context
->lastinode
.uid
= ii
->st_uid
;
318 context
->lastinode
.gid
= ii
->st_gid
;
319 context
->lastinode
.mode
= ii
->st_mode
;
320 context
->lastinode
.size
= ii
->st_size
;
321 context
->lastinode
.atime
= ii
->st_atime
;
322 context
->lastinode
.mtime
= ii
->st_mtime
;
323 context
->lastinode
.ctime
= ii
->st_ctime
;
324 context
->lastinode
.flags
= ii
->flags
;
325 context
->lastinode
.file
= FALSE
;
326 context
->lastinode
.o
= NULL
;
327 context
->lastinode
.sd
= NULL
;
329 if (context
->lastinode
.path
) {
330 ExFreePool(context
->lastinode
.path
);
331 context
->lastinode
.path
= NULL
;
335 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp2
->item
->data
;
338 if (tp2
->item
->size
< sizeof(INODE_ITEM
)) {
339 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
340 tp2
->item
->size
, sizeof(INODE_ITEM
));
341 return STATUS_INTERNAL_ERROR
;
344 context
->lastinode
.oldmode
= ii2
->st_mode
;
345 context
->lastinode
.olduid
= ii2
->st_uid
;
346 context
->lastinode
.oldgid
= ii2
->st_gid
;
348 if ((ii2
->st_mode
& __S_IFREG
) == __S_IFREG
&& (ii2
->st_mode
& __S_IFLNK
) != __S_IFLNK
&& (ii2
->st_mode
& __S_IFSOCK
) != __S_IFSOCK
)
349 context
->lastinode
.file
= TRUE
;
351 context
->lastinode
.new = FALSE
;
353 le
= context
->orphans
.Flink
;
354 while (le
!= &context
->orphans
) {
355 orphan
* o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
357 if (o2
->inode
== tp
->item
->key
.obj_id
) {
358 context
->lastinode
.o
= o2
;
360 } else if (o2
->inode
> tp
->item
->key
.obj_id
)
366 context
->lastinode
.new = TRUE
;
368 if (tp
->item
->key
.obj_id
== SUBVOL_ROOT_INODE
) {
371 Status
= find_send_dir(context
, tp
->item
->key
.obj_id
, ii
->generation
, &sd
, NULL
);
372 if (!NT_SUCCESS(Status
)) {
373 ERR("find_send_dir returned %08x\n", Status
);
377 sd
->atime
= ii
->st_atime
;
378 sd
->mtime
= ii
->st_mtime
;
379 sd
->ctime
= ii
->st_ctime
;
380 context
->root_dir
= sd
;
382 ULONG pos
= context
->datalen
;
389 // skip creating orphan directory if we've already done so
390 if (ii
->st_mode
& __S_IFDIR
) {
393 le
= context
->orphans
.Flink
;
394 while (le
!= &context
->orphans
) {
395 orphan
* o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
397 if (o2
->inode
== tp
->item
->key
.obj_id
) {
398 context
->lastinode
.o
= o2
;
399 o2
->sd
->atime
= ii
->st_atime
;
400 o2
->sd
->mtime
= ii
->st_mtime
;
401 o2
->sd
->ctime
= ii
->st_ctime
;
402 o2
->sd
->dummy
= FALSE
;
403 return STATUS_SUCCESS
;
404 } else if (o2
->inode
> tp
->item
->key
.obj_id
)
411 if ((ii
->st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
412 cmd
= BTRFS_SEND_CMD_MKSOCK
;
413 else if ((ii
->st_mode
& __S_IFLNK
) == __S_IFLNK
)
414 cmd
= BTRFS_SEND_CMD_SYMLINK
;
415 else if ((ii
->st_mode
& __S_IFCHR
) == __S_IFCHR
|| (ii
->st_mode
& __S_IFBLK
) == __S_IFBLK
)
416 cmd
= BTRFS_SEND_CMD_MKNOD
;
417 else if ((ii
->st_mode
& __S_IFDIR
) == __S_IFDIR
)
418 cmd
= BTRFS_SEND_CMD_MKDIR
;
419 else if ((ii
->st_mode
& __S_IFIFO
) == __S_IFIFO
)
420 cmd
= BTRFS_SEND_CMD_MKFIFO
;
422 cmd
= BTRFS_SEND_CMD_MKFILE
;
423 context
->lastinode
.file
= TRUE
;
426 send_command(context
, cmd
);
428 Status
= get_orphan_name(context
, tp
->item
->key
.obj_id
, ii
->generation
, name
);
429 if (!NT_SUCCESS(Status
)) {
430 ERR("get_orphan_name returned %08x\n", Status
);
434 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, name
, (UINT16
)strlen(name
));
435 send_add_tlv(context
, BTRFS_SEND_TLV_INODE
, &tp
->item
->key
.obj_id
, sizeof(UINT64
));
437 if (cmd
== BTRFS_SEND_CMD_MKNOD
|| cmd
== BTRFS_SEND_CMD_MKFIFO
|| cmd
== BTRFS_SEND_CMD_MKSOCK
) {
438 UINT64 rdev
= makedev((ii
->st_rdev
& 0xFFFFFFFFFFF) >> 20, ii
->st_rdev
& 0xFFFFF), mode
= ii
->st_mode
;
440 send_add_tlv(context
, BTRFS_SEND_TLV_RDEV
, &rdev
, sizeof(UINT64
));
441 send_add_tlv(context
, BTRFS_SEND_TLV_MODE
, &mode
, sizeof(UINT64
));
442 } else if (cmd
== BTRFS_SEND_CMD_SYMLINK
&& ii
->st_size
> 0) {
446 Status
= send_read_symlink(context
, tp
->item
->key
.obj_id
, &link
, &linklen
);
447 if (!NT_SUCCESS(Status
)) {
448 ERR("send_read_symlink returned %08x\n", Status
);
452 send_add_tlv(context
, BTRFS_SEND_TLV_PATH_LINK
, link
, linklen
);
455 send_command_finish(context
, pos
);
457 if (ii
->st_mode
& __S_IFDIR
) {
458 Status
= find_send_dir(context
, tp
->item
->key
.obj_id
, ii
->generation
, &sd
, NULL
);
459 if (!NT_SUCCESS(Status
)) {
460 ERR("find_send_dir returned %08x\n", Status
);
468 context
->lastinode
.sd
= sd
;
470 o
= ExAllocatePoolWithTag(PagedPool
, sizeof(orphan
), ALLOC_TAG
);
472 ERR("out of memory\n");
473 return STATUS_INSUFFICIENT_RESOURCES
;
476 o
->inode
= tp
->item
->key
.obj_id
;
477 o
->dir
= (ii
->st_mode
& __S_IFDIR
&& ii
->st_size
> 0) ? TRUE
: FALSE
;
478 strcpy(o
->tmpname
, name
);
480 add_orphan(context
, o
);
482 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, strlen(o
->tmpname
) + 1, ALLOC_TAG
);
483 if (!context
->lastinode
.path
) {
484 ERR("out of memory\n");
485 return STATUS_INSUFFICIENT_RESOURCES
;
488 strcpy(context
->lastinode
.path
, o
->tmpname
);
490 context
->lastinode
.o
= o
;
493 return STATUS_SUCCESS
;
496 static NTSTATUS
send_add_dir(send_context
* context
, UINT64 inode
, send_dir
* parent
, char* name
, UINT16 namelen
, BOOL dummy
, LIST_ENTRY
* lastentry
, send_dir
** psd
) {
498 send_dir
* sd
= ExAllocatePoolWithTag(PagedPool
, sizeof(send_dir
), ALLOC_TAG
);
501 ERR("out of memory\n");
502 return STATUS_INSUFFICIENT_RESOURCES
;
510 sd
->atime
= context
->lastinode
.atime
;
511 sd
->mtime
= context
->lastinode
.mtime
;
512 sd
->ctime
= context
->lastinode
.ctime
;
516 sd
->name
= ExAllocatePoolWithTag(PagedPool
, namelen
, ALLOC_TAG
);
518 ERR("out of memory\n");
520 return STATUS_INSUFFICIENT_RESOURCES
;
523 memcpy(sd
->name
, name
, namelen
);
527 sd
->namelen
= namelen
;
529 InitializeListHead(&sd
->deleted_children
);
532 InsertHeadList(lastentry
, &sd
->list_entry
);
534 le
= context
->dirs
.Flink
;
535 while (le
!= &context
->dirs
) {
536 send_dir
* sd2
= CONTAINING_RECORD(le
, send_dir
, list_entry
);
538 if (sd2
->inode
> sd
->inode
) {
539 InsertHeadList(sd2
->list_entry
.Blink
, &sd
->list_entry
);
544 return STATUS_SUCCESS
;
550 InsertTailList(&context
->dirs
, &sd
->list_entry
);
556 return STATUS_SUCCESS
;
559 static __inline UINT16
find_path_len(send_dir
* parent
, UINT16 namelen
) {
560 UINT16 len
= namelen
;
562 while (parent
&& parent
->namelen
> 0) {
563 len
+= parent
->namelen
+ 1;
564 parent
= parent
->parent
;
570 static void find_path(char* path
, send_dir
* parent
, char* name
, ULONG namelen
) {
573 RtlCopyMemory(path
, name
, namelen
);
575 while (parent
&& parent
->namelen
> 0) {
576 RtlMoveMemory(path
+ parent
->namelen
+ 1, path
, len
);
577 RtlCopyMemory(path
, parent
->name
, parent
->namelen
);
578 path
[parent
->namelen
] = '/';
579 len
+= parent
->namelen
+ 1;
581 parent
= parent
->parent
;
585 static void send_add_tlv_path(send_context
* context
, UINT16 type
, send_dir
* parent
, char* name
, UINT16 namelen
) {
586 UINT16 len
= find_path_len(parent
, namelen
);
588 send_add_tlv(context
, type
, NULL
, len
);
591 find_path((char*)&context
->data
[context
->datalen
- len
], parent
, name
, namelen
);
594 static NTSTATUS
found_path(send_context
* context
, send_dir
* parent
, char* name
, UINT16 namelen
) {
595 ULONG pos
= context
->datalen
;
597 if (context
->lastinode
.o
) {
598 send_command(context
, BTRFS_SEND_CMD_RENAME
);
600 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, context
->root_dir
, context
->lastinode
.o
->tmpname
, (UINT16
)strlen(context
->lastinode
.o
->tmpname
));
602 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH_TO
, parent
, name
, namelen
);
604 send_command_finish(context
, pos
);
606 send_command(context
, BTRFS_SEND_CMD_LINK
);
608 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, parent
, name
, namelen
);
610 send_add_tlv(context
, BTRFS_SEND_TLV_PATH_LINK
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
612 send_command_finish(context
, pos
);
615 if (context
->lastinode
.o
) {
618 if (context
->lastinode
.o
->sd
) {
619 if (context
->lastinode
.o
->sd
->name
)
620 ExFreePool(context
->lastinode
.o
->sd
->name
);
622 context
->lastinode
.o
->sd
->name
= ExAllocatePoolWithTag(PagedPool
, namelen
, ALLOC_TAG
);
623 if (!context
->lastinode
.o
->sd
->name
) {
624 ERR("out of memory\n");
625 return STATUS_INSUFFICIENT_RESOURCES
;
628 RtlCopyMemory(context
->lastinode
.o
->sd
->name
, name
, namelen
);
629 context
->lastinode
.o
->sd
->namelen
= namelen
;
630 context
->lastinode
.o
->sd
->parent
= parent
;
633 if (context
->lastinode
.path
)
634 ExFreePool(context
->lastinode
.path
);
636 pathlen
= find_path_len(parent
, namelen
);
637 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, pathlen
+ 1, ALLOC_TAG
);
638 if (!context
->lastinode
.path
) {
639 ERR("out of memory\n");
640 return STATUS_INSUFFICIENT_RESOURCES
;
643 find_path(context
->lastinode
.path
, parent
, name
, namelen
);
644 context
->lastinode
.path
[pathlen
] = 0;
646 RemoveEntryList(&context
->lastinode
.o
->list_entry
);
647 ExFreePool(context
->lastinode
.o
);
649 context
->lastinode
.o
= NULL
;
652 return STATUS_SUCCESS
;
655 static void send_utimes_command_dir(send_context
* context
, send_dir
* sd
, BTRFS_TIME
* atime
, BTRFS_TIME
* mtime
, BTRFS_TIME
* ctime
) {
656 ULONG pos
= context
->datalen
;
658 send_command(context
, BTRFS_SEND_CMD_UTIMES
);
660 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, sd
->parent
, sd
->name
, sd
->namelen
);
662 send_add_tlv(context
, BTRFS_SEND_TLV_ATIME
, atime
, sizeof(BTRFS_TIME
));
663 send_add_tlv(context
, BTRFS_SEND_TLV_MTIME
, mtime
, sizeof(BTRFS_TIME
));
664 send_add_tlv(context
, BTRFS_SEND_TLV_CTIME
, ctime
, sizeof(BTRFS_TIME
));
666 send_command_finish(context
, pos
);
669 static NTSTATUS
find_send_dir(send_context
* context
, UINT64 dir
, UINT64 generation
, send_dir
** psd
, BOOL
* added_dummy
) {
674 le
= context
->dirs
.Flink
;
675 while (le
!= &context
->dirs
) {
676 send_dir
* sd2
= CONTAINING_RECORD(le
, send_dir
, list_entry
);
678 if (sd2
->inode
> dir
)
680 else if (sd2
->inode
== dir
) {
684 *added_dummy
= FALSE
;
686 return STATUS_SUCCESS
;
692 if (dir
== SUBVOL_ROOT_INODE
) {
693 Status
= send_add_dir(context
, dir
, NULL
, NULL
, 0, FALSE
, le
, psd
);
694 if (!NT_SUCCESS(Status
)) {
695 ERR("send_add_dir returned %08x\n", Status
);
700 *added_dummy
= FALSE
;
702 return STATUS_SUCCESS
;
705 if (context
->parent
) {
709 searchkey
.obj_id
= dir
;
710 searchkey
.obj_type
= TYPE_INODE_REF
; // directories should never have an extiref
711 searchkey
.offset
= 0xffffffffffffffff;
713 Status
= find_item(context
->Vcb
, context
->parent
, &tp
, &searchkey
, FALSE
, NULL
);
714 if (!NT_SUCCESS(Status
)) {
715 ERR("find_item returned %08x\n", Status
);
719 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
720 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
723 if (tp
.item
->size
< sizeof(INODE_REF
) || tp
.item
->size
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
724 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
725 return STATUS_INTERNAL_ERROR
;
728 if (tp
.item
->key
.offset
== SUBVOL_ROOT_INODE
)
729 parent
= context
->root_dir
;
731 Status
= find_send_dir(context
, tp
.item
->key
.offset
, generation
, &parent
, NULL
);
732 if (!NT_SUCCESS(Status
)) {
733 ERR("find_send_dir returned %08x\n", Status
);
738 Status
= send_add_dir(context
, dir
, parent
, ir
->name
, ir
->n
, TRUE
, NULL
, psd
);
739 if (!NT_SUCCESS(Status
)) {
740 ERR("send_add_dir returned %08x\n", Status
);
745 *added_dummy
= FALSE
;
747 return STATUS_SUCCESS
;
751 Status
= get_orphan_name(context
, dir
, generation
, name
);
752 if (!NT_SUCCESS(Status
)) {
753 ERR("get_orphan_name returned %08x\n", Status
);
757 Status
= send_add_dir(context
, dir
, NULL
, name
, (UINT16
)strlen(name
), TRUE
, le
, psd
);
758 if (!NT_SUCCESS(Status
)) {
759 ERR("send_add_dir returned %08x\n", Status
);
766 return STATUS_SUCCESS
;
769 static NTSTATUS
send_inode_ref(send_context
* context
, traverse_ptr
* tp
, BOOL tree2
) {
771 UINT64 inode
= tp
? tp
->item
->key
.obj_id
: 0, dir
= tp
? tp
->item
->key
.offset
: 0;
778 if (inode
== dir
) // root
779 return STATUS_SUCCESS
;
781 if (tp
->item
->size
< sizeof(INODE_REF
)) {
782 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
783 tp
->item
->size
, sizeof(INODE_REF
));
784 return STATUS_INTERNAL_ERROR
;
787 if (dir
!= SUBVOL_ROOT_INODE
) {
790 Status
= find_send_dir(context
, dir
, context
->root
->root_item
.ctransid
, &sd
, &added_dummy
);
791 if (!NT_SUCCESS(Status
)) {
792 ERR("find_send_dir returned %08x\n", Status
);
796 // directory has higher inode number than file, so might need to be created
800 le
= context
->orphans
.Flink
;
801 while (le
!= &context
->orphans
) {
802 o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
804 if (o2
->inode
== dir
) {
807 } else if (o2
->inode
> dir
)
814 ULONG pos
= context
->datalen
;
816 send_command(context
, BTRFS_SEND_CMD_MKDIR
);
818 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, NULL
, sd
->name
, sd
->namelen
);
820 send_add_tlv(context
, BTRFS_SEND_TLV_INODE
, &dir
, sizeof(UINT64
));
822 send_command_finish(context
, pos
);
824 o2
= ExAllocatePoolWithTag(PagedPool
, sizeof(orphan
), ALLOC_TAG
);
826 ERR("out of memory\n");
827 return STATUS_INSUFFICIENT_RESOURCES
;
832 memcpy(o2
->tmpname
, sd
->name
, sd
->namelen
);
833 o2
->tmpname
[sd
->namelen
] = 0;
835 add_orphan(context
, o2
);
839 sd
= context
->root_dir
;
841 len
= tp
->item
->size
;
842 ir
= (INODE_REF
*)tp
->item
->data
;
847 if (len
< sizeof(INODE_REF
) || len
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
848 ERR("(%llx,%x,%llx) was truncated\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
849 return STATUS_INTERNAL_ERROR
;
852 r
= ExAllocatePoolWithTag(PagedPool
, offsetof(ref
, name
[0]) + ir
->n
, ALLOC_TAG
);
854 ERR("out of memory\n");
855 return STATUS_INSUFFICIENT_RESOURCES
;
860 RtlCopyMemory(r
->name
, ir
->name
, ir
->n
);
862 InsertTailList(tree2
? &context
->lastinode
.oldrefs
: &context
->lastinode
.refs
, &r
->list_entry
);
864 len
-= (UINT16
)offsetof(INODE_REF
, name
[0]) + ir
->n
;
865 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
868 return STATUS_SUCCESS
;
871 static NTSTATUS
send_inode_extref(send_context
* context
, traverse_ptr
* tp
, BOOL tree2
) {
875 if (tp
->item
->size
< sizeof(INODE_EXTREF
)) {
876 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
877 tp
->item
->size
, sizeof(INODE_EXTREF
));
878 return STATUS_INTERNAL_ERROR
;
881 len
= tp
->item
->size
;
882 ier
= (INODE_EXTREF
*)tp
->item
->data
;
890 if (len
< sizeof(INODE_EXTREF
) || len
< offsetof(INODE_EXTREF
, name
[0]) + ier
->n
) {
891 ERR("(%llx,%x,%llx) was truncated\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
892 return STATUS_INTERNAL_ERROR
;
895 if (ier
->dir
!= SUBVOL_ROOT_INODE
) {
899 Status
= find_send_dir(context
, ier
->dir
, context
->root
->root_item
.ctransid
, &sd
, &added_dummy
);
900 if (!NT_SUCCESS(Status
)) {
901 ERR("find_send_dir returned %08x\n", Status
);
905 // directory has higher inode number than file, so might need to be created
909 le
= context
->orphans
.Flink
;
910 while (le
!= &context
->orphans
) {
911 o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
913 if (o2
->inode
== ier
->dir
) {
916 } else if (o2
->inode
> ier
->dir
)
923 ULONG pos
= context
->datalen
;
925 send_command(context
, BTRFS_SEND_CMD_MKDIR
);
927 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, NULL
, sd
->name
, sd
->namelen
);
928 send_add_tlv(context
, BTRFS_SEND_TLV_INODE
, &ier
->dir
, sizeof(UINT64
));
930 send_command_finish(context
, pos
);
932 o2
= ExAllocatePoolWithTag(PagedPool
, sizeof(orphan
), ALLOC_TAG
);
934 ERR("out of memory\n");
935 return STATUS_INSUFFICIENT_RESOURCES
;
938 o2
->inode
= ier
->dir
;
940 memcpy(o2
->tmpname
, sd
->name
, sd
->namelen
);
941 o2
->tmpname
[sd
->namelen
] = 0;
943 add_orphan(context
, o2
);
947 sd
= context
->root_dir
;
949 r
= ExAllocatePoolWithTag(PagedPool
, offsetof(ref
, name
[0]) + ier
->n
, ALLOC_TAG
);
951 ERR("out of memory\n");
952 return STATUS_INSUFFICIENT_RESOURCES
;
957 RtlCopyMemory(r
->name
, ier
->name
, ier
->n
);
959 InsertTailList(tree2
? &context
->lastinode
.oldrefs
: &context
->lastinode
.refs
, &r
->list_entry
);
961 len
-= (UINT16
)offsetof(INODE_EXTREF
, name
[0]) + ier
->n
;
962 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
965 return STATUS_SUCCESS
;
968 static void send_subvol_header(send_context
* context
, root
* r
, file_ref
* fr
) {
969 ULONG pos
= context
->datalen
;
971 send_command(context
, context
->parent
? BTRFS_SEND_CMD_SNAPSHOT
: BTRFS_SEND_CMD_SUBVOL
);
973 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, fr
->dc
->utf8
.Buffer
, fr
->dc
->utf8
.Length
);
975 send_add_tlv(context
, BTRFS_SEND_TLV_UUID
, r
->root_item
.rtransid
== 0 ? &r
->root_item
.uuid
: &r
->root_item
.received_uuid
, sizeof(BTRFS_UUID
));
976 send_add_tlv(context
, BTRFS_SEND_TLV_TRANSID
, &r
->root_item
.ctransid
, sizeof(UINT64
));
978 if (context
->parent
) {
979 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_UUID
,
980 context
->parent
->root_item
.rtransid
== 0 ? &context
->parent
->root_item
.uuid
: &context
->parent
->root_item
.received_uuid
, sizeof(BTRFS_UUID
));
981 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_CTRANSID
, &context
->parent
->root_item
.ctransid
, sizeof(UINT64
));
984 send_command_finish(context
, pos
);
987 static void send_chown_command(send_context
* context
, char* path
, UINT64 uid
, UINT64 gid
) {
988 ULONG pos
= context
->datalen
;
990 send_command(context
, BTRFS_SEND_CMD_CHOWN
);
992 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, path
, path
? (UINT16
)strlen(path
) : 0);
993 send_add_tlv(context
, BTRFS_SEND_TLV_UID
, &uid
, sizeof(UINT64
));
994 send_add_tlv(context
, BTRFS_SEND_TLV_GID
, &gid
, sizeof(UINT64
));
996 send_command_finish(context
, pos
);
999 static void send_chmod_command(send_context
* context
, char* path
, UINT64 mode
) {
1000 ULONG pos
= context
->datalen
;
1002 send_command(context
, BTRFS_SEND_CMD_CHMOD
);
1006 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, path
, path
? (UINT16
)strlen(path
) : 0);
1007 send_add_tlv(context
, BTRFS_SEND_TLV_MODE
, &mode
, sizeof(UINT64
));
1009 send_command_finish(context
, pos
);
1012 static void send_utimes_command(send_context
* context
, char* path
, BTRFS_TIME
* atime
, BTRFS_TIME
* mtime
, BTRFS_TIME
* ctime
) {
1013 ULONG pos
= context
->datalen
;
1015 send_command(context
, BTRFS_SEND_CMD_UTIMES
);
1017 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, path
, path
? (UINT16
)strlen(path
) : 0);
1018 send_add_tlv(context
, BTRFS_SEND_TLV_ATIME
, atime
, sizeof(BTRFS_TIME
));
1019 send_add_tlv(context
, BTRFS_SEND_TLV_MTIME
, mtime
, sizeof(BTRFS_TIME
));
1020 send_add_tlv(context
, BTRFS_SEND_TLV_CTIME
, ctime
, sizeof(BTRFS_TIME
));
1022 send_command_finish(context
, pos
);
1025 static void send_truncate_command(send_context
* context
, char* path
, UINT64 size
) {
1026 ULONG pos
= context
->datalen
;
1028 send_command(context
, BTRFS_SEND_CMD_TRUNCATE
);
1030 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, path
, path
? (UINT16
)strlen(path
) : 0);
1031 send_add_tlv(context
, BTRFS_SEND_TLV_SIZE
, &size
, sizeof(UINT64
));
1033 send_command_finish(context
, pos
);
1036 static NTSTATUS
send_unlink_command(send_context
* context
, send_dir
* parent
, UINT16 namelen
, char* name
) {
1037 ULONG pos
= context
->datalen
;
1040 send_command(context
, BTRFS_SEND_CMD_UNLINK
);
1042 pathlen
= find_path_len(parent
, namelen
);
1043 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, NULL
, pathlen
);
1045 find_path((char*)&context
->data
[context
->datalen
- pathlen
], parent
, name
, namelen
);
1047 send_command_finish(context
, pos
);
1049 return STATUS_SUCCESS
;
1052 static void send_rmdir_command(send_context
* context
, UINT16 pathlen
, char* path
) {
1053 ULONG pos
= context
->datalen
;
1055 send_command(context
, BTRFS_SEND_CMD_RMDIR
);
1056 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, path
, pathlen
);
1057 send_command_finish(context
, pos
);
1060 static NTSTATUS
get_dir_last_child(send_context
* context
, UINT64
* last_inode
) {
1067 searchkey
.obj_id
= context
->lastinode
.inode
;
1068 searchkey
.obj_type
= TYPE_DIR_INDEX
;
1069 searchkey
.offset
= 2;
1071 Status
= find_item(context
->Vcb
, context
->parent
, &tp
, &searchkey
, FALSE
, NULL
);
1072 if (!NT_SUCCESS(Status
)) {
1073 ERR("find_item returned %08x\n", Status
);
1078 traverse_ptr next_tp
;
1080 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
1081 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
1083 if (tp
.item
->size
< sizeof(DIR_ITEM
) || tp
.item
->size
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
1084 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1085 return STATUS_INTERNAL_ERROR
;
1088 if (di
->key
.obj_type
== TYPE_INODE_ITEM
)
1089 *last_inode
= max(*last_inode
, di
->key
.obj_id
);
1093 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
1099 return STATUS_SUCCESS
;
1102 static NTSTATUS
add_pending_rmdir(send_context
* context
, UINT64 last_inode
) {
1106 pr
= ExAllocatePoolWithTag(PagedPool
, sizeof(pending_rmdir
), ALLOC_TAG
);
1108 ERR("out of memory\n");
1109 return STATUS_INSUFFICIENT_RESOURCES
;
1112 pr
->sd
= context
->lastinode
.sd
;
1113 pr
->last_child_inode
= last_inode
;
1115 le
= context
->pending_rmdirs
.Flink
;
1116 while (le
!= &context
->pending_rmdirs
) {
1117 pending_rmdir
* pr2
= CONTAINING_RECORD(le
, pending_rmdir
, list_entry
);
1119 if (pr2
->last_child_inode
> pr
->last_child_inode
) {
1120 InsertHeadList(pr2
->list_entry
.Blink
, &pr
->list_entry
);
1121 return STATUS_SUCCESS
;
1127 InsertTailList(&context
->pending_rmdirs
, &pr
->list_entry
);
1129 return STATUS_SUCCESS
;
1132 static NTSTATUS
look_for_collision(send_context
* context
, send_dir
* sd
, char* name
, ULONG namelen
, UINT64
* inode
, BOOL
* dir
) {
1139 searchkey
.obj_id
= sd
->inode
;
1140 searchkey
.obj_type
= TYPE_DIR_ITEM
;
1141 searchkey
.offset
= calc_crc32c(0xfffffffe, (UINT8
*)name
, namelen
);
1143 Status
= find_item(context
->Vcb
, context
->parent
, &tp
, &searchkey
, FALSE
, NULL
);
1144 if (!NT_SUCCESS(Status
)) {
1145 ERR("find_item returned %08x\n", Status
);
1149 if (keycmp(tp
.item
->key
, searchkey
))
1150 return STATUS_SUCCESS
;
1152 di
= (DIR_ITEM
*)tp
.item
->data
;
1153 len
= tp
.item
->size
;
1156 if (len
< sizeof(DIR_ITEM
) || len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
1157 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1158 return STATUS_INTERNAL_ERROR
;
1161 if (di
->n
== namelen
&& RtlCompareMemory(di
->name
, name
, namelen
) == namelen
) {
1162 *inode
= di
->key
.obj_type
== TYPE_INODE_ITEM
? di
->key
.obj_id
: 0;
1163 *dir
= di
->type
== BTRFS_TYPE_DIRECTORY
? TRUE
: FALSE
;
1164 return STATUS_OBJECT_NAME_COLLISION
;
1167 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
1168 len
-= (UINT16
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
1171 return STATUS_SUCCESS
;
1174 static NTSTATUS
make_file_orphan(send_context
* context
, UINT64 inode
, BOOL dir
, UINT64 generation
, ref
* r
) {
1176 ULONG pos
= context
->datalen
;
1177 send_dir
* sd
= NULL
;
1185 dc
= ExAllocatePoolWithTag(PagedPool
, offsetof(deleted_child
, name
[0]) + r
->namelen
, ALLOC_TAG
);
1187 ERR("out of memory\n");
1188 return STATUS_INSUFFICIENT_RESOURCES
;
1191 dc
->namelen
= r
->namelen
;
1192 RtlCopyMemory(dc
->name
, r
->name
, r
->namelen
);
1193 InsertTailList(&r
->sd
->deleted_children
, &dc
->list_entry
);
1196 le
= context
->orphans
.Flink
;
1197 while (le
!= &context
->orphans
) {
1198 orphan
* o2
= CONTAINING_RECORD(le
, orphan
, list_entry
);
1200 if (o2
->inode
== inode
) {
1201 send_command(context
, BTRFS_SEND_CMD_UNLINK
);
1203 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, r
->sd
, r
->name
, r
->namelen
);
1205 send_command_finish(context
, pos
);
1207 return STATUS_SUCCESS
;
1208 } else if (o2
->inode
> inode
)
1214 Status
= get_orphan_name(context
, inode
, generation
, name
);
1215 if (!NT_SUCCESS(Status
)) {
1216 ERR("get_orphan_name returned %08x\n", Status
);
1221 Status
= find_send_dir(context
, inode
, generation
, &sd
, NULL
);
1222 if (!NT_SUCCESS(Status
)) {
1223 ERR("find_send_dir returned %08x\n", Status
);
1229 send_command(context
, BTRFS_SEND_CMD_RENAME
);
1231 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, r
->sd
, r
->name
, r
->namelen
);
1232 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH_TO
, context
->root_dir
, name
, (UINT16
)strlen(name
));
1234 send_command_finish(context
, pos
);
1237 ExFreePool(sd
->name
);
1239 sd
->namelen
= (UINT16
)strlen(name
);
1240 sd
->name
= ExAllocatePoolWithTag(PagedPool
, sd
->namelen
, ALLOC_TAG
);
1242 ERR("out of memory\n");
1243 return STATUS_INSUFFICIENT_RESOURCES
;
1246 RtlCopyMemory(sd
->name
, name
, sd
->namelen
);
1247 sd
->parent
= context
->root_dir
;
1249 send_command(context
, BTRFS_SEND_CMD_RENAME
);
1251 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH
, r
->sd
, r
->name
, r
->namelen
);
1253 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH_TO
, context
->root_dir
, name
, (UINT16
)strlen(name
));
1255 send_command_finish(context
, pos
);
1258 o
= ExAllocatePoolWithTag(PagedPool
, sizeof(orphan
), ALLOC_TAG
);
1260 ERR("out of memory\n");
1261 return STATUS_INSUFFICIENT_RESOURCES
;
1266 strcpy(o
->tmpname
, name
);
1268 add_orphan(context
, o
);
1270 return STATUS_SUCCESS
;
1273 static NTSTATUS
flush_refs(send_context
* context
, traverse_ptr
* tp1
, traverse_ptr
* tp2
) {
1276 ref
*nameref
= NULL
, *nameref2
= NULL
;
1278 if (context
->lastinode
.mode
& __S_IFDIR
) { // directory
1279 ref
* r
= IsListEmpty(&context
->lastinode
.refs
) ? NULL
: CONTAINING_RECORD(context
->lastinode
.refs
.Flink
, ref
, list_entry
);
1280 ref
* or = IsListEmpty(&context
->lastinode
.oldrefs
) ? NULL
: CONTAINING_RECORD(context
->lastinode
.oldrefs
.Flink
, ref
, list_entry
);
1282 if (or && !context
->lastinode
.o
) {
1283 ULONG len
= find_path_len(or->sd
, or->namelen
);
1285 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, len
+ 1, ALLOC_TAG
);
1286 if (!context
->lastinode
.path
) {
1287 ERR("out of memory\n");
1288 return STATUS_INSUFFICIENT_RESOURCES
;
1291 find_path(context
->lastinode
.path
, or->sd
, or->name
, or->namelen
);
1292 context
->lastinode
.path
[len
] = 0;
1294 if (!context
->lastinode
.sd
) {
1295 Status
= find_send_dir(context
, context
->lastinode
.inode
, context
->lastinode
.gen
, &context
->lastinode
.sd
, FALSE
);
1296 if (!NT_SUCCESS(Status
)) {
1297 ERR("find_send_dir returned %08x\n", Status
);
1307 Status
= look_for_collision(context
, r
->sd
, r
->name
, r
->namelen
, &inode
, &dir
);
1308 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_COLLISION
) {
1309 ERR("look_for_collision returned %08x\n", Status
);
1313 if (Status
== STATUS_OBJECT_NAME_COLLISION
&& inode
> context
->lastinode
.inode
) {
1314 Status
= make_file_orphan(context
, inode
, dir
, context
->parent
->root_item
.ctransid
, r
);
1315 if (!NT_SUCCESS(Status
)) {
1316 ERR("make_file_orphan returned %08x\n", Status
);
1321 if (context
->lastinode
.o
) {
1322 Status
= found_path(context
, r
->sd
, r
->name
, r
->namelen
);
1323 if (!NT_SUCCESS(Status
)) {
1324 ERR("found_path returned %08x\n", Status
);
1329 send_utimes_command_dir(context
, r
->sd
, &r
->sd
->atime
, &r
->sd
->mtime
, &r
->sd
->ctime
);
1330 } else if (r
->sd
!= or->sd
|| r
->namelen
!= or->namelen
|| RtlCompareMemory(r
->name
, or->name
, r
->namelen
) != r
->namelen
) { // moved or renamed
1331 ULONG pos
= context
->datalen
, len
;
1333 send_command(context
, BTRFS_SEND_CMD_RENAME
);
1335 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, (UINT16
)strlen(context
->lastinode
.path
));
1337 send_add_tlv_path(context
, BTRFS_SEND_TLV_PATH_TO
, r
->sd
, r
->name
, r
->namelen
);
1339 send_command_finish(context
, pos
);
1342 send_utimes_command_dir(context
, r
->sd
, &r
->sd
->atime
, &r
->sd
->mtime
, &r
->sd
->ctime
);
1344 if (context
->lastinode
.sd
->name
)
1345 ExFreePool(context
->lastinode
.sd
->name
);
1347 context
->lastinode
.sd
->name
= ExAllocatePoolWithTag(PagedPool
, r
->namelen
, ALLOC_TAG
);
1348 if (!context
->lastinode
.sd
->name
) {
1349 ERR("out of memory\n");
1350 return STATUS_INSUFFICIENT_RESOURCES
;
1353 RtlCopyMemory(context
->lastinode
.sd
->name
, r
->name
, r
->namelen
);
1354 context
->lastinode
.sd
->parent
= r
->sd
;
1356 if (context
->lastinode
.path
)
1357 ExFreePool(context
->lastinode
.path
);
1359 len
= find_path_len(r
->sd
, r
->namelen
);
1360 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, len
+ 1, ALLOC_TAG
);
1361 if (!context
->lastinode
.path
) {
1362 ERR("out of memory\n");
1363 return STATUS_INSUFFICIENT_RESOURCES
;
1366 find_path(context
->lastinode
.path
, r
->sd
, r
->name
, r
->namelen
);
1367 context
->lastinode
.path
[len
] = 0;
1369 } else if (r
&& !or) { // new
1370 Status
= found_path(context
, r
->sd
, r
->name
, r
->namelen
);
1371 if (!NT_SUCCESS(Status
)) {
1372 ERR("found_path returned %08x\n", Status
);
1377 send_utimes_command_dir(context
, r
->sd
, &r
->sd
->atime
, &r
->sd
->mtime
, &r
->sd
->ctime
);
1381 Status
= get_dir_last_child(context
, &last_inode
);
1382 if (!NT_SUCCESS(Status
)) {
1383 ERR("get_dir_last_child returned %08x\n", Status
);
1387 if (last_inode
<= context
->lastinode
.inode
) {
1388 send_rmdir_command(context
, (UINT16
)strlen(context
->lastinode
.path
), context
->lastinode
.path
);
1391 send_utimes_command_dir(context
, or->sd
, &or->sd
->atime
, &or->sd
->mtime
, &or->sd
->ctime
);
1394 ULONG pos
= context
->datalen
;
1396 Status
= get_orphan_name(context
, context
->lastinode
.inode
, context
->lastinode
.gen
, name
);
1397 if (!NT_SUCCESS(Status
)) {
1398 ERR("get_orphan_name returned %08x\n", Status
);
1402 send_command(context
, BTRFS_SEND_CMD_RENAME
);
1403 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, (UINT16
)strlen(context
->lastinode
.path
));
1404 send_add_tlv(context
, BTRFS_SEND_TLV_PATH_TO
, name
, (UINT16
)strlen(name
));
1405 send_command_finish(context
, pos
);
1407 if (context
->lastinode
.sd
->name
)
1408 ExFreePool(context
->lastinode
.sd
->name
);
1410 context
->lastinode
.sd
->name
= ExAllocatePoolWithTag(PagedPool
, strlen(name
), ALLOC_TAG
);
1411 if (!context
->lastinode
.sd
->name
) {
1412 ERR("out of memory\n");
1413 return STATUS_INSUFFICIENT_RESOURCES
;
1416 RtlCopyMemory(context
->lastinode
.sd
->name
, name
, strlen(name
));
1417 context
->lastinode
.sd
->namelen
= (UINT16
)strlen(name
);
1418 context
->lastinode
.sd
->dummy
= TRUE
;
1419 context
->lastinode
.sd
->parent
= NULL
;
1421 send_utimes_command(context
, NULL
, &context
->root_dir
->atime
, &context
->root_dir
->mtime
, &context
->root_dir
->ctime
);
1423 Status
= add_pending_rmdir(context
, last_inode
);
1424 if (!NT_SUCCESS(Status
)) {
1425 ERR("add_pending_rmdir returned %08x\n", Status
);
1431 while (!IsListEmpty(&context
->lastinode
.refs
)) {
1432 r
= CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.refs
), ref
, list_entry
);
1436 while (!IsListEmpty(&context
->lastinode
.oldrefs
)) {
1437 or = CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.oldrefs
), ref
, list_entry
);
1441 return STATUS_SUCCESS
;
1443 if (!IsListEmpty(&context
->lastinode
.oldrefs
)) {
1444 ref
* or = CONTAINING_RECORD(context
->lastinode
.oldrefs
.Flink
, ref
, list_entry
);
1445 ULONG len
= find_path_len(or->sd
, or->namelen
);
1447 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, len
+ 1, ALLOC_TAG
);
1448 if (!context
->lastinode
.path
) {
1449 ERR("out of memory\n");
1450 return STATUS_INSUFFICIENT_RESOURCES
;
1453 find_path(context
->lastinode
.path
, or->sd
, or->name
, or->namelen
);
1454 context
->lastinode
.path
[len
] = 0;
1458 // remove unchanged refs
1459 le
= context
->lastinode
.oldrefs
.Flink
;
1460 while (le
!= &context
->lastinode
.oldrefs
) {
1461 ref
* or = CONTAINING_RECORD(le
, ref
, list_entry
);
1463 BOOL matched
= FALSE
;
1465 le2
= context
->lastinode
.refs
.Flink
;
1466 while (le2
!= &context
->lastinode
.refs
) {
1467 ref
* r
= CONTAINING_RECORD(le2
, ref
, list_entry
);
1469 if (r
->sd
== or->sd
&& r
->namelen
== or->namelen
&& RtlCompareMemory(r
->name
, or->name
, r
->namelen
) == r
->namelen
) {
1470 RemoveEntryList(&r
->list_entry
);
1481 RemoveEntryList(&or->list_entry
);
1489 while (!IsListEmpty(&context
->lastinode
.refs
)) {
1490 ref
* r
= CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.refs
), ref
, list_entry
);
1494 if (context
->parent
) {
1495 Status
= look_for_collision(context
, r
->sd
, r
->name
, r
->namelen
, &inode
, &dir
);
1496 if (!NT_SUCCESS(Status
) && Status
!= STATUS_OBJECT_NAME_COLLISION
) {
1497 ERR("look_for_collision returned %08x\n", Status
);
1501 if (Status
== STATUS_OBJECT_NAME_COLLISION
&& inode
> context
->lastinode
.inode
) {
1502 Status
= make_file_orphan(context
, inode
, dir
, context
->lastinode
.gen
, r
);
1503 if (!NT_SUCCESS(Status
)) {
1504 ERR("make_file_orphan returned %08x\n", Status
);
1510 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
1511 Status
= wait_for_flush(context
, tp1
, tp2
);
1512 if (!NT_SUCCESS(Status
)) {
1513 ERR("wait_for_flush returned %08x\n", Status
);
1517 if (context
->send
->cancelling
)
1518 return STATUS_SUCCESS
;
1521 Status
= found_path(context
, r
->sd
, r
->name
, r
->namelen
);
1522 if (!NT_SUCCESS(Status
)) {
1523 ERR("found_path returned %08x\n", Status
);
1528 send_utimes_command_dir(context
, r
->sd
, &r
->sd
->atime
, &r
->sd
->mtime
, &r
->sd
->ctime
);
1530 if (nameref
&& !nameref2
)
1536 while (!IsListEmpty(&context
->lastinode
.oldrefs
)) {
1537 ref
* or = CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.oldrefs
), ref
, list_entry
);
1538 BOOL deleted
= FALSE
;
1540 le
= or->sd
->deleted_children
.Flink
;
1541 while (le
!= &or->sd
->deleted_children
) {
1542 deleted_child
* dc
= CONTAINING_RECORD(le
, deleted_child
, list_entry
);
1544 if (dc
->namelen
== or->namelen
&& RtlCompareMemory(dc
->name
, or->name
, or->namelen
) == or->namelen
) {
1545 RemoveEntryList(&dc
->list_entry
);
1555 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
1556 Status
= wait_for_flush(context
, tp1
, tp2
);
1557 if (!NT_SUCCESS(Status
)) {
1558 ERR("wait_for_flush returned %08x\n", Status
);
1562 if (context
->send
->cancelling
)
1563 return STATUS_SUCCESS
;
1566 Status
= send_unlink_command(context
, or->sd
, or->namelen
, or->name
);
1567 if (!NT_SUCCESS(Status
)) {
1568 ERR("send_unlink_command returned %08x\n", Status
);
1573 send_utimes_command_dir(context
, or->sd
, &or->sd
->atime
, &or->sd
->mtime
, &or->sd
->ctime
);
1576 if (or == nameref
&& nameref2
) {
1577 UINT16 len
= find_path_len(nameref2
->sd
, nameref2
->namelen
);
1579 if (context
->lastinode
.path
)
1580 ExFreePool(context
->lastinode
.path
);
1582 context
->lastinode
.path
= ExAllocatePoolWithTag(PagedPool
, len
+ 1, ALLOC_TAG
);
1583 if (!context
->lastinode
.path
) {
1584 ERR("out of memory\n");
1585 return STATUS_INSUFFICIENT_RESOURCES
;
1588 find_path(context
->lastinode
.path
, nameref2
->sd
, nameref2
->name
, nameref2
->namelen
);
1589 context
->lastinode
.path
[len
] = 0;
1591 ExFreePool(nameref2
);
1598 return STATUS_SUCCESS
;
1601 static NTSTATUS
wait_for_flush(send_context
* context
, traverse_ptr
* tp1
, traverse_ptr
* tp2
) {
1606 key1
= tp1
->item
->key
;
1609 key2
= tp2
->item
->key
;
1611 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
1613 KeClearEvent(&context
->send
->cleared_event
);
1614 KeSetEvent(&context
->buffer_event
, 0, TRUE
);
1615 KeWaitForSingleObject(&context
->send
->cleared_event
, Executive
, KernelMode
, FALSE
, NULL
);
1617 ExAcquireResourceSharedLite(&context
->Vcb
->tree_lock
, TRUE
);
1619 if (context
->send
->cancelling
)
1620 return STATUS_SUCCESS
;
1623 Status
= find_item(context
->Vcb
, context
->root
, tp1
, &key1
, FALSE
, NULL
);
1624 if (!NT_SUCCESS(Status
)) {
1625 ERR("find_item returned %08x\n", Status
);
1629 if (keycmp(tp1
->item
->key
, key1
)) {
1630 ERR("readonly subvolume changed\n");
1631 return STATUS_INTERNAL_ERROR
;
1636 Status
= find_item(context
->Vcb
, context
->parent
, tp2
, &key2
, FALSE
, NULL
);
1637 if (!NT_SUCCESS(Status
)) {
1638 ERR("find_item returned %08x\n", Status
);
1642 if (keycmp(tp2
->item
->key
, key2
)) {
1643 ERR("readonly subvolume changed\n");
1644 return STATUS_INTERNAL_ERROR
;
1648 return STATUS_SUCCESS
;
1651 static NTSTATUS
add_ext_holes(LIST_ENTRY
* exts
, UINT64 size
) {
1656 while (le
!= exts
) {
1657 send_ext
* ext
= CONTAINING_RECORD(le
, send_ext
, list_entry
);
1659 if (ext
->offset
> lastoff
) {
1660 send_ext
* ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(send_ext
, data
.data
) + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
1664 ERR("out of memory\n");
1665 return STATUS_INSUFFICIENT_RESOURCES
;
1668 ed2
= (EXTENT_DATA2
*)ext2
->data
.data
;
1670 ext2
->offset
= lastoff
;
1671 ext2
->datalen
= offsetof(EXTENT_DATA
, data
) + sizeof(EXTENT_DATA2
);
1672 ext2
->data
.decoded_size
= ed2
->num_bytes
= ext
->offset
- lastoff
;
1673 ext2
->data
.type
= EXTENT_TYPE_REGULAR
;
1674 ed2
->address
= ed2
->size
= ed2
->offset
= 0;
1676 InsertHeadList(le
->Blink
, &ext2
->list_entry
);
1679 if (ext
->data
.type
== EXTENT_TYPE_INLINE
)
1680 lastoff
= ext
->offset
+ ext
->data
.decoded_size
;
1682 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
.data
;
1683 lastoff
= ext
->offset
+ ed2
->num_bytes
;
1689 if (size
> lastoff
) {
1690 send_ext
* ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(send_ext
, data
.data
) + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
1694 ERR("out of memory\n");
1695 return STATUS_INSUFFICIENT_RESOURCES
;
1698 ed2
= (EXTENT_DATA2
*)ext2
->data
.data
;
1700 ext2
->offset
= lastoff
;
1701 ext2
->datalen
= offsetof(EXTENT_DATA
, data
) + sizeof(EXTENT_DATA2
);
1702 ext2
->data
.decoded_size
= ed2
->num_bytes
= size
- lastoff
;
1703 ext2
->data
.type
= EXTENT_TYPE_REGULAR
;
1704 ed2
->address
= ed2
->size
= ed2
->offset
= 0;
1706 InsertTailList(exts
, &ext2
->list_entry
);
1709 return STATUS_SUCCESS
;
1712 static NTSTATUS
divide_ext(send_ext
* ext
, UINT64 len
, BOOL trunc
) {
1714 EXTENT_DATA2
*ed2a
, *ed2b
;
1716 if (ext
->data
.type
== EXTENT_TYPE_INLINE
) {
1718 ext2
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(offsetof(send_ext
, data
.data
) + ext
->data
.decoded_size
- len
), ALLOC_TAG
);
1721 ERR("out of memory\n");
1722 return STATUS_INSUFFICIENT_RESOURCES
;
1725 ext2
->offset
= ext
->offset
+ len
;
1726 ext2
->datalen
= (ULONG
)(ext
->data
.decoded_size
- len
);
1727 ext2
->data
.decoded_size
= ext
->data
.decoded_size
- len
;
1728 ext2
->data
.compression
= ext
->data
.compression
;
1729 ext2
->data
.encryption
= ext
->data
.encryption
;
1730 ext2
->data
.encoding
= ext
->data
.encoding
;
1731 ext2
->data
.type
= ext
->data
.type
;
1732 RtlCopyMemory(ext2
->data
.data
, ext
->data
.data
+ len
, (ULONG
)(ext
->data
.decoded_size
- len
));
1734 InsertHeadList(&ext
->list_entry
, &ext2
->list_entry
);
1737 ext
->data
.decoded_size
= len
;
1739 return STATUS_SUCCESS
;
1742 ed2a
= (EXTENT_DATA2
*)ext
->data
.data
;
1745 ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(send_ext
, data
.data
) + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
1748 ERR("out of memory\n");
1749 return STATUS_INSUFFICIENT_RESOURCES
;
1752 ed2b
= (EXTENT_DATA2
*)ext2
->data
.data
;
1754 ext2
->offset
= ext
->offset
+ len
;
1755 ext2
->datalen
= offsetof(EXTENT_DATA
, data
) + sizeof(EXTENT_DATA2
);
1757 ext2
->data
.compression
= ext
->data
.compression
;
1758 ext2
->data
.encryption
= ext
->data
.encryption
;
1759 ext2
->data
.encoding
= ext
->data
.encoding
;
1760 ext2
->data
.type
= ext
->data
.type
;
1761 ed2b
->num_bytes
= ed2a
->num_bytes
- len
;
1763 if (ed2a
->size
== 0) {
1764 ext2
->data
.decoded_size
= ed2b
->num_bytes
;
1765 ext
->data
.decoded_size
= len
;
1767 ed2b
->address
= ed2b
->size
= ed2b
->offset
= 0;
1769 ext2
->data
.decoded_size
= ext
->data
.decoded_size
;
1771 ed2b
->address
= ed2a
->address
;
1772 ed2b
->size
= ed2a
->size
;
1773 ed2b
->offset
= ed2a
->offset
+ len
;
1776 InsertHeadList(&ext
->list_entry
, &ext2
->list_entry
);
1779 ed2a
->num_bytes
= len
;
1781 return STATUS_SUCCESS
;
1784 static NTSTATUS
sync_ext_cutoff_points(send_context
* context
) {
1786 send_ext
*ext1
, *ext2
;
1788 ext1
= CONTAINING_RECORD(context
->lastinode
.exts
.Flink
, send_ext
, list_entry
);
1789 ext2
= CONTAINING_RECORD(context
->lastinode
.oldexts
.Flink
, send_ext
, list_entry
);
1793 EXTENT_DATA2
*ed2a
, *ed2b
;
1795 ed2a
= ext1
->data
.type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ext1
->data
.data
;
1796 ed2b
= ext2
->data
.type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ext2
->data
.data
;
1798 len1
= ed2a
? ed2a
->num_bytes
: ext1
->data
.decoded_size
;
1799 len2
= ed2b
? ed2b
->num_bytes
: ext2
->data
.decoded_size
;
1802 Status
= divide_ext(ext2
, len1
, FALSE
);
1803 if (!NT_SUCCESS(Status
)) {
1804 ERR("divide_ext returned %08x\n", Status
);
1807 } else if (len2
< len1
) {
1808 Status
= divide_ext(ext1
, len2
, FALSE
);
1809 if (!NT_SUCCESS(Status
)) {
1810 ERR("divide_ext returned %08x\n", Status
);
1815 if (ext1
->list_entry
.Flink
== &context
->lastinode
.exts
|| ext2
->list_entry
.Flink
== &context
->lastinode
.oldexts
)
1818 ext1
= CONTAINING_RECORD(ext1
->list_entry
.Flink
, send_ext
, list_entry
);
1819 ext2
= CONTAINING_RECORD(ext2
->list_entry
.Flink
, send_ext
, list_entry
);
1822 ext1
= CONTAINING_RECORD(context
->lastinode
.exts
.Blink
, send_ext
, list_entry
);
1823 ext2
= CONTAINING_RECORD(context
->lastinode
.oldexts
.Blink
, send_ext
, list_entry
);
1825 Status
= divide_ext(ext1
, context
->lastinode
.size
- ext1
->offset
, TRUE
);
1826 if (!NT_SUCCESS(Status
)) {
1827 ERR("divide_ext returned %08x\n", Status
);
1831 Status
= divide_ext(ext2
, context
->lastinode
.size
- ext2
->offset
, TRUE
);
1832 if (!NT_SUCCESS(Status
)) {
1833 ERR("divide_ext returned %08x\n", Status
);
1837 return STATUS_SUCCESS
;
1840 static BOOL
send_add_tlv_clone_path(send_context
* context
, root
* r
, UINT64 inode
) {
1850 while (num
!= SUBVOL_ROOT_INODE
) {
1851 searchkey
.obj_id
= num
;
1852 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1853 searchkey
.offset
= 0xffffffffffffffff;
1855 Status
= find_item(context
->Vcb
, r
, &tp
, &searchkey
, FALSE
, NULL
);
1856 if (!NT_SUCCESS(Status
)) {
1857 ERR("find_item returned %08x\n", Status
);
1861 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| (tp
.item
->key
.obj_type
!= TYPE_INODE_REF
&& tp
.item
->key
.obj_type
!= TYPE_INODE_EXTREF
)) {
1862 ERR("could not find INODE_REF for inode %llx\n", searchkey
.obj_id
);
1869 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
1870 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
1872 if (tp
.item
->size
< sizeof(INODE_REF
) || tp
.item
->size
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
1873 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1878 num
= tp
.item
->key
.offset
;
1880 INODE_EXTREF
* ier
= (INODE_EXTREF
*)tp
.item
->data
;
1882 if (tp
.item
->size
< sizeof(INODE_EXTREF
) || tp
.item
->size
< offsetof(INODE_EXTREF
, name
[0]) + ier
->n
) {
1883 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1892 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_PATH
, NULL
, len
);
1893 ptr
= &context
->data
[context
->datalen
];
1897 while (num
!= SUBVOL_ROOT_INODE
) {
1898 searchkey
.obj_id
= num
;
1899 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1900 searchkey
.offset
= 0xffffffffffffffff;
1902 Status
= find_item(context
->Vcb
, r
, &tp
, &searchkey
, FALSE
, NULL
);
1903 if (!NT_SUCCESS(Status
)) {
1904 ERR("find_item returned %08x\n", Status
);
1908 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| (tp
.item
->key
.obj_type
!= TYPE_INODE_REF
&& tp
.item
->key
.obj_type
!= TYPE_INODE_EXTREF
)) {
1909 ERR("could not find INODE_REF for inode %llx\n", searchkey
.obj_id
);
1918 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
1919 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
1921 RtlCopyMemory(ptr
- ir
->n
, ir
->name
, ir
->n
);
1923 num
= tp
.item
->key
.offset
;
1925 INODE_EXTREF
* ier
= (INODE_EXTREF
*)tp
.item
->data
;
1927 RtlCopyMemory(ptr
- ier
->n
, ier
->name
, ier
->n
);
1936 static BOOL
try_clone_edr(send_context
* context
, send_ext
* se
, EXTENT_DATA_REF
* edr
) {
1941 EXTENT_DATA2
* seed2
= (EXTENT_DATA2
*)se
->data
.data
;
1943 if (context
->parent
&& edr
->root
== context
->parent
->id
)
1944 r
= context
->parent
;
1946 if (!r
&& context
->num_clones
> 0) {
1949 for (i
= 0; i
< context
->num_clones
; i
++) {
1950 if (context
->clones
[i
]->id
== edr
->root
&& context
->clones
[i
] != context
->root
) {
1951 r
= context
->clones
[i
];
1960 searchkey
.obj_id
= edr
->objid
;
1961 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
1962 searchkey
.offset
= 0;
1964 Status
= find_item(context
->Vcb
, r
, &tp
, &searchkey
, FALSE
, NULL
);
1965 if (!NT_SUCCESS(Status
)) {
1966 ERR("find_item returned %08x\n", Status
);
1971 traverse_ptr next_tp
;
1973 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
1974 if (tp
.item
->size
< sizeof(EXTENT_DATA
))
1975 ERR("(%llx,%x,%llx) has size %u, not at least %u as expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
1977 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
1979 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
1980 if (tp
.item
->size
< offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
))
1981 ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
1982 tp
.item
->size
, offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
));
1984 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1986 if (ed2
->address
== seed2
->address
&& ed2
->size
== seed2
->size
&& seed2
->offset
<= ed2
->offset
&& seed2
->offset
+ seed2
->num_bytes
>= ed2
->offset
+ ed2
->num_bytes
) {
1987 UINT64 clone_offset
= tp
.item
->key
.offset
+ ed2
->offset
- seed2
->offset
;
1988 UINT64 clone_len
= min(context
->lastinode
.size
- se
->offset
, ed2
->num_bytes
);
1990 if (clone_offset
% context
->Vcb
->superblock
.sector_size
== 0 && clone_len
% context
->Vcb
->superblock
.sector_size
== 0) {
1991 ULONG pos
= context
->datalen
;
1993 send_command(context
, BTRFS_SEND_CMD_CLONE
);
1995 send_add_tlv(context
, BTRFS_SEND_TLV_OFFSET
, &se
->offset
, sizeof(UINT64
));
1996 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_LENGTH
, &clone_len
, sizeof(UINT64
));
1997 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
1998 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_UUID
, r
->root_item
.rtransid
== 0 ? &r
->root_item
.uuid
: &r
->root_item
.received_uuid
, sizeof(BTRFS_UUID
));
1999 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_CTRANSID
, &r
->root_item
.ctransid
, sizeof(UINT64
));
2001 if (!send_add_tlv_clone_path(context
, r
, tp
.item
->key
.obj_id
))
2002 context
->datalen
= pos
;
2004 send_add_tlv(context
, BTRFS_SEND_TLV_CLONE_OFFSET
, &clone_offset
, sizeof(UINT64
));
2006 send_command_finish(context
, pos
);
2015 } else if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
> searchkey
.obj_type
))
2018 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
2027 static BOOL
try_clone(send_context
* context
, send_ext
* se
) {
2031 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)se
->data
.data
;
2035 searchkey
.obj_id
= ed2
->address
;
2036 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2037 searchkey
.offset
= ed2
->size
;
2039 Status
= find_item(context
->Vcb
, context
->Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
2040 if (!NT_SUCCESS(Status
)) {
2041 ERR("find_item returned %08x\n", Status
);
2045 if (keycmp(tp
.item
->key
, searchkey
)) {
2046 ERR("(%llx,%x,%llx) not found\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2050 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
2051 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
2055 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2057 if (tp
.item
->size
> sizeof(EXTENT_ITEM
)) {
2058 UINT32 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
2059 UINT8
* ptr
= (UINT8
*)&ei
[1];
2062 UINT8 secttype
= *ptr
;
2063 ULONG sectlen
= get_extent_data_len(secttype
);
2064 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
2068 if (sectlen
> len
) {
2069 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
2074 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
2080 if (secttype
== TYPE_EXTENT_DATA_REF
) {
2081 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)(ptr
+ sizeof(UINT8
));
2083 if (try_clone_edr(context
, se
, sectedr
))
2088 ptr
+= sizeof(UINT8
) + sectlen
;
2092 if (rc
>= ei
->refcount
)
2095 searchkey
.obj_type
= TYPE_EXTENT_DATA_REF
;
2096 searchkey
.offset
= 0;
2098 Status
= find_item(context
->Vcb
, context
->Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
2099 if (!NT_SUCCESS(Status
)) {
2100 ERR("find_item returned %08x\n", Status
);
2105 traverse_ptr next_tp
;
2107 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
2108 if (tp
.item
->size
< sizeof(EXTENT_DATA_REF
))
2109 ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA_REF
));
2111 if (try_clone_edr(context
, se
, (EXTENT_DATA_REF
*)tp
.item
->data
))
2114 } else if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
> searchkey
.obj_type
))
2117 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
2126 static NTSTATUS
flush_extents(send_context
* context
, traverse_ptr
* tp1
, traverse_ptr
* tp2
) {
2129 if ((IsListEmpty(&context
->lastinode
.exts
) && IsListEmpty(&context
->lastinode
.oldexts
)) || context
->lastinode
.size
== 0)
2130 return STATUS_SUCCESS
;
2132 if (context
->parent
) {
2133 Status
= add_ext_holes(&context
->lastinode
.exts
, context
->lastinode
.size
);
2134 if (!NT_SUCCESS(Status
)) {
2135 ERR("add_ext_holes returned %08x\n", Status
);
2139 Status
= add_ext_holes(&context
->lastinode
.oldexts
, context
->lastinode
.size
);
2140 if (!NT_SUCCESS(Status
)) {
2141 ERR("add_ext_holes returned %08x\n", Status
);
2145 Status
= sync_ext_cutoff_points(context
);
2146 if (!NT_SUCCESS(Status
)) {
2147 ERR("sync_ext_cutoff_points returned %08x\n", Status
);
2152 while (!IsListEmpty(&context
->lastinode
.exts
)) {
2153 send_ext
* se
= CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.exts
), send_ext
, list_entry
);
2154 send_ext
* se2
= context
->parent
? CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.oldexts
), send_ext
, list_entry
) : NULL
;
2159 if (se
->data
.type
== EXTENT_TYPE_INLINE
&& se2
->data
.type
== EXTENT_TYPE_INLINE
&&
2160 RtlCompareMemory(se
->data
.data
, se2
->data
.data
, (ULONG
)se
->data
.decoded_size
) == (ULONG
)se
->data
.decoded_size
) {
2166 if (se
->data
.type
== EXTENT_TYPE_REGULAR
&& se2
->data
.type
== EXTENT_TYPE_REGULAR
) {
2167 EXTENT_DATA2
*ed2a
, *ed2b
;
2169 ed2a
= (EXTENT_DATA2
*)se
->data
.data
;
2170 ed2b
= (EXTENT_DATA2
*)se2
->data
.data
;
2172 if (RtlCompareMemory(ed2a
, ed2b
, sizeof(EXTENT_DATA2
)) == sizeof(EXTENT_DATA2
)) {
2180 if (se
->data
.type
== EXTENT_TYPE_INLINE
) {
2181 pos
= context
->datalen
;
2183 send_command(context
, BTRFS_SEND_CMD_WRITE
);
2185 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2186 send_add_tlv(context
, BTRFS_SEND_TLV_OFFSET
, &se
->offset
, sizeof(UINT64
));
2188 if (se
->data
.compression
== BTRFS_COMPRESSION_NONE
)
2189 send_add_tlv(context
, BTRFS_SEND_TLV_DATA
, se
->data
.data
, (UINT16
)se
->data
.decoded_size
);
2190 else if (se
->data
.compression
== BTRFS_COMPRESSION_ZLIB
|| se
->data
.compression
== BTRFS_COMPRESSION_LZO
) {
2191 ULONG inlen
= se
->datalen
- (ULONG
)offsetof(EXTENT_DATA
, data
[0]);
2193 send_add_tlv(context
, BTRFS_SEND_TLV_DATA
, NULL
, (UINT16
)se
->data
.decoded_size
);
2194 RtlZeroMemory(&context
->data
[context
->datalen
- se
->data
.decoded_size
], (ULONG
)se
->data
.decoded_size
);
2196 if (se
->data
.compression
== BTRFS_COMPRESSION_ZLIB
) {
2197 Status
= zlib_decompress(se
->data
.data
, inlen
, &context
->data
[context
->datalen
- se
->data
.decoded_size
], (UINT32
)se
->data
.decoded_size
);
2198 if (!NT_SUCCESS(Status
)) {
2199 ERR("zlib_decompress returned %08x\n", Status
);
2201 if (se2
) ExFreePool(se2
);
2204 } else if (se
->data
.compression
== BTRFS_COMPRESSION_LZO
) {
2205 if (inlen
< sizeof(UINT32
)) {
2206 ERR("extent data was truncated\n");
2208 if (se2
) ExFreePool(se2
);
2209 return STATUS_INTERNAL_ERROR
;
2211 inlen
-= sizeof(UINT32
);
2213 Status
= lzo_decompress(se
->data
.data
+ sizeof(UINT32
), inlen
, &context
->data
[context
->datalen
- se
->data
.decoded_size
], (UINT32
)se
->data
.decoded_size
, sizeof(UINT32
));
2214 if (!NT_SUCCESS(Status
)) {
2215 ERR("lzo_decompress returned %08x\n", Status
);
2217 if (se2
) ExFreePool(se2
);
2222 ERR("unhandled compression type %x\n", se
->data
.compression
);
2224 if (se2
) ExFreePool(se2
);
2225 return STATUS_NOT_IMPLEMENTED
;
2228 send_command_finish(context
, pos
);
2231 if (se2
) ExFreePool(se2
);
2235 ed2
= (EXTENT_DATA2
*)se
->data
.data
;
2237 if (ed2
->size
!= 0 && (context
->parent
|| context
->num_clones
> 0)) {
2238 if (try_clone(context
, se
)) {
2240 if (se2
) ExFreePool(se2
);
2245 if (ed2
->size
== 0) { // write sparse
2248 for (off
= ed2
->offset
; off
< ed2
->offset
+ ed2
->num_bytes
; off
+= MAX_SEND_WRITE
) {
2249 UINT16 length
= (UINT16
)min(min(ed2
->offset
+ ed2
->num_bytes
- off
, MAX_SEND_WRITE
), context
->lastinode
.size
- se
->offset
- off
);
2251 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
2252 Status
= wait_for_flush(context
, tp1
, tp2
);
2253 if (!NT_SUCCESS(Status
)) {
2254 ERR("wait_for_flush returned %08x\n", Status
);
2256 if (se2
) ExFreePool(se2
);
2260 if (context
->send
->cancelling
) {
2262 if (se2
) ExFreePool(se2
);
2263 return STATUS_SUCCESS
;
2267 pos
= context
->datalen
;
2269 send_command(context
, BTRFS_SEND_CMD_WRITE
);
2271 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2273 offset
= se
->offset
+ off
;
2274 send_add_tlv(context
, BTRFS_SEND_TLV_OFFSET
, &offset
, sizeof(UINT64
));
2276 send_add_tlv(context
, BTRFS_SEND_TLV_DATA
, NULL
, length
);
2277 RtlZeroMemory(&context
->data
[context
->datalen
- length
], length
);
2279 send_command_finish(context
, pos
);
2281 } else if (se
->data
.compression
== BTRFS_COMPRESSION_NONE
) {
2285 buf
= ExAllocatePoolWithTag(NonPagedPool
, MAX_SEND_WRITE
+ (2 * context
->Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2287 ERR("out of memory\n");
2289 if (se2
) ExFreePool(se2
);
2290 return STATUS_INSUFFICIENT_RESOURCES
;
2293 for (off
= ed2
->offset
; off
< ed2
->offset
+ ed2
->num_bytes
; off
+= MAX_SEND_WRITE
) {
2294 UINT16 length
= (UINT16
)min(ed2
->offset
+ ed2
->num_bytes
- off
, MAX_SEND_WRITE
);
2296 UINT64 addr
= ed2
->address
+ off
;
2299 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
2300 Status
= wait_for_flush(context
, tp1
, tp2
);
2301 if (!NT_SUCCESS(Status
)) {
2302 ERR("wait_for_flush returned %08x\n", Status
);
2305 if (se2
) ExFreePool(se2
);
2309 if (context
->send
->cancelling
) {
2312 if (se2
) ExFreePool(se2
);
2313 return STATUS_SUCCESS
;
2317 skip_start
= addr
% context
->Vcb
->superblock
.sector_size
;
2320 if (context
->lastinode
.flags
& BTRFS_INODE_NODATASUM
)
2325 len
= (UINT32
)sector_align(length
+ skip_start
, context
->Vcb
->superblock
.sector_size
) / context
->Vcb
->superblock
.sector_size
;
2327 csum
= ExAllocatePoolWithTag(PagedPool
, len
* sizeof(UINT32
), ALLOC_TAG
);
2329 ERR("out of memory\n");
2332 if (se2
) ExFreePool(se2
);
2333 return STATUS_INSUFFICIENT_RESOURCES
;
2336 Status
= load_csum(context
->Vcb
, csum
, addr
, len
, NULL
);
2337 if (!NT_SUCCESS(Status
)) {
2338 ERR("load_csum returned %08x\n", Status
);
2342 if (se2
) ExFreePool(se2
);
2343 return STATUS_INSUFFICIENT_RESOURCES
;
2347 Status
= read_data(context
->Vcb
, addr
, (UINT32
)sector_align(length
+ skip_start
, context
->Vcb
->superblock
.sector_size
),
2348 csum
, FALSE
, buf
, NULL
, NULL
, NULL
, 0, FALSE
, NormalPagePriority
);
2349 if (!NT_SUCCESS(Status
)) {
2350 ERR("read_data returned %08x\n", Status
);
2353 if (se2
) ExFreePool(se2
);
2354 if (csum
) ExFreePool(csum
);
2361 pos
= context
->datalen
;
2363 send_command(context
, BTRFS_SEND_CMD_WRITE
);
2365 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2367 offset
= se
->offset
+ off
;
2368 send_add_tlv(context
, BTRFS_SEND_TLV_OFFSET
, &offset
, sizeof(UINT64
));
2370 length
= min((UINT16
)(context
->lastinode
.size
- se
->offset
- off
), length
);
2371 send_add_tlv(context
, BTRFS_SEND_TLV_DATA
, buf
+ skip_start
, length
);
2373 send_command_finish(context
, pos
);
2378 UINT8
*buf
, *compbuf
;
2382 buf
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)se
->data
.decoded_size
, ALLOC_TAG
);
2384 ERR("out of memory\n");
2386 if (se2
) ExFreePool(se2
);
2387 return STATUS_INSUFFICIENT_RESOURCES
;
2390 compbuf
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)ed2
->size
, ALLOC_TAG
);
2392 ERR("out of memory\n");
2395 if (se2
) ExFreePool(se2
);
2396 return STATUS_INSUFFICIENT_RESOURCES
;
2399 if (context
->lastinode
.flags
& BTRFS_INODE_NODATASUM
)
2404 len
= (UINT32
)(ed2
->size
/ context
->Vcb
->superblock
.sector_size
);
2406 csum
= ExAllocatePoolWithTag(PagedPool
, len
* sizeof(UINT32
), ALLOC_TAG
);
2408 ERR("out of memory\n");
2409 ExFreePool(compbuf
);
2412 if (se2
) ExFreePool(se2
);
2413 return STATUS_INSUFFICIENT_RESOURCES
;
2416 Status
= load_csum(context
->Vcb
, csum
, ed2
->address
, len
, NULL
);
2417 if (!NT_SUCCESS(Status
)) {
2418 ERR("load_csum returned %08x\n", Status
);
2420 ExFreePool(compbuf
);
2423 if (se2
) ExFreePool(se2
);
2428 Status
= read_data(context
->Vcb
, ed2
->address
, (UINT32
)ed2
->size
, csum
, FALSE
, compbuf
, NULL
, NULL
, NULL
, 0, FALSE
, NormalPagePriority
);
2429 if (!NT_SUCCESS(Status
)) {
2430 ERR("read_data returned %08x\n", Status
);
2431 ExFreePool(compbuf
);
2434 if (se2
) ExFreePool(se2
);
2435 if (csum
) ExFreePool(csum
);
2442 if (se
->data
.compression
== BTRFS_COMPRESSION_ZLIB
) {
2443 Status
= zlib_decompress(compbuf
, (UINT32
)ed2
->size
, buf
, (UINT32
)se
->data
.decoded_size
);
2444 if (!NT_SUCCESS(Status
)) {
2445 ERR("zlib_decompress returned %08x\n", Status
);
2446 ExFreePool(compbuf
);
2449 if (se2
) ExFreePool(se2
);
2452 } else if (se
->data
.compression
== BTRFS_COMPRESSION_LZO
) {
2453 Status
= lzo_decompress(&compbuf
[sizeof(UINT32
)], (UINT32
)ed2
->size
, buf
, (UINT32
)se
->data
.decoded_size
, sizeof(UINT32
));
2454 if (!NT_SUCCESS(Status
)) {
2455 ERR("lzo_decompress returned %08x\n", Status
);
2456 ExFreePool(compbuf
);
2459 if (se2
) ExFreePool(se2
);
2464 ExFreePool(compbuf
);
2466 for (off
= ed2
->offset
; off
< ed2
->offset
+ ed2
->num_bytes
; off
+= MAX_SEND_WRITE
) {
2467 UINT16 length
= (UINT16
)min(ed2
->offset
+ ed2
->num_bytes
- off
, MAX_SEND_WRITE
);
2470 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
2471 Status
= wait_for_flush(context
, tp1
, tp2
);
2472 if (!NT_SUCCESS(Status
)) {
2473 ERR("wait_for_flush returned %08x\n", Status
);
2476 if (se2
) ExFreePool(se2
);
2480 if (context
->send
->cancelling
) {
2483 if (se2
) ExFreePool(se2
);
2484 return STATUS_SUCCESS
;
2488 pos
= context
->datalen
;
2490 send_command(context
, BTRFS_SEND_CMD_WRITE
);
2492 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2494 offset
= se
->offset
+ off
;
2495 send_add_tlv(context
, BTRFS_SEND_TLV_OFFSET
, &offset
, sizeof(UINT64
));
2497 length
= min((UINT16
)(context
->lastinode
.size
- se
->offset
- off
), length
);
2498 send_add_tlv(context
, BTRFS_SEND_TLV_DATA
, &buf
[off
], length
);
2500 send_command_finish(context
, pos
);
2507 if (se2
) ExFreePool(se2
);
2510 return STATUS_SUCCESS
;
2513 static NTSTATUS
finish_inode(send_context
* context
, traverse_ptr
* tp1
, traverse_ptr
* tp2
) {
2516 if (!IsListEmpty(&context
->lastinode
.refs
) || !IsListEmpty(&context
->lastinode
.oldrefs
)) {
2517 NTSTATUS Status
= flush_refs(context
, tp1
, tp2
);
2518 if (!NT_SUCCESS(Status
)) {
2519 ERR("flush_refs returned %08x\n", Status
);
2523 if (context
->send
->cancelling
)
2524 return STATUS_SUCCESS
;
2527 if (!context
->lastinode
.deleting
) {
2528 if (context
->lastinode
.file
) {
2529 NTSTATUS Status
= flush_extents(context
, tp1
, tp2
);
2530 if (!NT_SUCCESS(Status
)) {
2531 ERR("flush_extents returned %08x\n", Status
);
2535 if (context
->send
->cancelling
)
2536 return STATUS_SUCCESS
;
2538 send_truncate_command(context
, context
->lastinode
.path
, context
->lastinode
.size
);
2541 if (context
->lastinode
.new || context
->lastinode
.uid
!= context
->lastinode
.olduid
|| context
->lastinode
.gid
!= context
->lastinode
.oldgid
)
2542 send_chown_command(context
, context
->lastinode
.path
, context
->lastinode
.uid
, context
->lastinode
.gid
);
2544 if (((context
->lastinode
.mode
& __S_IFLNK
) != __S_IFLNK
|| ((context
->lastinode
.mode
& 07777) != 0777)) &&
2545 (context
->lastinode
.new || context
->lastinode
.mode
!= context
->lastinode
.oldmode
))
2546 send_chmod_command(context
, context
->lastinode
.path
, context
->lastinode
.mode
);
2548 send_utimes_command(context
, context
->lastinode
.path
, &context
->lastinode
.atime
, &context
->lastinode
.mtime
, &context
->lastinode
.ctime
);
2551 while (!IsListEmpty(&context
->lastinode
.exts
)) {
2552 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.exts
), send_ext
, list_entry
));
2555 while (!IsListEmpty(&context
->lastinode
.oldexts
)) {
2556 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context
->lastinode
.oldexts
), send_ext
, list_entry
));
2559 if (context
->parent
) {
2560 le
= context
->pending_rmdirs
.Flink
;
2562 while (le
!= &context
->pending_rmdirs
) {
2563 pending_rmdir
* pr
= CONTAINING_RECORD(le
, pending_rmdir
, list_entry
);
2565 if (pr
->last_child_inode
<= context
->lastinode
.inode
) {
2568 send_rmdir_command(context
, pr
->sd
->namelen
, pr
->sd
->name
);
2570 RemoveEntryList(&pr
->sd
->list_entry
);
2573 ExFreePool(pr
->sd
->name
);
2575 while (!IsListEmpty(&pr
->sd
->deleted_children
)) {
2576 deleted_child
* dc
= CONTAINING_RECORD(RemoveHeadList(&pr
->sd
->deleted_children
), deleted_child
, list_entry
);
2582 RemoveEntryList(&pr
->list_entry
);
2589 context
->lastinode
.inode
= 0;
2590 context
->lastinode
.o
= NULL
;
2592 if (context
->lastinode
.path
) {
2593 ExFreePool(context
->lastinode
.path
);
2594 context
->lastinode
.path
= NULL
;
2597 return STATUS_SUCCESS
;
2600 static NTSTATUS
send_extent_data(send_context
* context
, traverse_ptr
* tp
, traverse_ptr
* tp2
) {
2603 if (tp
&& tp2
&& tp
->item
->size
== tp2
->item
->size
&& RtlCompareMemory(tp
->item
->data
, tp2
->item
->data
, tp
->item
->size
) == tp
->item
->size
)
2604 return STATUS_SUCCESS
;
2606 if (!IsListEmpty(&context
->lastinode
.refs
) || !IsListEmpty(&context
->lastinode
.oldrefs
)) {
2607 Status
= flush_refs(context
, tp
, tp2
);
2608 if (!NT_SUCCESS(Status
)) {
2609 ERR("flush_refs returned %08x\n", Status
);
2613 if (context
->send
->cancelling
)
2614 return STATUS_SUCCESS
;
2617 if ((context
->lastinode
.mode
& __S_IFLNK
) == __S_IFLNK
)
2618 return STATUS_SUCCESS
;
2622 EXTENT_DATA2
* ed2
= NULL
;
2624 if (tp
->item
->size
< sizeof(EXTENT_DATA
)) {
2625 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
2626 tp
->item
->size
, sizeof(EXTENT_DATA
));
2627 return STATUS_INTERNAL_ERROR
;
2630 ed
= (EXTENT_DATA
*)tp
->item
->data
;
2632 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
2633 ERR("unknown encryption type %u\n", ed
->encryption
);
2634 return STATUS_INTERNAL_ERROR
;
2637 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
2638 ERR("unknown encoding type %u\n", ed
->encoding
);
2639 return STATUS_INTERNAL_ERROR
;
2642 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
&& ed
->compression
!= BTRFS_COMPRESSION_ZLIB
&& ed
->compression
!= BTRFS_COMPRESSION_LZO
) {
2643 ERR("unknown compression type %u\n", ed
->compression
);
2644 return STATUS_INTERNAL_ERROR
;
2647 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
2648 if (tp
->item
->size
< offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
)) {
2649 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
2650 tp
->item
->size
, offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
));
2651 return STATUS_INTERNAL_ERROR
;
2654 ed2
= (EXTENT_DATA2
*)ed
->data
;
2655 } else if (ed
->type
== EXTENT_TYPE_INLINE
) {
2656 if (tp
->item
->size
< offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
&& ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2657 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
2658 tp
->item
->size
, offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
);
2659 return STATUS_INTERNAL_ERROR
;
2663 if ((ed
->type
== EXTENT_TYPE_INLINE
|| (ed
->type
== EXTENT_TYPE_REGULAR
&& ed2
->size
!= 0)) && ed
->decoded_size
!= 0) {
2664 send_ext
* se
= ExAllocatePoolWithTag(PagedPool
, offsetof(send_ext
, data
) + tp
->item
->size
, ALLOC_TAG
);
2667 ERR("out of memory\n");
2668 return STATUS_INSUFFICIENT_RESOURCES
;
2671 se
->offset
= tp
->item
->key
.offset
;
2672 se
->datalen
= tp
->item
->size
;
2673 RtlCopyMemory(&se
->data
, tp
->item
->data
, tp
->item
->size
);
2674 InsertTailList(&context
->lastinode
.exts
, &se
->list_entry
);
2680 EXTENT_DATA2
* ed2
= NULL
;
2682 if (tp2
->item
->size
< sizeof(EXTENT_DATA
)) {
2683 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
2684 tp2
->item
->size
, sizeof(EXTENT_DATA
));
2685 return STATUS_INTERNAL_ERROR
;
2688 ed
= (EXTENT_DATA
*)tp2
->item
->data
;
2690 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
2691 ERR("unknown encryption type %u\n", ed
->encryption
);
2692 return STATUS_INTERNAL_ERROR
;
2695 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
2696 ERR("unknown encoding type %u\n", ed
->encoding
);
2697 return STATUS_INTERNAL_ERROR
;
2700 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
&& ed
->compression
!= BTRFS_COMPRESSION_ZLIB
&& ed
->compression
!= BTRFS_COMPRESSION_LZO
) {
2701 ERR("unknown compression type %u\n", ed
->compression
);
2702 return STATUS_INTERNAL_ERROR
;
2705 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
2706 if (tp2
->item
->size
< offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
)) {
2707 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
2708 tp2
->item
->size
, offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
));
2709 return STATUS_INTERNAL_ERROR
;
2712 ed2
= (EXTENT_DATA2
*)ed
->data
;
2713 } else if (ed
->type
== EXTENT_TYPE_INLINE
) {
2714 if (tp2
->item
->size
< offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
) {
2715 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
2716 tp2
->item
->size
, offsetof(EXTENT_DATA
, data
[0]) + ed
->decoded_size
);
2717 return STATUS_INTERNAL_ERROR
;
2721 if ((ed
->type
== EXTENT_TYPE_INLINE
|| (ed
->type
== EXTENT_TYPE_REGULAR
&& ed2
->size
!= 0)) && ed
->decoded_size
!= 0) {
2722 send_ext
* se
= ExAllocatePoolWithTag(PagedPool
, offsetof(send_ext
, data
) + tp2
->item
->size
, ALLOC_TAG
);
2725 ERR("out of memory\n");
2726 return STATUS_INSUFFICIENT_RESOURCES
;
2729 se
->offset
= tp2
->item
->key
.offset
;
2730 se
->datalen
= tp2
->item
->size
;
2731 RtlCopyMemory(&se
->data
, tp2
->item
->data
, tp2
->item
->size
);
2732 InsertTailList(&context
->lastinode
.oldexts
, &se
->list_entry
);
2736 return STATUS_SUCCESS
;
2746 LIST_ENTRY list_entry
;
2749 static NTSTATUS
send_xattr(send_context
* context
, traverse_ptr
* tp
, traverse_ptr
* tp2
) {
2750 if (tp
&& tp2
&& tp
->item
->size
== tp2
->item
->size
&& RtlCompareMemory(tp
->item
->data
, tp2
->item
->data
, tp
->item
->size
) == tp
->item
->size
)
2751 return STATUS_SUCCESS
;
2753 if (!IsListEmpty(&context
->lastinode
.refs
) || !IsListEmpty(&context
->lastinode
.oldrefs
)) {
2754 NTSTATUS Status
= flush_refs(context
, tp
, tp2
);
2755 if (!NT_SUCCESS(Status
)) {
2756 ERR("flush_refs returned %08x\n", Status
);
2760 if (context
->send
->cancelling
)
2761 return STATUS_SUCCESS
;
2764 if (tp
&& tp
->item
->size
< sizeof(DIR_ITEM
)) {
2765 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
,
2766 tp
->item
->size
, sizeof(DIR_ITEM
));
2767 return STATUS_INTERNAL_ERROR
;
2770 if (tp2
&& tp2
->item
->size
< sizeof(DIR_ITEM
)) {
2771 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
,
2772 tp2
->item
->size
, sizeof(DIR_ITEM
));
2773 return STATUS_INTERNAL_ERROR
;
2780 len
= tp
->item
->size
;
2781 di
= (DIR_ITEM
*)tp
->item
->data
;
2786 if (len
< sizeof(DIR_ITEM
) || len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
2787 ERR("(%llx,%x,%llx) was truncated\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
2788 return STATUS_INTERNAL_ERROR
;
2791 pos
= context
->datalen
;
2792 send_command(context
, BTRFS_SEND_CMD_SET_XATTR
);
2793 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2794 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_NAME
, di
->name
, di
->n
);
2795 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_DATA
, &di
->name
[di
->n
], di
->m
);
2796 send_command_finish(context
, pos
);
2798 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
2799 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
2801 } else if (!tp
&& tp2
) {
2805 len
= tp2
->item
->size
;
2806 di
= (DIR_ITEM
*)tp2
->item
->data
;
2811 if (len
< sizeof(DIR_ITEM
) || len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
2812 ERR("(%llx,%x,%llx) was truncated\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
);
2813 return STATUS_INTERNAL_ERROR
;
2816 pos
= context
->datalen
;
2817 send_command(context
, BTRFS_SEND_CMD_REMOVE_XATTR
);
2818 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2819 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_NAME
, di
->name
, di
->n
);
2820 send_command_finish(context
, pos
);
2822 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
2823 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
2830 InitializeListHead(&xattrs
);
2832 len
= tp
->item
->size
;
2833 di
= (DIR_ITEM
*)tp
->item
->data
;
2838 if (len
< sizeof(DIR_ITEM
) || len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
2839 ERR("(%llx,%x,%llx) was truncated\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
2840 return STATUS_INTERNAL_ERROR
;
2843 xa
= ExAllocatePoolWithTag(PagedPool
, sizeof(xattr_cmp
), ALLOC_TAG
);
2845 ERR("out of memory\n");
2847 while (!IsListEmpty(&xattrs
)) {
2848 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs
), xattr_cmp
, list_entry
));
2851 return STATUS_INSUFFICIENT_RESOURCES
;
2854 xa
->namelen
= di
->n
;
2855 xa
->name
= di
->name
;
2856 xa
->value1len
= di
->m
;
2857 xa
->value1
= di
->name
+ di
->n
;
2861 InsertTailList(&xattrs
, &xa
->list_entry
);
2863 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
2864 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
2867 len
= tp2
->item
->size
;
2868 di
= (DIR_ITEM
*)tp2
->item
->data
;
2875 if (len
< sizeof(DIR_ITEM
) || len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
) {
2876 ERR("(%llx,%x,%llx) was truncated\n", tp2
->item
->key
.obj_id
, tp2
->item
->key
.obj_type
, tp2
->item
->key
.offset
);
2877 return STATUS_INTERNAL_ERROR
;
2881 while (le
!= &xattrs
) {
2882 xa
= CONTAINING_RECORD(le
, xattr_cmp
, list_entry
);
2884 if (xa
->namelen
== di
->n
&& RtlCompareMemory(xa
->name
, di
->name
, di
->n
) == di
->n
) {
2885 xa
->value2len
= di
->m
;
2886 xa
->value2
= di
->name
+ di
->n
;
2895 xa
= ExAllocatePoolWithTag(PagedPool
, sizeof(xattr_cmp
), ALLOC_TAG
);
2897 ERR("out of memory\n");
2899 while (!IsListEmpty(&xattrs
)) {
2900 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs
), xattr_cmp
, list_entry
));
2903 return STATUS_INSUFFICIENT_RESOURCES
;
2906 xa
->namelen
= di
->n
;
2907 xa
->name
= di
->name
;
2910 xa
->value2len
= di
->m
;
2911 xa
->value2
= di
->name
+ di
->n
;
2913 InsertTailList(&xattrs
, &xa
->list_entry
);
2916 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
2917 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
2920 while (!IsListEmpty(&xattrs
)) {
2921 xattr_cmp
* xa
= CONTAINING_RECORD(RemoveHeadList(&xattrs
), xattr_cmp
, list_entry
);
2923 if (xa
->value1len
!= xa
->value2len
|| !xa
->value1
|| !xa
->value2
|| RtlCompareMemory(xa
->value1
, xa
->value2
, xa
->value1len
) != xa
->value1len
) {
2927 pos
= context
->datalen
;
2928 send_command(context
, BTRFS_SEND_CMD_REMOVE_XATTR
);
2929 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2930 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_NAME
, xa
->name
, xa
->namelen
);
2931 send_command_finish(context
, pos
);
2933 pos
= context
->datalen
;
2934 send_command(context
, BTRFS_SEND_CMD_SET_XATTR
);
2935 send_add_tlv(context
, BTRFS_SEND_TLV_PATH
, context
->lastinode
.path
, context
->lastinode
.path
? (UINT16
)strlen(context
->lastinode
.path
) : 0);
2936 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_NAME
, xa
->name
, xa
->namelen
);
2937 send_add_tlv(context
, BTRFS_SEND_TLV_XATTR_DATA
, xa
->value1
, xa
->value1len
);
2938 send_command_finish(context
, pos
);
2946 return STATUS_SUCCESS
;
2949 _Function_class_(KSTART_ROUTINE
)
2951 static void NTAPI
send_thread(void* ctx
) {
2953 static void send_thread(void* ctx
) {
2955 send_context
* context
= (send_context
*)ctx
;
2958 traverse_ptr tp
, tp2
;
2960 InterlockedIncrement(&context
->root
->send_ops
);
2962 if (context
->parent
)
2963 InterlockedIncrement(&context
->parent
->send_ops
);
2965 if (context
->clones
) {
2968 for (i
= 0; i
< context
->num_clones
; i
++) {
2969 InterlockedIncrement(&context
->clones
[i
]->send_ops
);
2973 ExAcquireResourceExclusiveLite(&context
->Vcb
->tree_lock
, TRUE
);
2975 flush_subvol_fcbs(context
->root
);
2977 if (context
->parent
)
2978 flush_subvol_fcbs(context
->parent
);
2980 if (context
->Vcb
->need_write
)
2981 Status
= do_write(context
->Vcb
, NULL
);
2983 Status
= STATUS_SUCCESS
;
2985 free_trees(context
->Vcb
);
2987 if (!NT_SUCCESS(Status
)) {
2988 ERR("do_write returned %08x\n", Status
);
2989 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
2993 ExConvertExclusiveToSharedLite(&context
->Vcb
->tree_lock
);
2995 searchkey
.obj_id
= searchkey
.offset
= 0;
2996 searchkey
.obj_type
= 0;
2998 Status
= find_item(context
->Vcb
, context
->root
, &tp
, &searchkey
, FALSE
, NULL
);
2999 if (!NT_SUCCESS(Status
)) {
3000 ERR("find_item returned %08x\n", Status
);
3001 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3005 if (context
->parent
) {
3006 BOOL ended1
= FALSE
, ended2
= FALSE
;
3007 Status
= find_item(context
->Vcb
, context
->parent
, &tp2
, &searchkey
, FALSE
, NULL
);
3008 if (!NT_SUCCESS(Status
)) {
3009 ERR("find_item returned %08x\n", Status
);
3010 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3015 traverse_ptr next_tp
;
3017 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
3018 KEY key1
= tp
.item
->key
, key2
= tp2
.item
->key
;
3020 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3022 KeClearEvent(&context
->send
->cleared_event
);
3023 KeSetEvent(&context
->buffer_event
, 0, TRUE
);
3024 KeWaitForSingleObject(&context
->send
->cleared_event
, Executive
, KernelMode
, FALSE
, NULL
);
3026 if (context
->send
->cancelling
)
3029 ExAcquireResourceSharedLite(&context
->Vcb
->tree_lock
, TRUE
);
3032 Status
= find_item(context
->Vcb
, context
->root
, &tp
, &key1
, FALSE
, NULL
);
3033 if (!NT_SUCCESS(Status
)) {
3034 ERR("find_item returned %08x\n", Status
);
3035 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3039 if (keycmp(tp
.item
->key
, key1
)) {
3040 ERR("readonly subvolume changed\n");
3041 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3042 Status
= STATUS_INTERNAL_ERROR
;
3048 Status
= find_item(context
->Vcb
, context
->parent
, &tp2
, &key2
, FALSE
, NULL
);
3049 if (!NT_SUCCESS(Status
)) {
3050 ERR("find_item returned %08x\n", Status
);
3051 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3055 if (keycmp(tp2
.item
->key
, key2
)) {
3056 ERR("readonly subvolume changed\n");
3057 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3058 Status
= STATUS_INTERNAL_ERROR
;
3064 while (!ended1
&& !ended2
&& tp
.tree
->header
.address
== tp2
.tree
->header
.address
) {
3065 Status
= skip_to_difference(context
->Vcb
, &tp
, &tp2
, &ended1
, &ended2
);
3066 if (!NT_SUCCESS(Status
)) {
3067 ERR("skip_to_difference returned %08x\n", Status
);
3068 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3073 if (!ended1
&& !ended2
&& !keycmp(tp
.item
->key
, tp2
.item
->key
)) {
3074 BOOL no_next
= FALSE
, no_next2
= FALSE
;
3076 TRACE("~ %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3078 if (context
->lastinode
.inode
!= 0 && tp
.item
->key
.obj_id
> context
->lastinode
.inode
) {
3079 Status
= finish_inode(context
, ended1
? NULL
: &tp
, ended2
? NULL
: &tp2
);
3080 if (!NT_SUCCESS(Status
)) {
3081 ERR("finish_inode returned %08x\n", Status
);
3082 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3086 if (context
->send
->cancelling
) {
3087 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3092 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
) {
3093 if (tp
.item
->size
== tp2
.item
->size
&& tp
.item
->size
> 0 && RtlCompareMemory(tp
.item
->data
, tp2
.item
->data
, tp
.item
->size
) == tp
.item
->size
) {
3094 UINT64 inode
= tp
.item
->key
.obj_id
;
3097 if (!find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
)) {
3104 if (tp
.item
->key
.obj_id
!= inode
)
3109 if (!find_next_item(context
->Vcb
, &tp2
, &next_tp
, FALSE
, NULL
)) {
3116 if (tp2
.item
->key
.obj_id
!= inode
)
3121 } else if (tp
.item
->size
> sizeof(UINT64
) && tp2
.item
->size
> sizeof(UINT64
) && *(UINT64
*)tp
.item
->data
!= *(UINT64
*)tp2
.item
->data
) {
3122 UINT64 inode
= tp
.item
->key
.obj_id
;
3124 Status
= send_inode(context
, NULL
, &tp2
);
3125 if (!NT_SUCCESS(Status
)) {
3126 ERR("send_inode returned %08x\n", Status
);
3127 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3132 if (!find_next_item(context
->Vcb
, &tp2
, &next_tp
, FALSE
, NULL
)) {
3139 if (tp2
.item
->key
.obj_id
!= inode
)
3142 if (tp2
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3143 Status
= send_inode_ref(context
, &tp2
, TRUE
);
3144 if (!NT_SUCCESS(Status
)) {
3145 ERR("send_inode_ref returned %08x\n", Status
);
3146 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3149 } else if (tp2
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3150 Status
= send_inode_extref(context
, &tp2
, TRUE
);
3151 if (!NT_SUCCESS(Status
)) {
3152 ERR("send_inode_extref returned %08x\n", Status
);
3153 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3159 Status
= finish_inode(context
, ended1
? NULL
: &tp
, ended2
? NULL
: &tp2
);
3160 if (!NT_SUCCESS(Status
)) {
3161 ERR("finish_inode returned %08x\n", Status
);
3162 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3166 if (context
->send
->cancelling
) {
3167 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3173 Status
= send_inode(context
, &tp
, NULL
);
3174 if (!NT_SUCCESS(Status
)) {
3175 ERR("send_inode returned %08x\n", Status
);
3176 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3180 Status
= send_inode(context
, &tp
, &tp2
);
3181 if (!NT_SUCCESS(Status
)) {
3182 ERR("send_inode returned %08x\n", Status
);
3183 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3187 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3188 Status
= send_inode_ref(context
, &tp
, FALSE
);
3189 if (!NT_SUCCESS(Status
)) {
3190 ERR("send_inode_ref returned %08x\n", Status
);
3191 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3195 Status
= send_inode_ref(context
, &tp2
, TRUE
);
3196 if (!NT_SUCCESS(Status
)) {
3197 ERR("send_inode_ref returned %08x\n", Status
);
3198 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3201 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3202 Status
= send_inode_extref(context
, &tp
, FALSE
);
3203 if (!NT_SUCCESS(Status
)) {
3204 ERR("send_inode_extref returned %08x\n", Status
);
3205 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3209 Status
= send_inode_extref(context
, &tp2
, TRUE
);
3210 if (!NT_SUCCESS(Status
)) {
3211 ERR("send_inode_extref returned %08x\n", Status
);
3212 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3215 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
3216 Status
= send_extent_data(context
, &tp
, &tp2
);
3217 if (!NT_SUCCESS(Status
)) {
3218 ERR("send_extent_data returned %08x\n", Status
);
3219 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3223 if (context
->send
->cancelling
) {
3224 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3227 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
3228 Status
= send_xattr(context
, &tp
, &tp2
);
3229 if (!NT_SUCCESS(Status
)) {
3230 ERR("send_xattr returned %08x\n", Status
);
3231 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3235 if (context
->send
->cancelling
) {
3236 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3242 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
3248 if (find_next_item(context
->Vcb
, &tp2
, &next_tp
, FALSE
, NULL
))
3254 } else if (ended2
|| (!ended1
&& !ended2
&& keycmp(tp
.item
->key
, tp2
.item
->key
) == -1)) {
3255 TRACE("A %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3257 if (context
->lastinode
.inode
!= 0 && tp
.item
->key
.obj_id
> context
->lastinode
.inode
) {
3258 Status
= finish_inode(context
, ended1
? NULL
: &tp
, ended2
? NULL
: &tp2
);
3259 if (!NT_SUCCESS(Status
)) {
3260 ERR("finish_inode returned %08x\n", Status
);
3261 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3265 if (context
->send
->cancelling
) {
3266 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3271 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
) {
3272 Status
= send_inode(context
, &tp
, NULL
);
3273 if (!NT_SUCCESS(Status
)) {
3274 ERR("send_inode returned %08x\n", Status
);
3275 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3278 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3279 Status
= send_inode_ref(context
, &tp
, FALSE
);
3280 if (!NT_SUCCESS(Status
)) {
3281 ERR("send_inode_ref returned %08x\n", Status
);
3282 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3285 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3286 Status
= send_inode_extref(context
, &tp
, FALSE
);
3287 if (!NT_SUCCESS(Status
)) {
3288 ERR("send_inode_extref returned %08x\n", Status
);
3289 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3292 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
3293 Status
= send_extent_data(context
, &tp
, NULL
);
3294 if (!NT_SUCCESS(Status
)) {
3295 ERR("send_extent_data returned %08x\n", Status
);
3296 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3300 if (context
->send
->cancelling
) {
3301 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3304 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
3305 Status
= send_xattr(context
, &tp
, NULL
);
3306 if (!NT_SUCCESS(Status
)) {
3307 ERR("send_xattr returned %08x\n", Status
);
3308 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3312 if (context
->send
->cancelling
) {
3313 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3318 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
3322 } else if (ended1
|| (!ended1
&& !ended2
&& keycmp(tp
.item
->key
, tp2
.item
->key
) == 1)) {
3323 TRACE("B %llx,%x,%llx\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
);
3325 if (context
->lastinode
.inode
!= 0 && tp2
.item
->key
.obj_id
> context
->lastinode
.inode
) {
3326 Status
= finish_inode(context
, ended1
? NULL
: &tp
, ended2
? NULL
: &tp2
);
3327 if (!NT_SUCCESS(Status
)) {
3328 ERR("finish_inode returned %08x\n", Status
);
3329 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3333 if (context
->send
->cancelling
) {
3334 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3339 if (tp2
.item
->key
.obj_type
== TYPE_INODE_ITEM
) {
3340 Status
= send_inode(context
, NULL
, &tp2
);
3341 if (!NT_SUCCESS(Status
)) {
3342 ERR("send_inode returned %08x\n", Status
);
3343 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3346 } else if (tp2
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3347 Status
= send_inode_ref(context
, &tp2
, TRUE
);
3348 if (!NT_SUCCESS(Status
)) {
3349 ERR("send_inode_ref returned %08x\n", Status
);
3350 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3353 } else if (tp2
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3354 Status
= send_inode_extref(context
, &tp2
, TRUE
);
3355 if (!NT_SUCCESS(Status
)) {
3356 ERR("send_inode_extref returned %08x\n", Status
);
3357 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3360 } else if (tp2
.item
->key
.obj_type
== TYPE_EXTENT_DATA
&& !context
->lastinode
.deleting
) {
3361 Status
= send_extent_data(context
, NULL
, &tp2
);
3362 if (!NT_SUCCESS(Status
)) {
3363 ERR("send_extent_data returned %08x\n", Status
);
3364 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3368 if (context
->send
->cancelling
) {
3369 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3372 } else if (tp2
.item
->key
.obj_type
== TYPE_XATTR_ITEM
&& !context
->lastinode
.deleting
) {
3373 Status
= send_xattr(context
, NULL
, &tp2
);
3374 if (!NT_SUCCESS(Status
)) {
3375 ERR("send_xattr returned %08x\n", Status
);
3376 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3380 if (context
->send
->cancelling
) {
3381 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3386 if (find_next_item(context
->Vcb
, &tp2
, &next_tp
, FALSE
, NULL
))
3391 } while (!ended1
|| !ended2
);
3394 traverse_ptr next_tp
;
3396 if (context
->datalen
> SEND_BUFFER_LENGTH
) {
3397 KEY key
= tp
.item
->key
;
3399 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3401 KeClearEvent(&context
->send
->cleared_event
);
3402 KeSetEvent(&context
->buffer_event
, 0, TRUE
);
3403 KeWaitForSingleObject(&context
->send
->cleared_event
, Executive
, KernelMode
, FALSE
, NULL
);
3405 if (context
->send
->cancelling
)
3408 ExAcquireResourceSharedLite(&context
->Vcb
->tree_lock
, TRUE
);
3410 Status
= find_item(context
->Vcb
, context
->root
, &tp
, &key
, FALSE
, NULL
);
3411 if (!NT_SUCCESS(Status
)) {
3412 ERR("find_item returned %08x\n", Status
);
3413 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3417 if (keycmp(tp
.item
->key
, key
)) {
3418 ERR("readonly subvolume changed\n");
3419 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3420 Status
= STATUS_INTERNAL_ERROR
;
3425 if (context
->lastinode
.inode
!= 0 && tp
.item
->key
.obj_id
> context
->lastinode
.inode
) {
3426 Status
= finish_inode(context
, &tp
, NULL
);
3427 if (!NT_SUCCESS(Status
)) {
3428 ERR("finish_inode returned %08x\n", Status
);
3429 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3433 if (context
->send
->cancelling
) {
3434 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3439 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
) {
3440 Status
= send_inode(context
, &tp
, NULL
);
3441 if (!NT_SUCCESS(Status
)) {
3442 ERR("send_inode returned %08x\n", Status
);
3443 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3446 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3447 Status
= send_inode_ref(context
, &tp
, FALSE
);
3448 if (!NT_SUCCESS(Status
)) {
3449 ERR("send_inode_ref returned %08x\n", Status
);
3450 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3453 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3454 Status
= send_inode_extref(context
, &tp
, FALSE
);
3455 if (!NT_SUCCESS(Status
)) {
3456 ERR("send_inode_extref returned %08x\n", Status
);
3457 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3460 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
3461 Status
= send_extent_data(context
, &tp
, NULL
);
3462 if (!NT_SUCCESS(Status
)) {
3463 ERR("send_extent_data returned %08x\n", Status
);
3464 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3468 if (context
->send
->cancelling
) {
3469 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3472 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
3473 Status
= send_xattr(context
, &tp
, NULL
);
3474 if (!NT_SUCCESS(Status
)) {
3475 ERR("send_xattr returned %08x\n", Status
);
3476 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3480 if (context
->send
->cancelling
) {
3481 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3486 if (find_next_item(context
->Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
3493 if (context
->lastinode
.inode
!= 0) {
3494 Status
= finish_inode(context
, NULL
, NULL
);
3495 if (!NT_SUCCESS(Status
)) {
3496 ERR("finish_inode returned %08x\n", Status
);
3497 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3501 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3503 if (context
->send
->cancelling
)
3506 ExReleaseResourceLite(&context
->Vcb
->tree_lock
);
3508 KeClearEvent(&context
->send
->cleared_event
);
3509 KeSetEvent(&context
->buffer_event
, 0, TRUE
);
3510 KeWaitForSingleObject(&context
->send
->cleared_event
, Executive
, KernelMode
, FALSE
, NULL
);
3512 Status
= STATUS_SUCCESS
;
3515 if (!NT_SUCCESS(Status
)) {
3516 KeSetEvent(&context
->buffer_event
, 0, FALSE
);
3518 if (context
->send
->ccb
)
3519 context
->send
->ccb
->send_status
= Status
;
3522 ExAcquireResourceExclusiveLite(&context
->Vcb
->send_load_lock
, TRUE
);
3524 while (!IsListEmpty(&context
->orphans
)) {
3525 orphan
* o
= CONTAINING_RECORD(RemoveHeadList(&context
->orphans
), orphan
, list_entry
);
3529 while (!IsListEmpty(&context
->dirs
)) {
3530 send_dir
* sd
= CONTAINING_RECORD(RemoveHeadList(&context
->dirs
), send_dir
, list_entry
);
3533 ExFreePool(sd
->name
);
3535 while (!IsListEmpty(&sd
->deleted_children
)) {
3536 deleted_child
* dc
= CONTAINING_RECORD(RemoveHeadList(&sd
->deleted_children
), deleted_child
, list_entry
);
3543 ZwClose(context
->send
->thread
);
3544 context
->send
->thread
= NULL
;
3546 if (context
->send
->ccb
)
3547 context
->send
->ccb
->send
= NULL
;
3549 RemoveEntryList(&context
->send
->list_entry
);
3550 ExFreePool(context
->send
);
3551 ExFreePool(context
->data
);
3553 InterlockedDecrement(&context
->Vcb
->running_sends
);
3554 InterlockedDecrement(&context
->root
->send_ops
);
3556 if (context
->parent
)
3557 InterlockedDecrement(&context
->parent
->send_ops
);
3559 ExReleaseResourceLite(&context
->Vcb
->send_load_lock
);
3561 if (context
->clones
) {
3564 for (i
= 0; i
< context
->num_clones
; i
++) {
3565 InterlockedDecrement(&context
->clones
[i
]->send_ops
);
3568 ExFreePool(context
->clones
);
3571 ExFreePool(context
);
3573 PsTerminateSystemThread(STATUS_SUCCESS
);
3576 NTSTATUS
send_subvol(device_extension
* Vcb
, void* data
, ULONG datalen
, PFILE_OBJECT FileObject
, PIRP Irp
) {
3580 root
* parsubvol
= NULL
;
3581 send_context
* context
;
3583 ULONG num_clones
= 0;
3584 root
** clones
= NULL
;
3586 if (!FileObject
|| !FileObject
->FsContext
|| !FileObject
->FsContext2
|| FileObject
->FsContext
== Vcb
->volume_fcb
)
3587 return STATUS_INVALID_PARAMETER
;
3589 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), Irp
->RequestorMode
))
3590 return STATUS_PRIVILEGE_NOT_HELD
;
3592 fcb
= FileObject
->FsContext
;
3593 ccb
= FileObject
->FsContext2
;
3595 if (fcb
->inode
!= SUBVOL_ROOT_INODE
|| fcb
== Vcb
->root_fileref
->fcb
)
3596 return STATUS_INVALID_PARAMETER
;
3598 if (!Vcb
->readonly
&& !(fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
3599 return STATUS_INVALID_PARAMETER
;
3602 btrfs_send_subvol
* bss
= (btrfs_send_subvol
*)data
;
3606 if (IoIs32bitProcess(Irp
)) {
3607 btrfs_send_subvol32
* bss32
= (btrfs_send_subvol32
*)data
;
3609 if (datalen
< offsetof(btrfs_send_subvol32
, num_clones
))
3610 return STATUS_INVALID_PARAMETER
;
3612 parent
= Handle32ToHandle(bss32
->parent
);
3614 if (datalen
>= offsetof(btrfs_send_subvol32
, clones
[0]))
3615 num_clones
= bss32
->num_clones
;
3617 if (datalen
< offsetof(btrfs_send_subvol32
, clones
[0]) + (num_clones
* sizeof(UINT32
)))
3618 return STATUS_INVALID_PARAMETER
;
3621 if (datalen
< offsetof(btrfs_send_subvol
, num_clones
))
3622 return STATUS_INVALID_PARAMETER
;
3624 parent
= bss
->parent
;
3626 if (datalen
>= offsetof(btrfs_send_subvol
, clones
[0]))
3627 num_clones
= bss
->num_clones
;
3629 if (datalen
< offsetof(btrfs_send_subvol
, clones
[0]) + (num_clones
* sizeof(HANDLE
)))
3630 return STATUS_INVALID_PARAMETER
;
3636 PFILE_OBJECT fileobj
;
3637 struct _fcb
* parfcb
;
3639 Status
= ObReferenceObjectByHandle(parent
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
3640 if (!NT_SUCCESS(Status
)) {
3641 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
3645 if (fileobj
->DeviceObject
!= FileObject
->DeviceObject
) {
3646 ObDereferenceObject(fileobj
);
3647 return STATUS_INVALID_PARAMETER
;
3650 parfcb
= fileobj
->FsContext
;
3652 if (!parfcb
|| parfcb
== Vcb
->root_fileref
->fcb
|| parfcb
== Vcb
->volume_fcb
|| parfcb
->inode
!= SUBVOL_ROOT_INODE
) {
3653 ObDereferenceObject(fileobj
);
3654 return STATUS_INVALID_PARAMETER
;
3657 parsubvol
= parfcb
->subvol
;
3658 ObDereferenceObject(fileobj
);
3660 if (!Vcb
->readonly
&& !(parsubvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
))
3661 return STATUS_INVALID_PARAMETER
;
3663 if (parsubvol
== fcb
->subvol
)
3664 return STATUS_INVALID_PARAMETER
;
3667 if (num_clones
> 0) {
3670 clones
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
*) * num_clones
, ALLOC_TAG
);
3672 ERR("out of memory\n");
3673 return STATUS_INSUFFICIENT_RESOURCES
;
3676 for (i
= 0; i
< num_clones
; i
++) {
3678 PFILE_OBJECT fileobj
;
3679 struct _fcb
* clonefcb
;
3682 if (IoIs32bitProcess(Irp
)) {
3683 btrfs_send_subvol32
* bss32
= (btrfs_send_subvol32
*)data
;
3685 h
= Handle32ToHandle(bss32
->clones
[i
]);
3690 Status
= ObReferenceObjectByHandle(h
, 0, *IoFileObjectType
, Irp
->RequestorMode
, (void**)&fileobj
, NULL
);
3691 if (!NT_SUCCESS(Status
)) {
3692 ERR("ObReferenceObjectByHandle returned %08x\n", Status
);
3697 if (fileobj
->DeviceObject
!= FileObject
->DeviceObject
) {
3698 ObDereferenceObject(fileobj
);
3700 return STATUS_INVALID_PARAMETER
;
3703 clonefcb
= fileobj
->FsContext
;
3705 if (!clonefcb
|| clonefcb
== Vcb
->root_fileref
->fcb
|| clonefcb
== Vcb
->volume_fcb
|| clonefcb
->inode
!= SUBVOL_ROOT_INODE
) {
3706 ObDereferenceObject(fileobj
);
3708 return STATUS_INVALID_PARAMETER
;
3711 clones
[i
] = clonefcb
->subvol
;
3712 ObDereferenceObject(fileobj
);
3714 if (!Vcb
->readonly
&& !(clones
[i
]->root_item
.flags
& BTRFS_SUBVOL_READONLY
)) {
3716 return STATUS_INVALID_PARAMETER
;
3722 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
3725 WARN("send operation already running\n");
3726 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3727 return STATUS_DEVICE_NOT_READY
;
3730 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(send_context
), ALLOC_TAG
);
3732 ERR("out of memory\n");
3733 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3734 return STATUS_INSUFFICIENT_RESOURCES
;
3738 context
->root
= fcb
->subvol
;
3739 context
->parent
= parsubvol
;
3740 InitializeListHead(&context
->orphans
);
3741 InitializeListHead(&context
->dirs
);
3742 InitializeListHead(&context
->pending_rmdirs
);
3743 context
->lastinode
.inode
= 0;
3744 context
->lastinode
.path
= NULL
;
3745 context
->lastinode
.sd
= NULL
;
3746 context
->root_dir
= NULL
;
3747 context
->num_clones
= num_clones
;
3748 context
->clones
= clones
;
3749 InitializeListHead(&context
->lastinode
.refs
);
3750 InitializeListHead(&context
->lastinode
.oldrefs
);
3751 InitializeListHead(&context
->lastinode
.exts
);
3752 InitializeListHead(&context
->lastinode
.oldexts
);
3754 context
->data
= ExAllocatePoolWithTag(PagedPool
, SEND_BUFFER_LENGTH
+ (2 * MAX_SEND_WRITE
), ALLOC_TAG
); // give ourselves some wiggle room
3755 if (!context
->data
) {
3756 ExFreePool(context
);
3757 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3758 return STATUS_INSUFFICIENT_RESOURCES
;
3761 context
->datalen
= 0;
3763 send_subvol_header(context
, fcb
->subvol
, ccb
->fileref
); // FIXME - fileref needs some sort of lock here
3765 KeInitializeEvent(&context
->buffer_event
, NotificationEvent
, FALSE
);
3767 send
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(send_info
), ALLOC_TAG
);
3769 ERR("out of memory\n");
3770 ExFreePool(context
->data
);
3771 ExFreePool(context
);
3772 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3773 return STATUS_INSUFFICIENT_RESOURCES
;
3776 KeInitializeEvent(&send
->cleared_event
, NotificationEvent
, FALSE
);
3778 send
->context
= context
;
3779 context
->send
= send
;
3783 ccb
->send_status
= STATUS_SUCCESS
;
3785 send
->cancelling
= FALSE
;
3787 InterlockedIncrement(&Vcb
->running_sends
);
3789 Status
= PsCreateSystemThread(&send
->thread
, 0, NULL
, NULL
, NULL
, send_thread
, context
);
3790 if (!NT_SUCCESS(Status
)) {
3791 ERR("PsCreateSystemThread returned %08x\n", Status
);
3793 InterlockedDecrement(&Vcb
->running_sends
);
3795 ExFreePool(context
->data
);
3796 ExFreePool(context
);
3797 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3801 InsertTailList(&Vcb
->send_ops
, &send
->list_entry
);
3802 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3804 return STATUS_SUCCESS
;
3807 NTSTATUS
read_send_buffer(device_extension
* Vcb
, PFILE_OBJECT FileObject
, void* data
, ULONG datalen
, ULONG_PTR
* retlen
, KPROCESSOR_MODE processor_mode
) {
3809 send_context
* context
;
3811 ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
3813 return STATUS_INVALID_PARAMETER
;
3815 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3816 return STATUS_PRIVILEGE_NOT_HELD
;
3818 ExAcquireResourceExclusiveLite(&Vcb
->send_load_lock
, TRUE
);
3821 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3822 return !NT_SUCCESS(ccb
->send_status
) ? ccb
->send_status
: STATUS_END_OF_FILE
;
3825 context
= (send_context
*)ccb
->send
->context
;
3827 KeWaitForSingleObject(&context
->buffer_event
, Executive
, KernelMode
, FALSE
, NULL
);
3830 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3831 return STATUS_SUCCESS
;
3834 RtlCopyMemory(data
, context
->data
, min(datalen
, context
->datalen
));
3836 if (datalen
< context
->datalen
) { // not empty yet
3838 RtlMoveMemory(context
->data
, &context
->data
[datalen
], context
->datalen
- datalen
);
3839 context
->datalen
-= datalen
;
3840 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3842 *retlen
= context
->datalen
;
3843 context
->datalen
= 0;
3844 ExReleaseResourceLite(&Vcb
->send_load_lock
);
3846 KeClearEvent(&context
->buffer_event
);
3847 KeSetEvent(&ccb
->send
->cleared_event
, 0, FALSE
);
3850 return STATUS_SUCCESS
;