1 /* Copyright (c) Mark Harmstone 2016
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 static NTSTATUS STDCALL
move_subvol(device_extension
* Vcb
, fcb
* fcb
, root
* destsubvol
, UINT64 destinode
, PANSI_STRING utf8
, UINT32 crc32
, UINT32 oldcrc32
, BTRFS_TIME
* now
, BOOL ReplaceIfExists
, LIST_ENTRY
* rollback
);
22 static NTSTATUS STDCALL
set_basic_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, LIST_ENTRY
* rollback
) {
23 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
24 fcb
* fcb
= FileObject
->FsContext
;
26 BOOL inode_item_changed
= FALSE
;
32 TRACE("file = %.*S, attributes = %x\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, fbi
->FileAttributes
);
34 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
35 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
36 return STATUS_INVALID_PARAMETER
;
39 // FIXME - what if FCB is volume or root?
40 // FIXME - what about subvol roots?
42 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
43 // FIXME - handle times == -1
45 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
46 if (fbi
->FileAttributes
!= 0) {
50 defda
= get_file_attributes(Vcb
, &fcb
->inode_item
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fcb
->filepart
.Length
> 0 && fcb
->filepart
.Buffer
[0] == '.', TRUE
);
52 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
53 fbi
->FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
54 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
55 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
58 if (defda
!= fbi
->FileAttributes
) {
61 TRACE("inserting new DOSATTRIB xattr\n");
62 sprintf(val
, "0x%lx", fbi
->FileAttributes
);
64 Status
= set_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
*)val
, strlen(val
), rollback
);
65 if (!NT_SUCCESS(Status
)) {
66 ERR("set_xattr returned %08x\n", Status
);
70 delete_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, rollback
);
72 fcb
->atts
= fbi
->FileAttributes
;
74 KeQuerySystemTime(&time
);
75 win_time_to_unix(time
, &now
);
77 fcb
->inode_item
.st_ctime
= now
;
78 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
79 fcb
->subvol
->root_item
.ctime
= now
;
81 inode_item_changed
= TRUE
;
84 // FIXME - CreationTime
85 // FIXME - LastAccessTime
86 // FIXME - LastWriteTime
89 if (inode_item_changed
) {
94 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
95 fcb
->inode_item
.sequence
++;
97 searchkey
.obj_id
= fcb
->inode
;
98 searchkey
.obj_type
= TYPE_INODE_ITEM
;
99 searchkey
.offset
= 0xffffffffffffffff;
101 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
102 if (!NT_SUCCESS(Status
)) {
103 ERR("error - find_item returned %08x\n", Status
);
107 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
108 delete_tree_item(Vcb
, &tp
, rollback
);
110 WARN("couldn't find old INODE_ITEM\n");
112 free_traverse_ptr(&tp
);
114 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
116 ERR("out of memory\n");
117 return STATUS_INSUFFICIENT_RESOURCES
;
120 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
122 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
)) {
123 ERR("error - failed to insert item\n");
125 return STATUS_INTERNAL_ERROR
;
129 return STATUS_SUCCESS
;
132 static NTSTATUS STDCALL
set_disposition_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
133 FILE_DISPOSITION_INFORMATION
* fdi
= Irp
->AssociatedIrp
.SystemBuffer
;
134 fcb
* fcb
= FileObject
->FsContext
;
137 TRACE("changing delete_on_close to %s for %.*S (fcb %p)\n", fdi
->DeleteFile
? "TRUE" : "FALSE", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, fcb
);
139 atts
= fcb
->ads
? fcb
->par
->atts
: fcb
->atts
;
140 TRACE("atts = %x\n", atts
);
142 if (atts
& FILE_ATTRIBUTE_READONLY
)
143 return STATUS_CANNOT_DELETE
;
145 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0)
146 return STATUS_DIRECTORY_NOT_EMPTY
;
148 if (!MmFlushImageSection(&fcb
->nonpaged
->segment_object
, MmFlushForDelete
)) {
149 WARN("trying to delete file which is being mapped as an image\n");
150 return STATUS_CANNOT_DELETE
;
153 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
154 FIXME("FIXME - subvol deletion not yet supported\n");
155 return STATUS_INTERNAL_ERROR
;
158 fcb
->delete_on_close
= fdi
->DeleteFile
;
159 // FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
160 FileObject
->DeletePending
= fdi
->DeleteFile
;
162 return STATUS_SUCCESS
;
165 static NTSTATUS
add_inode_extref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, UINT64 index
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
171 searchkey
.obj_id
= inode
;
172 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
173 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
175 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
176 if (!NT_SUCCESS(Status
)) {
177 ERR("error - find_item returned %08x\n", Status
);
181 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
182 ULONG iersize
= tp
.item
->size
+ sizeof(INODE_EXTREF
) - 1 + utf8
->Length
;
184 UINT32 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
186 if (iersize
> maxlen
) {
187 ERR("item would be too long (%u > %u)\n", iersize
, maxlen
);
188 free_traverse_ptr(&tp
);
189 return STATUS_INTERNAL_ERROR
;
192 ier2
= ExAllocatePoolWithTag(PagedPool
, iersize
, ALLOC_TAG
);
194 ERR("out of memory\n");
195 free_traverse_ptr(&tp
);
196 return STATUS_INSUFFICIENT_RESOURCES
;
199 if (tp
.item
->size
> 0)
200 RtlCopyMemory(ier2
, tp
.item
->data
, tp
.item
->size
);
202 ier
= (INODE_EXTREF
*)&ier2
[tp
.item
->size
];
205 ier
->n
= utf8
->Length
;
206 RtlCopyMemory(ier
->name
, utf8
->Buffer
, utf8
->Length
);
208 delete_tree_item(Vcb
, &tp
, rollback
);
210 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ier2
, iersize
, NULL
, rollback
)) {
211 ERR("error - failed to insert item\n");
212 free_traverse_ptr(&tp
);
213 return STATUS_INTERNAL_ERROR
;
216 ier
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_EXTREF
) - 1 + utf8
->Length
, ALLOC_TAG
);
218 ERR("out of memory\n");
219 free_traverse_ptr(&tp
);
220 return STATUS_INSUFFICIENT_RESOURCES
;
225 ier
->n
= utf8
->Length
;
226 RtlCopyMemory(ier
->name
, utf8
->Buffer
, utf8
->Length
);
228 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ier
, sizeof(INODE_EXTREF
) - 1 + utf8
->Length
, NULL
, rollback
)) {
229 ERR("error - failed to insert item\n");
230 free_traverse_ptr(&tp
);
231 return STATUS_INTERNAL_ERROR
;
235 free_traverse_ptr(&tp
);
237 return STATUS_SUCCESS
;
240 NTSTATUS
add_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, UINT64 index
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
246 searchkey
.obj_id
= inode
;
247 searchkey
.obj_type
= TYPE_INODE_REF
;
248 searchkey
.offset
= parinode
;
250 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
251 if (!NT_SUCCESS(Status
)) {
252 ERR("error - find_item returned %08x\n", Status
);
256 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
257 ULONG irsize
= tp
.item
->size
+ sizeof(INODE_REF
) - 1 + utf8
->Length
;
259 UINT32 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
261 if (irsize
> maxlen
) {
262 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
263 TRACE("INODE_REF too long, creating INODE_EXTREF\n");
264 free_traverse_ptr(&tp
);
265 return add_inode_extref(Vcb
, subvol
, inode
, parinode
, index
, utf8
, rollback
);
267 ERR("item would be too long (%u > %u)\n", irsize
, maxlen
);
268 free_traverse_ptr(&tp
);
269 return STATUS_INTERNAL_ERROR
;
273 ir2
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
275 ERR("out of memory\n");
276 free_traverse_ptr(&tp
);
277 return STATUS_INSUFFICIENT_RESOURCES
;
280 if (tp
.item
->size
> 0)
281 RtlCopyMemory(ir2
, tp
.item
->data
, tp
.item
->size
);
283 ir
= (INODE_REF
*)&ir2
[tp
.item
->size
];
285 ir
->n
= utf8
->Length
;
286 RtlCopyMemory(ir
->name
, utf8
->Buffer
, utf8
->Length
);
288 delete_tree_item(Vcb
, &tp
, rollback
);
290 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ir2
, irsize
, NULL
, rollback
)) {
291 ERR("error - failed to insert item\n");
292 free_traverse_ptr(&tp
);
293 return STATUS_INTERNAL_ERROR
;
296 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + utf8
->Length
, ALLOC_TAG
);
298 ERR("out of memory\n");
299 free_traverse_ptr(&tp
);
300 return STATUS_INSUFFICIENT_RESOURCES
;
304 ir
->n
= utf8
->Length
;
305 RtlCopyMemory(ir
->name
, utf8
->Buffer
, utf8
->Length
);
307 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ir
, sizeof(INODE_REF
) - 1 + ir
->n
, NULL
, rollback
)) {
308 ERR("error - failed to insert item\n");
309 free_traverse_ptr(&tp
);
310 return STATUS_INTERNAL_ERROR
;
314 free_traverse_ptr(&tp
);
316 return STATUS_SUCCESS
;
319 static NTSTATUS
get_fcb_from_dir_item(device_extension
* Vcb
, fcb
** pfcb
, fcb
* parent
, root
* subvol
, DIR_ITEM
* di
) {
327 le
= parent
->children
.Flink
;
329 while (le
!= &parent
->children
) {
330 c
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
332 if (c
->refcount
> 0 && c
->inode
== di
->key
.obj_id
&& c
->subvol
== subvol
) {
334 #ifdef DEBUG_FCB_REFCOUNTS
335 WARN("fcb %p: refcount now %i (%.*S)\n", c
, c
->refcount
, c
->full_filename
.Length
/ sizeof(WCHAR
), c
->full_filename
.Buffer
);
338 return STATUS_SUCCESS
;
346 ERR("out of memory\n");
347 return STATUS_INSUFFICIENT_RESOURCES
;
352 sf2
->utf8
.Length
= sf2
->utf8
.MaximumLength
= di
->n
;
353 sf2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->n
, ALLOC_TAG
);
354 if (!sf2
->utf8
.Buffer
) {
355 ERR("out of memory\n");
356 return STATUS_INSUFFICIENT_RESOURCES
;
359 RtlCopyMemory(sf2
->utf8
.Buffer
, di
->name
, di
->n
);
364 #ifdef DEBUG_FCB_REFCOUNTS
365 WARN("fcb %p: refcount now %i (%.*S)\n", parent
, parent
->refcount
, parent
->full_filename
.Length
/ sizeof(WCHAR
), parent
->full_filename
.Buffer
);
368 if (di
->key
.obj_type
== TYPE_ROOT_ITEM
) {
369 root
* fcbroot
= Vcb
->roots
;
370 while (fcbroot
&& fcbroot
->id
!= di
->key
.obj_id
)
371 fcbroot
= fcbroot
->next
;
373 sf2
->subvol
= fcbroot
;
374 sf2
->inode
= SUBVOL_ROOT_INODE
;
376 sf2
->subvol
= subvol
;
377 sf2
->inode
= di
->key
.obj_id
;
380 sf2
->type
= di
->type
;
383 Vcb
->fcbs
->prev
= sf2
;
385 sf2
->next
= Vcb
->fcbs
;
388 sf2
->name_offset
= parent
->full_filename
.Length
/ sizeof(WCHAR
);
390 if (parent
!= Vcb
->root_fcb
)
393 InsertTailList(&parent
->children
, &sf2
->list_entry
);
395 searchkey
.obj_id
= sf2
->inode
;
396 searchkey
.obj_type
= TYPE_INODE_ITEM
;
397 searchkey
.offset
= 0xffffffffffffffff;
399 Status
= find_item(Vcb
, sf2
->subvol
, &tp
, &searchkey
, FALSE
);
400 if (!NT_SUCCESS(Status
)) {
401 ERR("error - find_item returned %08x\n", Status
);
406 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
407 ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2
->inode
, sf2
->subvol
->id
);
408 free_traverse_ptr(&tp
);
410 return STATUS_INTERNAL_ERROR
;
413 if (tp
.item
->size
> 0)
414 RtlCopyMemory(&sf2
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
416 free_traverse_ptr(&tp
);
418 // This is just a quick function for the sake of move_across_subvols. As such, we don't bother
419 // with sf2->atts, sf2->sd, or sf2->full_filename.
423 return STATUS_SUCCESS
;
426 // static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) {
428 // LIST_ENTRY* le = Vcb->trees.Flink;
430 // while (le != &Vcb->trees) {
431 // tree* t = CONTAINING_RECORD(le, tree, list_entry);
433 // rc += t->refcount;
439 // while (le != tc) {
440 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
445 // t = tc2->tree->parent;
457 static NTSTATUS STDCALL
move_inode_across_subvols(device_extension
* Vcb
, fcb
* fcb
, root
* destsubvol
, UINT64 destinode
, UINT64 inode
, UINT64 oldparinode
, PANSI_STRING utf8
, UINT32 crc32
, BTRFS_TIME
* now
, LIST_ENTRY
* rollback
) {
458 UINT64 oldindex
, index
;
461 BOOL has_hardlink
= FALSE
;
464 traverse_ptr tp
, next_tp
;
470 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
471 fcb
->inode_item
.sequence
++;
472 fcb
->inode_item
.st_ctime
= *now
;
474 searchkey
.obj_id
= fcb
->inode
;
475 searchkey
.obj_type
= TYPE_INODE_ITEM
;
476 searchkey
.offset
= 0;
478 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
479 if (!NT_SUCCESS(Status
)) {
480 ERR("error - find_item returned %08x\n", Status
);
484 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
485 delete_tree_item(Vcb
, &tp
, rollback
);
487 if (fcb
->inode_item
.st_nlink
> 1) {
488 fcb
->inode_item
.st_nlink
--;
490 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
492 ERR("out of memory\n");
493 free_traverse_ptr(&tp
);
494 return STATUS_INSUFFICIENT_RESOURCES
;
497 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
499 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
)) {
500 ERR("error - failed to insert item\n");
501 free_traverse_ptr(&tp
);
502 return STATUS_INTERNAL_ERROR
;
508 WARN("couldn't find old INODE_ITEM\n");
511 free_traverse_ptr(&tp
);
513 fcb
->inode_item
.st_nlink
= 1;
515 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
517 ERR("out of memory\n");
518 return STATUS_INSUFFICIENT_RESOURCES
;
521 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
523 if (!insert_tree_item(Vcb
, destsubvol
, inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
)) {
524 ERR("error - failed to insert item\n");
525 return STATUS_INTERNAL_ERROR
;
528 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->utf8
.Buffer
, (ULONG
)fcb
->utf8
.Length
);
530 // delete old DIR_ITEM
532 Status
= delete_dir_item(Vcb
, fcb
->subvol
, oldparinode
, oldcrc32
, &fcb
->utf8
, rollback
);
533 if (!NT_SUCCESS(Status
)) {
534 ERR("delete_dir_item returned %08x\n", Status
);
538 // create new DIR_ITEM
540 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, ALLOC_TAG
);
542 ERR("out of memory\n");
543 return STATUS_INSUFFICIENT_RESOURCES
;
546 di
->key
.obj_id
= inode
;
547 di
->key
.obj_type
= TYPE_INODE_ITEM
;
549 di
->transid
= Vcb
->superblock
.generation
;
551 di
->n
= utf8
->Length
;
552 di
->type
= fcb
->type
;
553 RtlCopyMemory(di
->name
, utf8
->Buffer
, utf8
->Length
);
555 Status
= add_dir_item(Vcb
, destsubvol
, destinode
, crc32
, di
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, rollback
);
556 if (!NT_SUCCESS(Status
)) {
557 ERR("add_dir_item returned %08x\n", Status
);
561 Status
= delete_inode_ref(Vcb
, fcb
->subvol
, fcb
->inode
, oldparinode
, &fcb
->utf8
, &oldindex
, rollback
);
562 if (!NT_SUCCESS(Status
)) {
563 ERR("delete_inode_ref returned %08x\n", Status
);
570 WARN("couldn't find old INODE_REF\n");
572 searchkey
.obj_id
= oldparinode
;
573 searchkey
.obj_type
= TYPE_DIR_INDEX
;
574 searchkey
.offset
= oldindex
;
576 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
577 if (!NT_SUCCESS(Status
)) {
578 ERR("error - find_item returned %08x\n", Status
);
582 if (!keycmp(&searchkey
, &tp
.item
->key
))
583 delete_tree_item(Vcb
, &tp
, rollback
);
585 WARN("couldn't find old DIR_INDEX\n");
587 free_traverse_ptr(&tp
);
592 searchkey
.obj_id
= destinode
;
593 searchkey
.obj_type
= TYPE_DIR_INDEX
+ 1;
594 searchkey
.offset
= 0;
596 Status
= find_item(Vcb
, destsubvol
, &tp
, &searchkey
, FALSE
);
597 if (!NT_SUCCESS(Status
)) {
598 ERR("error - find_item returned %08x\n", Status
);
602 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
603 if (find_prev_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
604 free_traverse_ptr(&tp
);
607 TRACE("moving back to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
611 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_DIR_INDEX
) {
612 index
= tp
.item
->key
.offset
+ 1;
616 free_traverse_ptr(&tp
);
620 Status
= add_inode_ref(Vcb
, destsubvol
, inode
, destinode
, index
, utf8
, rollback
);
621 if (!NT_SUCCESS(Status
)) {
622 ERR("add_inode_ref returned %08x\n", Status
);
628 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, ALLOC_TAG
);
630 ERR("out of memory\n");
631 return STATUS_INSUFFICIENT_RESOURCES
;
634 di
->key
.obj_id
= inode
;
635 di
->key
.obj_type
= TYPE_INODE_ITEM
;
637 di
->transid
= Vcb
->superblock
.generation
;
639 di
->n
= utf8
->Length
;
640 di
->type
= fcb
->type
;
641 RtlCopyMemory(di
->name
, utf8
->Buffer
, utf8
->Length
);
643 if (!insert_tree_item(Vcb
, destsubvol
, destinode
, TYPE_DIR_INDEX
, index
, di
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, NULL
, rollback
)) {
644 ERR("error - failed to insert item\n");
645 return STATUS_INTERNAL_ERROR
;
650 searchkey
.obj_id
= fcb
->inode
;
651 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
652 searchkey
.offset
= 0;
654 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
655 if (!NT_SUCCESS(Status
)) {
656 ERR("error - find_item returned %08x\n", Status
);
661 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
&& tp
.item
->size
> 0) {
662 di
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
665 ERR("out of memory\n");
666 free_traverse_ptr(&tp
);
667 return STATUS_INSUFFICIENT_RESOURCES
;
670 RtlCopyMemory(di
, tp
.item
->data
, tp
.item
->size
);
672 if (!insert_tree_item(Vcb
, destsubvol
, inode
, TYPE_XATTR_ITEM
, tp
.item
->key
.offset
, di
, tp
.item
->size
, NULL
, rollback
)) {
673 ERR("error - failed to insert item\n");
674 free_traverse_ptr(&tp
);
675 return STATUS_INTERNAL_ERROR
;
679 delete_tree_item(Vcb
, &tp
, rollback
);
682 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
684 free_traverse_ptr(&tp
);
687 if (next_tp
.item
->key
.obj_id
> fcb
->inode
|| next_tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
)
692 free_traverse_ptr(&tp
);
696 searchkey
.obj_id
= fcb
->inode
;
697 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
698 searchkey
.offset
= 0;
700 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
701 if (!NT_SUCCESS(Status
)) {
702 ERR("error - find_item returned %08x\n", Status
);
707 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
708 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
709 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_DATA
));
711 EXTENT_DATA
* ed
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
714 ERR("out of memory\n");
715 free_traverse_ptr(&tp
);
716 return STATUS_INSUFFICIENT_RESOURCES
;
719 RtlCopyMemory(ed
, tp
.item
->data
, tp
.item
->size
);
721 // FIXME - update ed's generation
723 if (!insert_tree_item(Vcb
, destsubvol
, inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ed
, tp
.item
->size
, NULL
, rollback
)) {
724 ERR("error - failed to insert item\n");
725 free_traverse_ptr(&tp
);
726 return STATUS_INTERNAL_ERROR
;
729 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
730 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
732 if (ed2
->address
!= 0) {
733 Status
= add_extent_ref(Vcb
, ed2
->address
, ed2
->size
, destsubvol
, inode
, tp
.item
->key
.offset
, rollback
);
735 if (!NT_SUCCESS(Status
)) {
736 ERR("add_extent_ref returned %08x\n", Status
);
737 free_traverse_ptr(&tp
);
742 Status
= remove_extent_ref(Vcb
, ed2
->address
, ed2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
, NULL
, rollback
);
744 if (!NT_SUCCESS(Status
)) {
745 ERR("remove_extent_ref returned %08x\n", Status
);
746 free_traverse_ptr(&tp
);
754 delete_tree_item(Vcb
, &tp
, rollback
);
758 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
760 free_traverse_ptr(&tp
);
763 if (next_tp
.item
->key
.obj_id
> fcb
->inode
|| next_tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
)
768 free_traverse_ptr(&tp
);
770 return STATUS_SUCCESS
;
781 LIST_ENTRY list_entry
;
784 static NTSTATUS
add_to_dir_list(fcb
* fcb
, UINT8 level
, LIST_ENTRY
* dl
, UINT64 newparinode
, BOOL
* empty
) {
786 traverse_ptr tp
, next_tp
;
792 searchkey
.obj_id
= fcb
->inode
;
793 searchkey
.obj_type
= TYPE_DIR_INDEX
;
794 searchkey
.offset
= 2;
796 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
797 if (!NT_SUCCESS(Status
)) {
798 ERR("error - find_item returned %08x\n", Status
);
803 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_DIR_INDEX
) {
804 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
805 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(DIR_ITEM
));
807 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
811 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
+ di
->m
) {
812 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
814 if (di
->key
.obj_type
== TYPE_INODE_ITEM
|| di
->key
.obj_type
== TYPE_ROOT_ITEM
) {
815 if (di
->key
.obj_type
== TYPE_ROOT_ITEM
)
816 TRACE("moving subvol %llx\n", di
->key
.obj_id
);
818 TRACE("moving inode %llx\n", di
->key
.obj_id
);
822 Status
= get_fcb_from_dir_item(fcb
->Vcb
, &child
, fcb
, fcb
->subvol
, di
);
823 if (!NT_SUCCESS(Status
)) {
824 ERR("get_fcb_from_dir_item returned %08x\n", Status
);
825 free_traverse_ptr(&tp
);
829 dl2
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_list
), ALLOC_TAG
);
831 ERR("out of memory\n");
832 free_traverse_ptr(&tp
);
833 return STATUS_INSUFFICIENT_RESOURCES
;
838 dl2
->newparinode
= newparinode
;
839 dl2
->subvol
= di
->key
.obj_type
== TYPE_ROOT_ITEM
;
841 dl2
->utf8
.Length
= dl2
->utf8
.MaximumLength
= di
->n
;
842 dl2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dl2
->utf8
.MaximumLength
, ALLOC_TAG
);
843 if (!dl2
->utf8
.Buffer
) {
844 ERR("out of memory\n");
845 free_traverse_ptr(&tp
);
846 return STATUS_INSUFFICIENT_RESOURCES
;
849 RtlCopyMemory(dl2
->utf8
.Buffer
, di
->name
, dl2
->utf8
.Length
);
850 dl2
->crc32
= calc_crc32c(0xfffffffe, (UINT8
*)dl2
->utf8
.Buffer
, (ULONG
)dl2
->utf8
.Length
);
852 InsertTailList(dl
, &dl2
->list_entry
);
858 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
860 free_traverse_ptr(&tp
);
863 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
868 free_traverse_ptr(&tp
);
870 return STATUS_SUCCESS
;
873 static NTSTATUS STDCALL
move_across_subvols(device_extension
* Vcb
, fcb
* fcb
, root
* destsubvol
, UINT64 destinode
, PANSI_STRING utf8
, UINT32 crc32
, BTRFS_TIME
* now
, LIST_ENTRY
* rollback
) {
874 UINT64 inode
, oldparinode
;
878 if (destsubvol
->lastinode
== 0)
879 get_last_inode(Vcb
, destsubvol
);
881 inode
= destsubvol
->lastinode
+ 1;
882 destsubvol
->lastinode
++;
884 oldparinode
= fcb
->subvol
== fcb
->par
->subvol
? fcb
->par
->inode
: SUBVOL_ROOT_INODE
;
886 Status
= move_inode_across_subvols(Vcb
, fcb
, destsubvol
, destinode
, inode
, oldparinode
, utf8
, crc32
, now
, rollback
);
887 if (!NT_SUCCESS(Status
)) {
888 ERR("move_inode_across_subvols returned %08x\n", Status
);
892 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0) {
894 UINT8 level
, max_level
;
897 InitializeListHead(&dl
);
899 add_to_dir_list(fcb
, 0, &dl
, inode
, &b
);
907 dir_list
* dl2
= CONTAINING_RECORD(le
, dir_list
, list_entry
);
909 if (dl2
->level
== level
&& !dl2
->subvol
) {
911 destsubvol
->lastinode
++;
913 dl2
->newinode
= inode
;
915 if (dl2
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
916 add_to_dir_list(dl2
->fcb
, level
+1, &dl
, dl2
->newinode
, &b
);
917 if (!b
) empty
= FALSE
;
929 for (level
= 0; level
<= max_level
; level
++) {
930 TRACE("level %u\n", level
);
934 dir_list
* dl2
= CONTAINING_RECORD(le
, dir_list
, list_entry
);
936 if (dl2
->level
== level
) {
938 TRACE("subvol %llx\n", dl2
->fcb
->subvol
->id
);
940 Status
= move_subvol(Vcb
, dl2
->fcb
, destsubvol
, dl2
->newparinode
, &dl2
->utf8
, dl2
->crc32
, dl2
->crc32
, now
, FALSE
, rollback
);
941 if (!NT_SUCCESS(Status
)) {
942 ERR("move_subvol returned %08x\n", Status
);
946 TRACE("inode %llx\n", dl2
->fcb
->inode
);
948 Status
= move_inode_across_subvols(Vcb
, dl2
->fcb
, destsubvol
, dl2
->newparinode
, dl2
->newinode
, dl2
->fcb
->par
->inode
, &dl2
->utf8
, dl2
->crc32
, now
, rollback
);
949 if (!NT_SUCCESS(Status
)) {
950 ERR("move_inode_across_subvols returned %08x\n", Status
);
960 while (!IsListEmpty(&dl
)) {
963 le
= RemoveHeadList(&dl
);
964 dl2
= CONTAINING_RECORD(le
, dir_list
, list_entry
);
966 ExFreePool(dl2
->utf8
.Buffer
);
974 fcb
->subvol
= destsubvol
;
976 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
977 fcb
->subvol
->root_item
.ctime
= *now
;
979 return STATUS_SUCCESS
;
982 static NTSTATUS
delete_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, UINT64 parinode
, PANSI_STRING utf8
, UINT64
* index
, LIST_ENTRY
* rollback
) {
987 searchkey
.obj_id
= parsubvolid
;
988 searchkey
.obj_type
= TYPE_ROOT_REF
;
989 searchkey
.offset
= subvolid
;
991 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
992 if (!NT_SUCCESS(Status
)) {
993 ERR("error - find_item returned %08x\n", Status
);
997 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
998 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
999 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(ROOT_REF
));
1004 rr
= (ROOT_REF
*)tp
.item
->data
;
1005 len
= tp
.item
->size
;
1010 if (len
< sizeof(ROOT_REF
) || len
< sizeof(ROOT_REF
) - 1 + rr
->n
) {
1011 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1015 itemlen
= sizeof(ROOT_REF
) - sizeof(char) + rr
->n
;
1017 if (rr
->dir
== parinode
&& rr
->n
== utf8
->Length
&& RtlCompareMemory(rr
->name
, utf8
->Buffer
, rr
->n
) == rr
->n
) {
1018 ULONG newlen
= tp
.item
->size
- itemlen
;
1020 delete_tree_item(Vcb
, &tp
, rollback
);
1023 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1025 UINT8
*newrr
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *rroff
;
1028 ERR("out of memory\n");
1029 free_traverse_ptr(&tp
);
1030 return STATUS_INSUFFICIENT_RESOURCES
;
1033 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1035 if ((UINT8
*)rr
> tp
.item
->data
) {
1036 RtlCopyMemory(newrr
, tp
.item
->data
, (UINT8
*)rr
- tp
.item
->data
);
1037 rroff
= newrr
+ ((UINT8
*)rr
- tp
.item
->data
);
1042 if ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
< tp
.item
->size
)
1043 RtlCopyMemory(rroff
, &rr
->name
[rr
->n
], tp
.item
->size
- ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
));
1045 insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newrr
, newlen
, NULL
, rollback
);
1054 if (len
> itemlen
) {
1056 rr
= (ROOT_REF
*)&rr
->name
[rr
->n
];
1062 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey
.offset
, searchkey
.obj_id
);
1065 free_traverse_ptr(&tp
);
1067 return STATUS_SUCCESS
;
1070 static NTSTATUS
add_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, ROOT_REF
* rr
, LIST_ENTRY
* rollback
) {
1075 searchkey
.obj_id
= parsubvolid
;
1076 searchkey
.obj_type
= TYPE_ROOT_REF
;
1077 searchkey
.offset
= subvolid
;
1079 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1080 if (!NT_SUCCESS(Status
)) {
1081 ERR("error - find_item returned %08x\n", Status
);
1085 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1086 ULONG rrsize
= tp
.item
->size
+ sizeof(ROOT_REF
) - 1 + rr
->n
;
1089 rr2
= ExAllocatePoolWithTag(PagedPool
, rrsize
, ALLOC_TAG
);
1091 ERR("out of memory\n");
1092 return STATUS_INSUFFICIENT_RESOURCES
;
1095 if (tp
.item
->size
> 0)
1096 RtlCopyMemory(rr2
, tp
.item
->data
, tp
.item
->size
);
1098 RtlCopyMemory(rr2
+ tp
.item
->size
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
);
1101 delete_tree_item(Vcb
, &tp
, rollback
);
1103 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr2
, rrsize
, NULL
, rollback
)) {
1104 ERR("error - failed to insert item\n");
1106 free_traverse_ptr(&tp
);
1107 return STATUS_INTERNAL_ERROR
;
1110 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
, NULL
, rollback
)) {
1111 ERR("error - failed to insert item\n");
1113 free_traverse_ptr(&tp
);
1114 return STATUS_INTERNAL_ERROR
;
1118 free_traverse_ptr(&tp
);
1120 return STATUS_SUCCESS
;
1123 static NTSTATUS STDCALL
update_root_backref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, LIST_ENTRY
* rollback
) {
1130 searchkey
.obj_id
= parsubvolid
;
1131 searchkey
.obj_type
= TYPE_ROOT_REF
;
1132 searchkey
.offset
= subvolid
;
1134 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1135 if (!NT_SUCCESS(Status
)) {
1136 ERR("error - find_item returned %08x\n", Status
);
1140 if (!keycmp(&tp
.item
->key
, &searchkey
) && tp
.item
->size
> 0) {
1141 datalen
= tp
.item
->size
;
1143 data
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
1145 ERR("out of memory\n");
1146 return STATUS_INSUFFICIENT_RESOURCES
;
1149 RtlCopyMemory(data
, tp
.item
->data
, datalen
);
1154 free_traverse_ptr(&tp
);
1156 searchkey
.obj_id
= subvolid
;
1157 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
1158 searchkey
.offset
= parsubvolid
;
1160 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1161 if (!NT_SUCCESS(Status
)) {
1162 ERR("error - find_item returned %08x\n", Status
);
1166 if (!keycmp(&tp
.item
->key
, &searchkey
))
1167 delete_tree_item(Vcb
, &tp
, rollback
);
1169 free_traverse_ptr(&tp
);
1172 if (!insert_tree_item(Vcb
, Vcb
->root_root
, subvolid
, TYPE_ROOT_BACKREF
, parsubvolid
, data
, datalen
, NULL
, rollback
)) {
1173 ERR("error - failed to insert item\n");
1175 return STATUS_INTERNAL_ERROR
;
1179 return STATUS_SUCCESS
;
1182 static NTSTATUS STDCALL
move_subvol(device_extension
* Vcb
, fcb
* fcb
, root
* destsubvol
, UINT64 destinode
, PANSI_STRING utf8
, UINT32 crc32
, UINT32 oldcrc32
, BTRFS_TIME
* now
, BOOL ReplaceIfExists
, LIST_ENTRY
* rollback
) {
1187 UINT64 oldindex
, index
;
1190 // delete old DIR_ITEM
1192 Status
= delete_dir_item(Vcb
, fcb
->par
->subvol
, fcb
->par
->inode
, oldcrc32
, &fcb
->utf8
, rollback
);
1193 if (!NT_SUCCESS(Status
)) {
1194 ERR("delete_dir_item returned %08x\n", Status
);
1198 // create new DIR_ITEM
1200 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, ALLOC_TAG
);
1202 ERR("out of memory\n");
1203 return STATUS_INSUFFICIENT_RESOURCES
;
1206 di
->key
.obj_id
= fcb
->subvol
->id
;
1207 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
1209 di
->transid
= Vcb
->superblock
.generation
;
1211 di
->n
= utf8
->Length
;
1212 di
->type
= fcb
->type
;
1213 RtlCopyMemory(di
->name
, utf8
->Buffer
, utf8
->Length
);
1215 Status
= add_dir_item(Vcb
, destsubvol
, destinode
, crc32
, di
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, rollback
);
1216 if (!NT_SUCCESS(Status
)) {
1217 ERR("add_dir_item returned %08x\n", Status
);
1221 // delete old ROOT_REF
1225 Status
= delete_root_ref(Vcb
, fcb
->subvol
->id
, fcb
->par
->subvol
->id
, fcb
->par
->inode
, &fcb
->utf8
, &oldindex
, rollback
);
1226 if (!NT_SUCCESS(Status
)) {
1227 ERR("delete_root_ref returned %08x\n", Status
);
1231 TRACE("root index = %llx\n", oldindex
);
1233 // delete old DIR_INDEX
1235 if (oldindex
!= 0) {
1236 searchkey
.obj_id
= fcb
->par
->inode
;
1237 searchkey
.obj_type
= TYPE_DIR_INDEX
;
1238 searchkey
.offset
= oldindex
;
1240 Status
= find_item(Vcb
, fcb
->par
->subvol
, &tp
, &searchkey
, FALSE
);
1241 if (!NT_SUCCESS(Status
)) {
1242 ERR("error - find_item returned %08x\n", Status
);
1246 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1247 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1249 delete_tree_item(Vcb
, &tp
, rollback
);
1251 WARN("could not find old DIR_INDEX entry\n");
1254 free_traverse_ptr(&tp
);
1257 // create new DIR_INDEX
1259 if (fcb
->par
->subvol
== destsubvol
&& fcb
->par
->inode
== destinode
) {
1262 index
= find_next_dir_index(Vcb
, destsubvol
, destinode
);
1265 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, ALLOC_TAG
);
1267 ERR("out of memory\n");
1268 return STATUS_INSUFFICIENT_RESOURCES
;
1271 di
->key
.obj_id
= fcb
->subvol
->id
;
1272 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
1274 di
->transid
= Vcb
->superblock
.generation
;
1276 di
->n
= utf8
->Length
;
1277 di
->type
= fcb
->type
;
1278 RtlCopyMemory(di
->name
, utf8
->Buffer
, utf8
->Length
);
1280 if (!insert_tree_item(Vcb
, destsubvol
, destinode
, TYPE_DIR_INDEX
, index
, di
, sizeof(DIR_ITEM
) - 1 + utf8
->Length
, NULL
, rollback
)) {
1281 ERR("error - failed to insert item\n");
1282 return STATUS_INTERNAL_ERROR
;
1285 // create new ROOT_REF
1287 rr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_REF
) - 1 + utf8
->Length
, ALLOC_TAG
);
1289 ERR("out of memory\n");
1290 return STATUS_INSUFFICIENT_RESOURCES
;
1293 rr
->dir
= destinode
;
1295 rr
->n
= utf8
->Length
;
1296 RtlCopyMemory(rr
->name
, utf8
->Buffer
, utf8
->Length
);
1298 Status
= add_root_ref(Vcb
, fcb
->subvol
->id
, destsubvol
->id
, rr
, rollback
);
1299 if (!NT_SUCCESS(Status
)) {
1300 ERR("add_root_ref returned %08x\n", Status
);
1304 Status
= update_root_backref(Vcb
, fcb
->subvol
->id
, fcb
->par
->subvol
->id
, rollback
);
1305 if (!NT_SUCCESS(Status
)) {
1306 ERR("update_root_backref 1 returned %08x\n", Status
);
1310 if (fcb
->par
->subvol
!= destsubvol
) {
1311 Status
= update_root_backref(Vcb
, fcb
->subvol
->id
, destsubvol
->id
, rollback
);
1312 if (!NT_SUCCESS(Status
)) {
1313 ERR("update_root_backref 1 returned %08x\n", Status
);
1317 fcb
->par
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1318 fcb
->par
->subvol
->root_item
.ctime
= *now
;
1321 destsubvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1322 destsubvol
->root_item
.ctime
= *now
;
1324 return STATUS_SUCCESS
;
1327 static BOOL
has_open_children(fcb
* fcb
) {
1328 LIST_ENTRY
* le
= fcb
->children
.Flink
;
1331 while (le
!= &fcb
->children
) {
1332 c
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
1334 if (c
->refcount
> 0) {
1335 if (c
->open_count
> 0)
1338 if (has_open_children(c
))
1348 static NTSTATUS STDCALL
set_rename_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
, BOOL ReplaceIfExists
, LIST_ENTRY
* rollback
) {
1349 FILE_RENAME_INFORMATION
* fri
= Irp
->AssociatedIrp
.SystemBuffer
;
1350 fcb
*fcb
= FileObject
->FsContext
, *tfofcb
, *oldparfcb
, *oldfcb
;
1352 UINT64 parinode
, dirpos
;
1354 UNICODE_STRING fnus
;
1355 ULONG fnlen
, utf8len
, disize
;
1358 UINT32 crc32
, oldcrc32
;
1360 traverse_ptr tp
, next_tp
;
1364 BOOL across_directories
;
1367 // FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
1369 TRACE(" tfo = %p\n", tfo
);
1370 TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists
);
1371 TRACE(" RootDirectory = %p\n", fri
->RootDirectory
);
1372 TRACE(" FileName = %.*S\n", fri
->FileNameLength
/ sizeof(WCHAR
), fri
->FileName
);
1374 KeQuerySystemTime(&time
);
1375 win_time_to_unix(time
, &now
);
1380 ERR("error - tried to rename file with no parent\n");
1381 Status
= STATUS_ACCESS_DENIED
;
1386 fnlen
= fri
->FileNameLength
/ sizeof(WCHAR
);
1389 parsubvol
= fcb
->par
->subvol
;
1390 parinode
= fcb
->par
->inode
;
1393 across_directories
= FALSE
;
1397 tfofcb
= tfo
->FsContext
;
1398 parsubvol
= tfofcb
->subvol
;
1399 parinode
= tfofcb
->inode
;
1401 for (i
= fnlen
- 1; i
>= 0; i
--) {
1402 if (fri
->FileName
[i
] == '\\' || fri
->FileName
[i
] == '/') {
1403 fn
= &fri
->FileName
[i
+1];
1404 fnlen
= (fri
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
1409 across_directories
= parsubvol
!= fcb
->par
->subvol
|| parinode
!= fcb
->par
->inode
;
1413 fnus
.Length
= fnus
.MaximumLength
= fnlen
* sizeof(WCHAR
);
1415 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1417 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1418 if (!NT_SUCCESS(Status
))
1421 utf8
.MaximumLength
= utf8
.Length
= utf8len
;
1422 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
1424 ERR("out of memory\n");
1425 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1429 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1430 if (!NT_SUCCESS(Status
))
1433 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)utf8
.Buffer
, (ULONG
)utf8
.Length
);
1435 // FIXME - set to crc32 if utf8 and oldutf8 are identical
1436 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->utf8
.Buffer
, (ULONG
)fcb
->utf8
.Length
);
1438 // TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
1442 Status
= get_fcb(Vcb
, &oldfcb
, &fnus
, tfo
? tfo
->FsContext
: NULL
, FALSE
);
1444 if (NT_SUCCESS(Status
)) {
1445 WARN("destination file %.*S already exists\n", oldfcb
->full_filename
.Length
/ sizeof(WCHAR
), oldfcb
->full_filename
.Buffer
);
1447 if (fcb
!= oldfcb
&& !(oldfcb
->open_count
== 0 && oldfcb
->deleted
)) {
1448 if (!ReplaceIfExists
) {
1449 Status
= STATUS_OBJECT_NAME_COLLISION
;
1451 } else if (oldfcb
->open_count
>= 1 && !oldfcb
->deleted
) {
1452 WARN("trying to overwrite open file\n");
1453 Status
= STATUS_ACCESS_DENIED
;
1457 if (oldfcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1458 WARN("trying to overwrite directory\n");
1459 Status
= STATUS_ACCESS_DENIED
;
1465 if (has_open_children(fcb
)) {
1466 WARN("trying to rename file with open children\n");
1467 Status
= STATUS_ACCESS_DENIED
;
1472 Status
= delete_fcb(oldfcb
, NULL
, rollback
);
1473 if (!NT_SUCCESS(Status
)) {
1474 ERR("delete_fcb returned %08x\n", Status
);
1479 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
1480 UNICODE_STRING filename
;
1482 filename
.Buffer
= fn
;
1483 filename
.MaximumLength
= filename
.Length
= fnlen
* sizeof(WCHAR
);
1485 Status
= move_subvol(Vcb
, fcb
, tfofcb
->subvol
, tfofcb
->inode
, &utf8
, crc32
, oldcrc32
, &now
, ReplaceIfExists
, rollback
);
1487 if (!NT_SUCCESS(Status
)) {
1488 ERR("move_subvol returned %08x\n", Status
);
1491 } else if (parsubvol
!= fcb
->subvol
) {
1492 UNICODE_STRING filename
;
1494 filename
.Buffer
= fn
;
1495 filename
.MaximumLength
= filename
.Length
= fnlen
* sizeof(WCHAR
);
1497 Status
= move_across_subvols(Vcb
, fcb
, tfofcb
->subvol
, tfofcb
->inode
, &utf8
, crc32
, &now
, rollback
);
1499 if (!NT_SUCCESS(Status
)) {
1500 ERR("move_across_subvols returned %08x\n", Status
);
1507 // delete old DIR_ITEM entry
1509 Status
= delete_dir_item(Vcb
, fcb
->subvol
, fcb
->par
->inode
, oldcrc32
, &fcb
->utf8
, rollback
);
1510 if (!NT_SUCCESS(Status
)) {
1511 ERR("delete_dir_item returned %08x\n", Status
);
1515 // FIXME - make sure fcb's filepart matches the case on disk
1517 // create new DIR_ITEM entry
1519 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + utf8
.Length
, ALLOC_TAG
);
1521 ERR("out of memory\n");
1522 return STATUS_INSUFFICIENT_RESOURCES
;
1525 di
->key
.obj_id
= fcb
->inode
;
1526 di
->key
.obj_type
= TYPE_INODE_ITEM
;
1528 di
->transid
= Vcb
->superblock
.generation
;
1530 di
->n
= utf8
.Length
;
1531 di
->type
= fcb
->type
;
1532 RtlCopyMemory(di
->name
, utf8
.Buffer
, utf8
.Length
);
1534 Status
= add_dir_item(Vcb
, parsubvol
, parinode
, crc32
, di
, sizeof(DIR_ITEM
) - 1 + utf8
.Length
, rollback
);
1535 if (!NT_SUCCESS(Status
)) {
1536 ERR("add_dir_item returned %08x\n", Status
);
1542 Status
= delete_inode_ref(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->par
->inode
, &fcb
->utf8
, &oldindex
, rollback
);
1543 if (!NT_SUCCESS(Status
)) {
1544 ERR("delete_inode_ref returned %08x\n", Status
);
1548 // delete old DIR_INDEX entry
1550 if (oldindex
!= 0) {
1551 searchkey
.obj_id
= fcb
->par
->inode
;
1552 searchkey
.obj_type
= TYPE_DIR_INDEX
;
1553 searchkey
.offset
= oldindex
;
1555 Status
= find_item(Vcb
, fcb
->par
->subvol
, &tp
, &searchkey
, FALSE
);
1556 if (!NT_SUCCESS(Status
)) {
1557 ERR("error - find_item returned %08x\n", Status
);
1561 if (!keycmp(&tp
.item
->key
, &searchkey
))
1562 delete_tree_item(Vcb
, &tp
, rollback
);
1564 WARN("couldn't find DIR_INDEX\n");
1567 free_traverse_ptr(&tp
);
1569 WARN("couldn't get index from INODE_REF\n");
1572 // create new DIR_INDEX entry
1574 if (parsubvol
!= fcb
->par
->subvol
|| parinode
!= fcb
->par
->inode
) {
1575 searchkey
.obj_id
= parinode
;
1576 searchkey
.obj_type
= TYPE_DIR_INDEX
+ 1;
1577 searchkey
.offset
= 0;
1579 Status
= find_item(Vcb
, parsubvol
, &tp
, &searchkey
, FALSE
);
1580 if (!NT_SUCCESS(Status
)) {
1581 ERR("error - find_item returned %08x\n", Status
);
1588 TRACE("%llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1590 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_DIR_INDEX
) {
1591 dirpos
= tp
.item
->key
.offset
+ 1;
1595 if (find_prev_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
1596 free_traverse_ptr(&tp
);
1600 } while (tp
.item
->key
.obj_id
>= parinode
&& tp
.item
->key
.obj_type
>= TYPE_DIR_INDEX
);
1602 free_traverse_ptr(&tp
);
1606 disize
= (ULONG
)(sizeof(DIR_ITEM
) - 1 + utf8
.Length
);
1607 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
1609 ERR("out of memory\n");
1610 return STATUS_INSUFFICIENT_RESOURCES
;
1613 di
->key
.obj_id
= fcb
->inode
;
1614 di
->key
.obj_type
= TYPE_INODE_ITEM
;
1616 di
->transid
= Vcb
->superblock
.generation
;
1618 di
->n
= (UINT16
)utf8
.Length
;
1619 di
->type
= fcb
->type
;
1620 RtlCopyMemory(di
->name
, utf8
.Buffer
, utf8
.Length
);
1622 if (!insert_tree_item(Vcb
, parsubvol
, parinode
, TYPE_DIR_INDEX
, dirpos
, di
, disize
, NULL
, rollback
))
1623 ERR("error - failed to insert item\n");
1625 // create new INODE_REF entry
1627 Status
= add_inode_ref(Vcb
, parsubvol
, fcb
->inode
, parinode
, dirpos
, &utf8
, rollback
);
1628 if (!NT_SUCCESS(Status
)) {
1629 ERR("add_inode_ref returned %08x\n", Status
);
1633 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1634 fcb
->inode_item
.sequence
++;
1635 fcb
->inode_item
.st_ctime
= now
;
1637 searchkey
.obj_id
= fcb
->inode
;
1638 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1639 searchkey
.offset
= 0xffffffffffffffff;
1641 Status
= find_item(Vcb
, parsubvol
, &tp
, &searchkey
, FALSE
);
1642 if (!NT_SUCCESS(Status
)) {
1643 ERR("error - find_item returned %08x\n", Status
);
1647 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
1648 delete_tree_item(Vcb
, &tp
, rollback
);
1650 free_traverse_ptr(&tp
);
1652 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1654 ERR("out of memory\n");
1655 return STATUS_INSUFFICIENT_RESOURCES
;
1658 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
1660 if (!insert_tree_item(Vcb
, parsubvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
)) {
1661 WARN("insert_tree_item failed\n");
1665 // update directory INODE_ITEMs
1667 fcb
->par
->inode_item
.transid
= Vcb
->superblock
.generation
;
1668 fcb
->par
->inode_item
.sequence
++;
1669 fcb
->par
->inode_item
.st_ctime
= now
;
1670 fcb
->par
->inode_item
.st_mtime
= now
;
1672 TRACE("fcb->par->inode_item.st_size was %llx\n", fcb
->par
->inode_item
.st_size
);
1673 if (!tfofcb
|| (fcb
->par
->inode
== tfofcb
->inode
&& fcb
->par
->subvol
== tfofcb
->subvol
)) {
1674 fcb
->par
->inode_item
.st_size
+= 2 * (utf8
.Length
- fcb
->utf8
.Length
);
1676 fcb
->par
->inode_item
.st_size
-= 2 * fcb
->utf8
.Length
;
1677 TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb
->inode_item
.st_size
);
1678 tfofcb
->inode_item
.st_size
+= 2 * utf8
.Length
;
1679 TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb
->inode_item
.st_size
);
1680 tfofcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1681 tfofcb
->inode_item
.sequence
++;
1682 tfofcb
->inode_item
.st_ctime
= now
;
1683 tfofcb
->inode_item
.st_mtime
= now
;
1685 TRACE("fcb->par->inode_item.st_size now %llx\n", fcb
->par
->inode_item
.st_size
);
1687 if (oldfcb
&& oldfcb
->par
!= fcb
->par
) {
1688 TRACE("oldfcb->par->inode_item.st_size was %llx\n", oldfcb
->par
->inode_item
.st_size
);
1689 oldfcb
->par
->inode_item
.st_size
-= 2 * oldfcb
->utf8
.Length
;
1690 TRACE("oldfcb->par->inode_item.st_size now %llx\n", oldfcb
->par
->inode_item
.st_size
);
1693 searchkey
.obj_id
= fcb
->par
->inode
;
1694 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1695 searchkey
.offset
= 0xffffffffffffffff;
1697 Status
= find_item(Vcb
, fcb
->par
->subvol
, &tp
, &searchkey
, FALSE
);
1698 if (!NT_SUCCESS(Status
)) {
1699 ERR("error - find_item returned %08x\n", Status
);
1703 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
1704 delete_tree_item(Vcb
, &tp
, rollback
);
1706 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1708 ERR("out of memory\n");
1709 free_traverse_ptr(&tp
);
1710 return STATUS_INSUFFICIENT_RESOURCES
;
1713 RtlCopyMemory(ii
, &fcb
->par
->inode_item
, sizeof(INODE_ITEM
));
1715 if (!insert_tree_item(Vcb
, fcb
->par
->subvol
, fcb
->par
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
))
1716 WARN("insert_tree_item failed\n");
1718 free_traverse_ptr(&tp
);
1720 if (tfofcb
&& (fcb
->par
->inode
!= tfofcb
->inode
|| fcb
->par
->subvol
!= tfofcb
->subvol
)) {
1721 searchkey
.obj_id
= tfofcb
->inode
;
1722 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1723 searchkey
.offset
= 0xffffffffffffffff;
1725 Status
= find_item(Vcb
, tfofcb
->subvol
, &tp
, &searchkey
, FALSE
);
1726 if (!NT_SUCCESS(Status
)) {
1727 ERR("error - find_item returned %08x\n", Status
);
1731 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
1732 delete_tree_item(Vcb
, &tp
, rollback
);
1734 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1736 ERR("out of memory\n");
1737 free_traverse_ptr(&tp
);
1738 return STATUS_INSUFFICIENT_RESOURCES
;
1741 RtlCopyMemory(ii
, &tfofcb
->inode_item
, sizeof(INODE_ITEM
));
1743 if (!insert_tree_item(Vcb
, tfofcb
->subvol
, tfofcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
))
1744 WARN("insert_tree_item failed\n");
1746 free_traverse_ptr(&tp
);
1749 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1750 fcb
->subvol
->root_item
.ctime
= now
;
1752 // FIXME - handle overwrite by rename here
1753 FsRtlNotifyFullReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fcb
->full_filename
, fcb
->name_offset
* sizeof(WCHAR
), NULL
, NULL
,
1754 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
1755 across_directories
? FILE_ACTION_REMOVED
: FILE_ACTION_RENAMED_OLD_NAME
, NULL
);
1757 // FIXME - change full_filename and name_offset of open children
1759 if (fnlen
!= fcb
->filepart
.Length
/ sizeof(WCHAR
) || RtlCompareMemory(fn
, fcb
->filepart
.Buffer
, fcb
->filepart
.Length
) != fcb
->filepart
.Length
) {
1760 RtlFreeUnicodeString(&fcb
->filepart
);
1761 fcb
->filepart
.Length
= fcb
->filepart
.MaximumLength
= (USHORT
)(fnlen
* sizeof(WCHAR
));
1762 fcb
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->filepart
.Length
, ALLOC_TAG
);
1764 if (!fcb
->filepart
.Buffer
) {
1765 ERR("out of memory\n");
1767 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1771 RtlCopyMemory(fcb
->filepart
.Buffer
, fn
, fcb
->filepart
.Length
);
1774 if (tfo
&& tfofcb
!= fcb
->par
) {
1775 oldparfcb
= fcb
->par
;
1778 fcb
->par
->refcount
++;
1780 RemoveEntryList(&fcb
->list_entry
);
1781 InsertTailList(&fcb
->par
->children
, &fcb
->list_entry
);
1783 #ifdef DEBUG_FCB_REFCOUNTS
1784 WARN("fcb %p: refcount now %i (%.*S)\n", fcb
->par
, fcb
->par
->refcount
, fcb
->par
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->par
->full_filename
.Buffer
);
1786 free_fcb(oldparfcb
);
1789 ExFreePool(fcb
->utf8
.Buffer
);
1793 // change fcb->full_filename
1795 fcb
->full_filename
.MaximumLength
= fcb
->par
->full_filename
.Length
+ fcb
->filepart
.Length
;
1796 if (fcb
->par
->par
) fcb
->full_filename
.MaximumLength
+= sizeof(WCHAR
);
1797 ExFreePool(fcb
->full_filename
.Buffer
);
1799 fcb
->full_filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->full_filename
.MaximumLength
, ALLOC_TAG
);
1800 if (!fcb
->full_filename
.Buffer
) {
1801 ERR("out of memory\n");
1803 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1807 RtlCopyMemory(fcb
->full_filename
.Buffer
, fcb
->par
->full_filename
.Buffer
, fcb
->par
->full_filename
.Length
);
1808 fcb
->full_filename
.Length
= fcb
->par
->full_filename
.Length
;
1810 if (fcb
->par
->par
) {
1811 fcb
->full_filename
.Buffer
[fcb
->full_filename
.Length
/ sizeof(WCHAR
)] = '\\';
1812 fcb
->full_filename
.Length
+= sizeof(WCHAR
);
1814 fcb
->name_offset
= fcb
->full_filename
.Length
/ sizeof(WCHAR
);
1816 RtlAppendUnicodeStringToString(&fcb
->full_filename
, &fcb
->filepart
);
1818 FsRtlNotifyFullReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fcb
->full_filename
, fcb
->name_offset
* sizeof(WCHAR
), NULL
, NULL
,
1819 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
1820 across_directories
? FILE_ACTION_ADDED
: FILE_ACTION_RENAMED_NEW_NAME
, NULL
);
1822 Status
= STATUS_SUCCESS
;
1826 ExFreePool(utf8
.Buffer
);
1834 static NTSTATUS STDCALL
stream_set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
, LIST_ENTRY
* rollback
) {
1835 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
1836 fcb
* fcb
= FileObject
->FsContext
;
1847 TRACE("setting new end to %llx bytes (currently %x)\n", feofi
->EndOfFile
.QuadPart
, fcb
->adssize
);
1849 if (feofi
->EndOfFile
.QuadPart
< fcb
->adssize
) {
1851 return STATUS_SUCCESS
;
1853 TRACE("truncating stream to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
1855 if (feofi
->EndOfFile
.QuadPart
> 0) {
1856 if (!get_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, &data
, &datalen
)) {
1857 ERR("get_xattr failed\n");
1858 return STATUS_INTERNAL_ERROR
;
1862 Status
= set_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, data
, feofi
->EndOfFile
.QuadPart
, rollback
);
1863 if (!NT_SUCCESS(Status
)) {
1864 ERR("set_xattr returned %08x\n", Status
);
1868 fcb
->adssize
= feofi
->EndOfFile
.QuadPart
;
1872 } else if (feofi
->EndOfFile
.QuadPart
> fcb
->adssize
) {
1876 TRACE("extending stream to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
1878 // find maximum length of xattr
1879 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
1881 searchkey
.obj_id
= fcb
->inode
;
1882 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
1883 searchkey
.offset
= fcb
->adshash
;
1885 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
1886 if (!NT_SUCCESS(Status
)) {
1887 ERR("error - find_item returned %08x\n", Status
);
1891 if (keycmp(&tp
.item
->key
, &searchkey
)) {
1892 ERR("error - could not find key for xattr\n");
1893 free_traverse_ptr(&tp
);
1894 return STATUS_INTERNAL_ERROR
;
1897 if (tp
.item
->size
< datalen
) {
1898 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
, datalen
);
1899 free_traverse_ptr(&tp
);
1900 return STATUS_INTERNAL_ERROR
;
1903 maxlen
-= tp
.item
->size
- datalen
; // subtract XATTR_ITEM overhead
1905 free_traverse_ptr(&tp
);
1907 if (feofi
->EndOfFile
.QuadPart
> maxlen
) {
1908 ERR("error - xattr too long (%llu > %u)\n", feofi
->EndOfFile
.QuadPart
, maxlen
);
1909 return STATUS_DISK_FULL
;
1912 if (!get_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, &data
, &datalen
)) {
1913 ERR("get_xattr failed\n");
1914 return STATUS_INTERNAL_ERROR
;
1917 data2
= ExAllocatePoolWithTag(PagedPool
, feofi
->EndOfFile
.QuadPart
, ALLOC_TAG
);
1919 ERR("out of memory\n");
1921 return STATUS_INSUFFICIENT_RESOURCES
;
1924 RtlCopyMemory(data2
, data
, datalen
);
1927 RtlZeroMemory(&data2
[datalen
], feofi
->EndOfFile
.QuadPart
- datalen
);
1929 Status
= set_xattr(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, data2
, feofi
->EndOfFile
.QuadPart
, rollback
);
1930 if (!NT_SUCCESS(Status
)) {
1931 ERR("set_xattr returned %08x\n", Status
);
1935 fcb
->adssize
= feofi
->EndOfFile
.QuadPart
;
1940 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
1941 ccfs
.FileSize
= fcb
->Header
.FileSize
;
1942 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
1944 CcSetFileSizes(FileObject
, &ccfs
);
1946 KeQuerySystemTime(&time
);
1947 win_time_to_unix(time
, &now
);
1949 fcb
->par
->inode_item
.transid
= Vcb
->superblock
.generation
;
1950 fcb
->par
->inode_item
.sequence
++;
1951 fcb
->par
->inode_item
.st_ctime
= now
;
1953 searchkey
.obj_id
= fcb
->inode
;
1954 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1955 searchkey
.offset
= 0;
1957 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
1958 if (!NT_SUCCESS(Status
)) {
1959 ERR("error - find_item returned %08x\n", Status
);
1963 if (!keycmp(&tp
.item
->key
, &searchkey
))
1964 delete_tree_item(Vcb
, &tp
, rollback
);
1966 WARN("couldn't find existing INODE_ITEM\n");
1968 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1970 ERR("out of memory\n");
1971 free_traverse_ptr(&tp
);
1972 return STATUS_INSUFFICIENT_RESOURCES
;
1975 RtlCopyMemory(ii
, &fcb
->par
->inode_item
, sizeof(INODE_ITEM
));
1976 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
1978 free_traverse_ptr(&tp
);
1980 fcb
->par
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1981 fcb
->par
->subvol
->root_item
.ctime
= now
;
1983 return STATUS_SUCCESS
;
1986 static NTSTATUS STDCALL
set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
, LIST_ENTRY
* rollback
) {
1987 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
1988 fcb
* fcb
= FileObject
->FsContext
;
1997 return STATUS_FILE_CLOSED
;
2000 return stream_set_end_of_file_information(Vcb
, Irp
, FileObject
, advance_only
, rollback
);
2002 TRACE("filename %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
2003 TRACE("paging IO: %s\n", Irp
->Flags
& IRP_PAGING_IO
? "TRUE" : "FALSE");
2004 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2005 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2008 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi
->EndOfFile
.QuadPart
, fcb
->inode_item
.st_size
);
2010 // if (feofi->EndOfFile.QuadPart==0x36c000)
2013 if (feofi
->EndOfFile
.QuadPart
< fcb
->inode_item
.st_size
) {
2015 return STATUS_SUCCESS
;
2017 TRACE("truncating file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2019 Status
= truncate_file(fcb
, feofi
->EndOfFile
.QuadPart
, rollback
);
2020 if (!NT_SUCCESS(Status
)) {
2021 ERR("error - truncate_file failed\n");
2024 } else if (feofi
->EndOfFile
.QuadPart
> fcb
->inode_item
.st_size
) {
2025 if (Irp
->Flags
& IRP_PAGING_IO
) {
2026 TRACE("paging IO tried to extend file size\n");
2027 return STATUS_SUCCESS
;
2030 TRACE("extending file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2032 // FIXME - pass flag to say that new extents should be prealloc rather than sparse
2033 Status
= extend_file(fcb
, feofi
->EndOfFile
.QuadPart
, rollback
);
2034 if (!NT_SUCCESS(Status
)) {
2035 ERR("error - extend_file failed\n");
2040 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2041 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2042 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2044 CcSetFileSizes(FileObject
, &ccfs
);
2045 TRACE("setting FileSize for %.*S to %llx\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, ccfs
.FileSize
);
2047 KeQuerySystemTime(&time
);
2049 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2051 searchkey
.obj_id
= fcb
->inode
;
2052 searchkey
.obj_type
= TYPE_INODE_ITEM
;
2053 searchkey
.offset
= 0;
2055 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
2056 if (!NT_SUCCESS(Status
)) {
2057 ERR("error - find_item returned %08x\n", Status
);
2061 if (!keycmp(&tp
.item
->key
, &searchkey
))
2062 delete_tree_item(Vcb
, &tp
, rollback
);
2064 WARN("couldn't find existing INODE_ITEM\n");
2066 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
2068 ERR("out of memory\n");
2069 return STATUS_INSUFFICIENT_RESOURCES
;
2072 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
2073 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
2075 free_traverse_ptr(&tp
);
2077 return STATUS_SUCCESS
;
2080 // static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2081 // FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2082 // fcb* fcb = FileObject->FsContext;
2084 // FIXME("FIXME\n");
2085 // ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
2086 // ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
2088 // return STATUS_NOT_IMPLEMENTED;
2091 static NTSTATUS STDCALL
set_position_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
2092 FILE_POSITION_INFORMATION
* fpi
= (FILE_POSITION_INFORMATION
*)Irp
->AssociatedIrp
.SystemBuffer
;
2093 #ifdef DEBUG_LONG_MESSAGES
2094 fcb
* fcb
= FileObject
->FsContext
;
2096 TRACE("setting the position on %.*S to %llx\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, fpi
->CurrentByteOffset
.QuadPart
);
2099 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2101 FileObject
->CurrentByteOffset
= fpi
->CurrentByteOffset
;
2103 return STATUS_SUCCESS
;
2106 NTSTATUS STDCALL
drv_set_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2108 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2109 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2110 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
2112 LIST_ENTRY rollback
;
2114 InitializeListHead(&rollback
);
2116 FsRtlEnterFileSystem();
2118 top_level
= is_top_level(Irp
);
2120 if (Vcb
->readonly
) {
2121 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2125 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
) {
2126 Status
= STATUS_ACCESS_DENIED
;
2130 Irp
->IoStatus
.Information
= 0;
2132 Status
= STATUS_NOT_IMPLEMENTED
;
2134 TRACE("set information\n");
2136 acquire_tree_lock(Vcb
, TRUE
);
2138 switch (IrpSp
->Parameters
.SetFile
.FileInformationClass
) {
2139 case FileAllocationInformation
:
2140 TRACE("FileAllocationInformation\n");
2141 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, FALSE
, &rollback
);
2144 case FileBasicInformation
:
2145 TRACE("FileBasicInformation\n");
2146 Status
= set_basic_information(Vcb
, Irp
, IrpSp
->FileObject
, &rollback
);
2149 case FileDispositionInformation
:
2150 TRACE("FileDispositionInformation\n");
2151 Status
= set_disposition_information(Vcb
, Irp
, IrpSp
->FileObject
);
2154 case FileEndOfFileInformation
:
2155 TRACE("FileEndOfFileInformation\n");
2156 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.AdvanceOnly
, &rollback
);
2159 case FileLinkInformation
:
2160 FIXME("STUB: FileLinkInformation\n");
2163 case FilePositionInformation
:
2164 TRACE("FilePositionInformation\n");
2165 Status
= set_position_information(Vcb
, Irp
, IrpSp
->FileObject
);
2168 case FileRenameInformation
:
2169 TRACE("FileRenameInformation\n");
2170 // FIXME - make this work with streams
2171 Status
= set_rename_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
, IrpSp
->Parameters
.SetFile
.ReplaceIfExists
, &rollback
);
2174 case FileValidDataLengthInformation
:
2175 FIXME("STUB: FileValidDataLengthInformation\n");
2178 #if (NTDDI_VERSION >= NTDDI_VISTA)
2179 case FileNormalizedNameInformation
:
2180 FIXME("STUB: FileNormalizedNameInformation\n");
2185 #if (NTDDI_VERSION >= NTDDI_WIN7)
2186 case FileStandardLinkInformation
:
2187 FIXME("STUB: FileStandardLinkInformation\n");
2190 case FileRemoteProtocolInformation
:
2191 FIXME("STUB: FileRemoteProtocolInformation\n");
2196 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.SetFile
.FileInformationClass
);
2199 if (NT_SUCCESS(Status
))
2200 Status
= consider_write(Vcb
);
2202 if (NT_SUCCESS(Status
))
2203 clear_rollback(&rollback
);
2205 do_rollback(Vcb
, &rollback
);
2207 release_tree_lock(Vcb
, TRUE
);
2210 Irp
->IoStatus
.Status
= Status
;
2212 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
2215 IoSetTopLevelIrp(NULL
);
2217 FsRtlExitFileSystem();
2222 static NTSTATUS STDCALL
fill_in_file_basic_information(FILE_BASIC_INFORMATION
* fbi
, INODE_ITEM
* ii
, LONG
* length
, fcb
* fcb
) {
2223 RtlZeroMemory(fbi
, sizeof(FILE_BASIC_INFORMATION
));
2225 *length
-= sizeof(FILE_BASIC_INFORMATION
);
2227 fbi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2228 fbi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2229 fbi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2230 fbi
->ChangeTime
.QuadPart
= 0;
2231 fbi
->FileAttributes
= fcb
->ads
? fcb
->par
->atts
: fcb
->atts
;
2233 return STATUS_SUCCESS
;
2236 static NTSTATUS STDCALL
fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION
* fnoi
, fcb
* fcb
, LONG
* length
) {
2239 if (*length
< sizeof(FILE_NETWORK_OPEN_INFORMATION
)) {
2241 return STATUS_BUFFER_OVERFLOW
;
2244 RtlZeroMemory(fnoi
, sizeof(FILE_NETWORK_OPEN_INFORMATION
));
2246 *length
-= sizeof(FILE_NETWORK_OPEN_INFORMATION
);
2249 ii
= &fcb
->par
->inode_item
;
2251 ii
= &fcb
->inode_item
;
2254 fnoi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2255 fnoi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2256 fnoi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2257 fnoi
->ChangeTime
.QuadPart
= 0;
2260 fnoi
->AllocationSize
.QuadPart
= fnoi
->EndOfFile
.QuadPart
= fcb
->adssize
;
2261 fnoi
->FileAttributes
= fcb
->par
->atts
;
2263 fnoi
->AllocationSize
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2264 fnoi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2265 fnoi
->FileAttributes
= fcb
->atts
;
2268 return STATUS_SUCCESS
;
2271 static NTSTATUS STDCALL
fill_in_file_standard_information(FILE_STANDARD_INFORMATION
* fsi
, fcb
* fcb
, LONG
* length
) {
2272 RtlZeroMemory(fsi
, sizeof(FILE_STANDARD_INFORMATION
));
2274 *length
-= sizeof(FILE_STANDARD_INFORMATION
);
2277 fsi
->AllocationSize
.QuadPart
= fsi
->EndOfFile
.QuadPart
= fcb
->adssize
;
2278 fsi
->NumberOfLinks
= fcb
->par
->inode_item
.st_nlink
;
2279 fsi
->Directory
= S_ISDIR(fcb
->par
->inode_item
.st_mode
);
2281 fsi
->AllocationSize
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2282 fsi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2283 fsi
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
2284 fsi
->Directory
= S_ISDIR(fcb
->inode_item
.st_mode
);
2287 TRACE("length = %llu\n", fsi
->EndOfFile
.QuadPart
);
2289 fsi
->DeletePending
= fcb
->delete_on_close
;
2291 return STATUS_SUCCESS
;
2294 static NTSTATUS STDCALL
fill_in_file_internal_information(FILE_INTERNAL_INFORMATION
* fii
, UINT64 inode
, LONG
* length
) {
2295 *length
-= sizeof(FILE_INTERNAL_INFORMATION
);
2297 fii
->IndexNumber
.QuadPart
= inode
;
2299 return STATUS_SUCCESS
;
2302 static NTSTATUS STDCALL
fill_in_file_ea_information(FILE_EA_INFORMATION
* eai
, LONG
* length
) {
2303 *length
-= sizeof(FILE_EA_INFORMATION
);
2307 return STATUS_SUCCESS
;
2310 static NTSTATUS STDCALL
fill_in_file_access_information(FILE_ACCESS_INFORMATION
* fai
, LONG
* length
) {
2311 *length
-= sizeof(FILE_ACCESS_INFORMATION
);
2313 fai
->AccessFlags
= GENERIC_READ
;
2315 return STATUS_NOT_IMPLEMENTED
;
2318 static NTSTATUS STDCALL
fill_in_file_position_information(FILE_POSITION_INFORMATION
* fpi
, PFILE_OBJECT FileObject
, LONG
* length
) {
2319 RtlZeroMemory(fpi
, sizeof(FILE_POSITION_INFORMATION
));
2321 *length
-= sizeof(FILE_POSITION_INFORMATION
);
2323 fpi
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
2325 return STATUS_SUCCESS
;
2328 static NTSTATUS STDCALL
fill_in_file_mode_information(FILE_MODE_INFORMATION
* fmi
, ccb
* ccb
, LONG
* length
) {
2329 RtlZeroMemory(fmi
, sizeof(FILE_MODE_INFORMATION
));
2331 *length
-= sizeof(FILE_MODE_INFORMATION
);
2333 if (ccb
->options
& FILE_WRITE_THROUGH
)
2334 fmi
->Mode
|= FILE_WRITE_THROUGH
;
2336 if (ccb
->options
& FILE_SEQUENTIAL_ONLY
)
2337 fmi
->Mode
|= FILE_SEQUENTIAL_ONLY
;
2339 if (ccb
->options
& FILE_NO_INTERMEDIATE_BUFFERING
)
2340 fmi
->Mode
|= FILE_NO_INTERMEDIATE_BUFFERING
;
2342 if (ccb
->options
& FILE_SYNCHRONOUS_IO_ALERT
)
2343 fmi
->Mode
|= FILE_SYNCHRONOUS_IO_ALERT
;
2345 if (ccb
->options
& FILE_SYNCHRONOUS_IO_NONALERT
)
2346 fmi
->Mode
|= FILE_SYNCHRONOUS_IO_NONALERT
;
2348 if (ccb
->options
& FILE_DELETE_ON_CLOSE
)
2349 fmi
->Mode
|= FILE_DELETE_ON_CLOSE
;
2351 return STATUS_SUCCESS
;
2354 static NTSTATUS STDCALL
fill_in_file_alignment_information(FILE_ALIGNMENT_INFORMATION
* fai
, device_extension
* Vcb
, LONG
* length
) {
2355 RtlZeroMemory(fai
, sizeof(FILE_ALIGNMENT_INFORMATION
));
2357 *length
-= sizeof(FILE_ALIGNMENT_INFORMATION
);
2359 fai
->AlignmentRequirement
= Vcb
->devices
[0].devobj
->AlignmentRequirement
;
2361 return STATUS_SUCCESS
;
2364 static NTSTATUS STDCALL
fill_in_file_name_information(FILE_NAME_INFORMATION
* fni
, fcb
* fcb
, LONG
* length
) {
2368 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2369 ULONG datasuflen
= wcslen(datasuf
) * sizeof(WCHAR
);
2371 RtlZeroMemory(fni
, sizeof(FILE_NAME_INFORMATION
));
2373 *length
-= (LONG
)offsetof(FILE_NAME_INFORMATION
, FileName
[0]);
2375 TRACE("maximum length is %u\n", *length
);
2376 fni
->FileNameLength
= 0;
2378 fni
->FileName
[0] = 0;
2380 if (*length
>= (LONG
)fcb
->full_filename
.Length
) {
2381 RtlCopyMemory(fni
->FileName
, fcb
->full_filename
.Buffer
, fcb
->full_filename
.Length
);
2383 retlen
= fcb
->full_filename
.Length
;
2385 *length
-= fcb
->full_filename
.Length
;
2388 RtlCopyMemory(fni
->FileName
, fcb
->full_filename
.Buffer
, *length
);
2396 fni
->FileNameLength
= fcb
->full_filename
.Length
;
2399 if (*length
>= (LONG
)datasuflen
) {
2400 RtlCopyMemory(&fni
->FileName
[fcb
->full_filename
.Length
/ sizeof(WCHAR
)], datasuf
, datasuflen
);
2402 retlen
+= datasuflen
;
2404 *length
-= datasuflen
;
2407 RtlCopyMemory(&fni
->FileName
[fcb
->full_filename
.Length
/ sizeof(WCHAR
)], datasuf
, *length
);
2416 TRACE("%.*S\n", retlen
/ sizeof(WCHAR
), fni
->FileName
);
2418 return STATUS_SUCCESS
;
2421 static NTSTATUS STDCALL
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION
* ati
, fcb
* fcb
, LONG
* length
) {
2422 *length
-= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
);
2424 ati
->FileAttributes
= fcb
->ads
? fcb
->par
->atts
: fcb
->atts
;
2425 ati
->ReparseTag
= 0;
2427 return STATUS_SUCCESS
;
2431 UNICODE_STRING name
;
2435 static NTSTATUS STDCALL
fill_in_file_stream_information(FILE_STREAM_INFORMATION
* fsi
, fcb
* fcb
, LONG
* length
) {
2437 UINT64 i
, num_streams
;
2438 stream_info
* streams
;
2439 FILE_STREAM_INFORMATION
* entry
;
2442 traverse_ptr tp
, next_tp
;
2445 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2446 static char xapref
[] = "user.";
2449 suf
.Buffer
= datasuf
;
2450 suf
.Length
= suf
.MaximumLength
= wcslen(datasuf
) * sizeof(WCHAR
);
2454 searchkey
.obj_id
= fcb
->inode
;
2455 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
2456 searchkey
.offset
= 0;
2458 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
2459 if (!NT_SUCCESS(Status
)) {
2460 ERR("error - find_item returned %08x\n", Status
);
2465 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
2466 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
2467 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(DIR_ITEM
));
2469 ULONG len
= tp
.item
->size
;
2470 DIR_ITEM
* xa
= (DIR_ITEM
*)tp
.item
->data
;
2473 if (len
< sizeof(DIR_ITEM
) || len
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
2474 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2478 if (xa
->n
> strlen(xapref
) && RtlCompareMemory(xa
->name
, xapref
, strlen(xapref
)) == strlen(xapref
)) {
2479 if (tp
.item
->key
.offset
!= EA_DOSATTRIB_HASH
|| xa
->n
!= strlen(EA_DOSATTRIB
) || RtlCompareMemory(xa
->name
, EA_DOSATTRIB
, xa
->n
) != xa
->n
) {
2484 len
-= sizeof(DIR_ITEM
) - sizeof(char) + xa
->n
+ xa
->m
;
2485 xa
= (DIR_ITEM
*)&xa
->name
[xa
->n
+ xa
->m
]; // FIXME - test xattr hash collisions work
2490 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
2492 free_traverse_ptr(&tp
);
2495 if (next_tp
.item
->key
.obj_id
> fcb
->inode
|| next_tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
)
2500 free_traverse_ptr(&tp
);
2502 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
2503 if (!NT_SUCCESS(Status
)) {
2504 ERR("error - find_item returned %08x\n", Status
);
2508 streams
= ExAllocatePoolWithTag(PagedPool
, sizeof(stream_info
) * num_streams
, ALLOC_TAG
);
2510 ERR("out of memory\n");
2511 return STATUS_INSUFFICIENT_RESOURCES
;
2516 streams
[0].name
.Length
= streams
[0].name
.MaximumLength
= 0;
2517 streams
[0].name
.Buffer
= NULL
;
2518 streams
[0].size
= fcb
->inode_item
.st_size
;
2519 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + streams
[0].name
.Length
;
2523 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
2524 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
2525 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(DIR_ITEM
));
2527 ULONG len
= tp
.item
->size
;
2528 DIR_ITEM
* xa
= (DIR_ITEM
*)tp
.item
->data
;
2532 if (len
< sizeof(DIR_ITEM
) || len
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
2533 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2537 if (xa
->n
> strlen(xapref
) && RtlCompareMemory(xa
->name
, xapref
, strlen(xapref
)) == strlen(xapref
) &&
2538 (tp
.item
->key
.offset
!= EA_DOSATTRIB_HASH
|| xa
->n
!= strlen(EA_DOSATTRIB
) || RtlCompareMemory(xa
->name
, EA_DOSATTRIB
, xa
->n
) != xa
->n
)) {
2539 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, &xa
->name
[strlen(xapref
)], xa
->n
- strlen(xapref
));
2540 if (!NT_SUCCESS(Status
)) {
2543 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
2545 for (j
= i
; j
< num_streams
; j
++)
2546 streams
[j
].name
.Buffer
= NULL
;
2551 streams
[i
].name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
2552 if (!streams
[i
].name
.Buffer
) {
2555 ERR("out of memory\n");
2557 for (j
= i
+1; j
< num_streams
; j
++)
2558 streams
[j
].name
.Buffer
= NULL
;
2560 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2564 Status
= RtlUTF8ToUnicodeN(streams
[i
].name
.Buffer
, stringlen
, &stringlen
, &xa
->name
[strlen(xapref
)], xa
->n
- strlen(xapref
));
2566 if (!NT_SUCCESS(Status
)) {
2569 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
2571 ExFreePool(streams
[i
].name
.Buffer
);
2572 for (j
= i
; j
< num_streams
; j
++)
2573 streams
[j
].name
.Buffer
= NULL
;
2578 streams
[i
].name
.Length
= streams
[i
].name
.MaximumLength
= stringlen
;
2580 streams
[i
].size
= xa
->m
;
2581 reqsize
= sector_align(reqsize
, sizeof(LONGLONG
));
2582 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + streams
[i
].name
.Length
;
2584 TRACE("streams[%llu].name = %.*S (length = %u)\n", i
, streams
[i
].name
.Length
/ sizeof(WCHAR
), streams
[i
].name
.Buffer
, streams
[i
].name
.Length
/ sizeof(WCHAR
));
2589 len
-= sizeof(DIR_ITEM
) - sizeof(char) + xa
->n
+ xa
->m
;
2590 xa
= (DIR_ITEM
*)&xa
->name
[xa
->n
+ xa
->m
]; // FIXME - test xattr hash collisions work
2595 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
2597 free_traverse_ptr(&tp
);
2600 if (next_tp
.item
->key
.obj_id
> fcb
->inode
|| next_tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
)
2605 free_traverse_ptr(&tp
);
2607 TRACE("length = %i, reqsize = %u\n", *length
, reqsize
);
2609 if (reqsize
> *length
) {
2610 Status
= STATUS_BUFFER_OVERFLOW
;
2615 for (i
= 0; i
< num_streams
; i
++) {
2616 entry
->StreamNameLength
= streams
[i
].name
.Length
+ suf
.Length
+ sizeof(WCHAR
);
2617 entry
->StreamSize
.QuadPart
= streams
[i
].size
;
2620 entry
->StreamAllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2622 entry
->StreamAllocationSize
.QuadPart
= streams
[i
].size
;
2624 entry
->StreamName
[0] = ':';
2626 if (streams
[i
].name
.Length
> 0)
2627 RtlCopyMemory(&entry
->StreamName
[1], streams
[i
].name
.Buffer
, streams
[i
].name
.Length
);
2629 RtlCopyMemory(&entry
->StreamName
[1 + (streams
[i
].name
.Length
/ sizeof(WCHAR
))], suf
.Buffer
, suf
.Length
);
2631 if (i
== num_streams
- 1)
2632 entry
->NextEntryOffset
= 0;
2634 entry
->NextEntryOffset
= sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + streams
[i
].name
.Length
, sizeof(LONGLONG
));
2636 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ entry
->NextEntryOffset
);
2642 Status
= STATUS_SUCCESS
;
2645 for (i
= 0; i
< num_streams
; i
++) {
2646 if (streams
[i
].name
.Buffer
)
2647 ExFreePool(streams
[i
].name
.Buffer
);
2650 ExFreePool(streams
);
2655 static NTSTATUS STDCALL
fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION
* fsli
, fcb
* fcb
, LONG
* length
) {
2656 TRACE("FileStandardLinkInformation\n");
2658 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
2660 fsli
->NumberOfAccessibleLinks
= fcb
->inode_item
.st_nlink
;
2661 fsli
->TotalNumberOfLinks
= fcb
->inode_item
.st_nlink
;
2662 fsli
->DeletePending
= fcb
->delete_on_close
;
2663 fsli
->Directory
= fcb
->type
== BTRFS_TYPE_DIRECTORY
? TRUE
: FALSE
;
2665 *length
-= sizeof(FILE_STANDARD_LINK_INFORMATION
);
2667 return STATUS_SUCCESS
;
2670 static NTSTATUS STDCALL
query_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
2671 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2672 LONG length
= IrpSp
->Parameters
.QueryFile
.Length
;
2673 fcb
* fcb
= FileObject
->FsContext
;
2674 ccb
* ccb
= FileObject
->FsContext2
;
2677 TRACE("(%p, %p, %p)\n", Vcb
, FileObject
, Irp
);
2678 TRACE("fcb = %p\n", fcb
);
2680 if (fcb
== Vcb
->volume_fcb
)
2681 return STATUS_INVALID_PARAMETER
;
2683 switch (IrpSp
->Parameters
.QueryFile
.FileInformationClass
) {
2684 case FileAllInformation
:
2686 FILE_ALL_INFORMATION
* fai
= Irp
->AssociatedIrp
.SystemBuffer
;
2689 TRACE("FileAllInformation\n");
2692 ii
= &fcb
->par
->inode_item
;
2694 ii
= &fcb
->inode_item
;
2697 fill_in_file_basic_information(&fai
->BasicInformation
, ii
, &length
, fcb
);
2700 fill_in_file_standard_information(&fai
->StandardInformation
, fcb
, &length
);
2703 fill_in_file_internal_information(&fai
->InternalInformation
, fcb
->inode
, &length
);
2706 fill_in_file_ea_information(&fai
->EaInformation
, &length
);
2709 fill_in_file_access_information(&fai
->AccessInformation
, &length
);
2712 fill_in_file_position_information(&fai
->PositionInformation
, FileObject
, &length
);
2715 fill_in_file_mode_information(&fai
->ModeInformation
, ccb
, &length
);
2718 fill_in_file_alignment_information(&fai
->AlignmentInformation
, Vcb
, &length
);
2721 fill_in_file_name_information(&fai
->NameInformation
, fcb
, &length
);
2723 Status
= STATUS_SUCCESS
;
2728 case FileAttributeTagInformation
:
2730 FILE_ATTRIBUTE_TAG_INFORMATION
* ati
= Irp
->AssociatedIrp
.SystemBuffer
;
2732 TRACE("FileAttributeTagInformation\n");
2734 Status
= fill_in_file_attribute_information(ati
, fcb
, &length
);
2739 case FileBasicInformation
:
2741 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
2744 TRACE("FileBasicInformation\n");
2746 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_BASIC_INFORMATION
)) {
2748 Status
= STATUS_BUFFER_OVERFLOW
;
2753 ii
= &fcb
->par
->inode_item
;
2755 ii
= &fcb
->inode_item
;
2757 Status
= fill_in_file_basic_information(fbi
, ii
, &length
, fcb
);
2761 case FileCompressionInformation
:
2762 FIXME("STUB: FileCompressionInformation\n");
2763 Status
= STATUS_INVALID_PARAMETER
;
2766 case FileEaInformation
:
2768 FILE_EA_INFORMATION
* eai
= Irp
->AssociatedIrp
.SystemBuffer
;
2770 TRACE("FileEaInformation\n");
2772 Status
= fill_in_file_ea_information(eai
, &length
);
2777 case FileInternalInformation
:
2779 FILE_INTERNAL_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
2781 TRACE("FileInternalInformation\n");
2783 Status
= fill_in_file_internal_information(fii
, fcb
->inode
, &length
);
2788 case FileNameInformation
:
2790 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
2792 TRACE("FileNameInformation\n");
2794 Status
= fill_in_file_name_information(fni
, fcb
, &length
);
2799 case FileNetworkOpenInformation
:
2801 FILE_NETWORK_OPEN_INFORMATION
* fnoi
= Irp
->AssociatedIrp
.SystemBuffer
;
2803 TRACE("FileNetworkOpenInformation\n");
2805 Status
= fill_in_file_network_open_information(fnoi
, fcb
, &length
);
2810 case FilePositionInformation
:
2812 FILE_POSITION_INFORMATION
* fpi
= Irp
->AssociatedIrp
.SystemBuffer
;
2814 TRACE("FilePositionInformation\n");
2816 Status
= fill_in_file_position_information(fpi
, FileObject
, &length
);
2821 case FileStandardInformation
:
2823 FILE_STANDARD_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
2825 TRACE("FileStandardInformation\n");
2827 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STANDARD_INFORMATION
)) {
2829 Status
= STATUS_BUFFER_OVERFLOW
;
2833 Status
= fill_in_file_standard_information(fsi
, fcb
, &length
);
2838 case FileStreamInformation
:
2840 FILE_STREAM_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
2842 TRACE("FileStreamInformation\n");
2844 Status
= fill_in_file_stream_information(fsi
, fcb
, &length
);
2849 #if (NTDDI_VERSION >= NTDDI_VISTA)
2850 case FileHardLinkInformation
:
2851 FIXME("STUB: FileHardLinkInformation\n");
2852 Status
= STATUS_INVALID_PARAMETER
;
2855 case FileNormalizedNameInformation
:
2857 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
2859 TRACE("FileNormalizedNameInformation\n");
2861 Status
= fill_in_file_name_information(fni
, fcb
, &length
);
2868 #if (NTDDI_VERSION >= NTDDI_WIN7)
2869 case FileStandardLinkInformation
:
2871 FILE_STANDARD_LINK_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
2873 TRACE("FileStandardLinkInformation\n");
2875 Status
= fill_in_file_standard_link_information(fsli
, fcb
, &length
);
2880 case FileRemoteProtocolInformation
:
2881 FIXME("STUB: FileRemoteProtocolInformation\n");
2882 Status
= STATUS_INVALID_PARAMETER
;
2887 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryFile
.FileInformationClass
);
2888 Status
= STATUS_INVALID_PARAMETER
;
2894 Status
= STATUS_BUFFER_OVERFLOW
;
2897 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- length
;
2900 TRACE("query_info returning %08x\n", Status
);
2905 NTSTATUS STDCALL
drv_query_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2906 PIO_STACK_LOCATION IrpSp
;
2909 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2912 FsRtlEnterFileSystem();
2914 top_level
= is_top_level(Irp
);
2916 Irp
->IoStatus
.Information
= 0;
2918 TRACE("query information\n");
2920 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2922 acquire_tree_lock(Vcb
, FALSE
);
2924 fcb
= IrpSp
->FileObject
->FsContext
;
2925 TRACE("fcb = %p\n", fcb
);
2926 TRACE("fcb->subvol = %p\n", fcb
->subvol
);
2928 Status
= query_info(fcb
->Vcb
, IrpSp
->FileObject
, Irp
);
2930 TRACE("returning %08x\n", Status
);
2932 Irp
->IoStatus
.Status
= Status
;
2934 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
2936 release_tree_lock(Vcb
, FALSE
);
2939 IoSetTopLevelIrp(NULL
);
2941 FsRtlExitFileSystem();