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 #if (NTDDI_VERSION >= NTDDI_WIN10)
21 // not currently in mingw - introduced with Windows 10
22 #ifndef FileIdInformation
23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
27 static NTSTATUS
get_inode_dir_path(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PUNICODE_STRING us
, PIRP Irp
);
29 static NTSTATUS STDCALL
set_basic_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
30 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
31 fcb
* fcb
= FileObject
->FsContext
;
32 ccb
* ccb
= FileObject
->FsContext2
;
33 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
34 ULONG defda
, filter
= 0;
35 BOOL inode_item_changed
= FALSE
;
39 if (fileref
&& fileref
->parent
)
40 fcb
= fileref
->parent
->fcb
;
42 ERR("stream did not have fileref\n");
43 return STATUS_INTERNAL_ERROR
;
47 TRACE("file = %S, attributes = %x\n", file_desc(FileObject
), fbi
->FileAttributes
);
49 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
51 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
52 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
53 Status
= STATUS_INVALID_PARAMETER
;
57 if (fcb
->inode
== SUBVOL_ROOT_INODE
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
&&
58 (fbi
->FileAttributes
== 0 || fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)) {
59 Status
= STATUS_ACCESS_DENIED
;
63 if (fbi
->CreationTime
.QuadPart
== -1)
64 ccb
->user_set_creation_time
= TRUE
;
65 else if (fbi
->CreationTime
.QuadPart
!= 0) {
66 win_time_to_unix(fbi
->CreationTime
, &fcb
->inode_item
.otime
);
67 inode_item_changed
= TRUE
;
68 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
70 ccb
->user_set_creation_time
= TRUE
;
73 if (fbi
->LastAccessTime
.QuadPart
== -1)
74 ccb
->user_set_access_time
= TRUE
;
75 else if (fbi
->LastAccessTime
.QuadPart
!= 0) {
76 win_time_to_unix(fbi
->LastAccessTime
, &fcb
->inode_item
.st_atime
);
77 inode_item_changed
= TRUE
;
78 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
80 ccb
->user_set_access_time
= TRUE
;
83 if (fbi
->LastWriteTime
.QuadPart
== -1)
84 ccb
->user_set_write_time
= TRUE
;
85 else if (fbi
->LastWriteTime
.QuadPart
!= 0) {
86 win_time_to_unix(fbi
->LastWriteTime
, &fcb
->inode_item
.st_mtime
);
87 inode_item_changed
= TRUE
;
88 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
90 ccb
->user_set_write_time
= TRUE
;
93 if (fbi
->ChangeTime
.QuadPart
== -1)
94 ccb
->user_set_change_time
= TRUE
;
95 else if (fbi
->ChangeTime
.QuadPart
!= 0) {
96 win_time_to_unix(fbi
->ChangeTime
, &fcb
->inode_item
.st_ctime
);
97 inode_item_changed
= TRUE
;
100 ccb
->user_set_change_time
= TRUE
;
103 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
104 if (fbi
->FileAttributes
!= 0) {
108 defda
= get_file_attributes(Vcb
, &fcb
->inode_item
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fileref
->filepart
.Length
> 0 && fileref
->filepart
.Buffer
[0] == '.', TRUE
, Irp
);
110 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
111 fbi
->FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
112 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
113 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
115 fcb
->atts_changed
= TRUE
;
117 if (fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)
118 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
120 if (defda
== fbi
->FileAttributes
)
121 fcb
->atts_deleted
= TRUE
;
123 fcb
->atts
= fbi
->FileAttributes
;
125 KeQuerySystemTime(&time
);
126 win_time_to_unix(time
, &now
);
128 if (!ccb
->user_set_change_time
)
129 fcb
->inode_item
.st_ctime
= now
;
131 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
132 fcb
->subvol
->root_item
.ctime
= now
;
134 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
135 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
136 fcb
->subvol
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
138 fcb
->subvol
->root_item
.flags
&= ~BTRFS_SUBVOL_READONLY
;
141 inode_item_changed
= TRUE
;
143 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
146 if (inode_item_changed
) {
147 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
148 fcb
->inode_item
.sequence
++;
149 fcb
->inode_item_changed
= TRUE
;
155 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
);
157 Status
= STATUS_SUCCESS
;
160 ExReleaseResourceLite(fcb
->Header
.Resource
);
165 static NTSTATUS STDCALL
set_disposition_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
166 FILE_DISPOSITION_INFORMATION
* fdi
= Irp
->AssociatedIrp
.SystemBuffer
;
167 fcb
* fcb
= FileObject
->FsContext
;
168 ccb
* ccb
= FileObject
->FsContext2
;
169 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
174 return STATUS_INVALID_PARAMETER
;
176 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
178 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
180 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi
->DeleteFile
? "TRUE" : "FALSE", file_desc(FileObject
), fcb
);
184 atts
= fileref
->parent
->fcb
->atts
;
186 ERR("no fileref for stream\n");
187 Status
= STATUS_INTERNAL_ERROR
;
193 TRACE("atts = %x\n", atts
);
195 if (atts
& FILE_ATTRIBUTE_READONLY
) {
196 Status
= STATUS_CANNOT_DELETE
;
200 // FIXME - can we skip this bit for subvols?
201 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0) {
202 Status
= STATUS_DIRECTORY_NOT_EMPTY
;
206 if (!MmFlushImageSection(&fcb
->nonpaged
->segment_object
, MmFlushForDelete
)) {
207 WARN("trying to delete file which is being mapped as an image\n");
208 Status
= STATUS_CANNOT_DELETE
;
212 ccb
->fileref
->delete_on_close
= fdi
->DeleteFile
;
214 FileObject
->DeletePending
= fdi
->DeleteFile
;
216 Status
= STATUS_SUCCESS
;
219 ExReleaseResourceLite(fcb
->Header
.Resource
);
221 ExReleaseResourceLite(&Vcb
->fcb_lock
);
226 BOOL
has_open_children(file_ref
* fileref
) {
227 LIST_ENTRY
* le
= fileref
->children
.Flink
;
229 if (IsListEmpty(&fileref
->children
))
232 while (le
!= &fileref
->children
) {
233 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
235 if (c
->open_count
> 0)
238 if (has_open_children(c
))
247 static NTSTATUS
duplicate_fcb(fcb
* oldfcb
, fcb
** pfcb
) {
248 device_extension
* Vcb
= oldfcb
->Vcb
;
252 // FIXME - we can skip a lot of this if the inode is about to be deleted
254 fcb
= create_fcb(PagedPool
); // FIXME - what if we duplicate the paging file?
256 ERR("out of memory\n");
257 return STATUS_INSUFFICIENT_RESOURCES
;
262 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
263 fcb
->Header
.AllocationSize
= oldfcb
->Header
.AllocationSize
;
264 fcb
->Header
.FileSize
= oldfcb
->Header
.FileSize
;
265 fcb
->Header
.ValidDataLength
= oldfcb
->Header
.ValidDataLength
;
267 fcb
->type
= oldfcb
->type
;
271 fcb
->adshash
= oldfcb
->adshash
;
272 fcb
->adsmaxlen
= oldfcb
->adsmaxlen
;
274 if (oldfcb
->adsxattr
.Buffer
&& oldfcb
->adsxattr
.Length
> 0) {
275 fcb
->adsxattr
.Length
= oldfcb
->adsxattr
.Length
;
276 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
277 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
279 if (!fcb
->adsxattr
.Buffer
) {
280 ERR("out of memory\n");
282 return STATUS_INSUFFICIENT_RESOURCES
;
285 RtlCopyMemory(fcb
->adsxattr
.Buffer
, oldfcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
286 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
289 if (oldfcb
->adsdata
.Buffer
&& oldfcb
->adsdata
.Length
> 0) {
290 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= oldfcb
->adsdata
.Length
;
291 fcb
->adsdata
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsdata
.MaximumLength
, ALLOC_TAG
);
293 if (!fcb
->adsdata
.Buffer
) {
294 ERR("out of memory\n");
296 return STATUS_INSUFFICIENT_RESOURCES
;
299 RtlCopyMemory(fcb
->adsdata
.Buffer
, oldfcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
305 RtlCopyMemory(&fcb
->inode_item
, &oldfcb
->inode_item
, sizeof(INODE_ITEM
));
306 fcb
->inode_item_changed
= TRUE
;
308 if (oldfcb
->sd
&& RtlLengthSecurityDescriptor(oldfcb
->sd
) > 0) {
309 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, RtlLengthSecurityDescriptor(oldfcb
->sd
), ALLOC_TAG
);
311 ERR("out of memory\n");
313 return STATUS_INSUFFICIENT_RESOURCES
;
316 RtlCopyMemory(fcb
->sd
, oldfcb
->sd
, RtlLengthSecurityDescriptor(oldfcb
->sd
));
319 fcb
->atts
= oldfcb
->atts
;
321 le
= oldfcb
->extents
.Flink
;
322 while (le
!= &oldfcb
->extents
) {
323 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
326 extent
* ext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
329 ERR("out of memory\n");
331 return STATUS_INSUFFICIENT_RESOURCES
;
334 ext2
->offset
= ext
->offset
;
335 ext2
->datalen
= ext
->datalen
;
337 if (ext2
->datalen
> 0) {
338 ext2
->data
= ExAllocatePoolWithTag(PagedPool
, ext2
->datalen
, ALLOC_TAG
);
341 ERR("out of memory\n");
343 return STATUS_INSUFFICIENT_RESOURCES
;
346 RtlCopyMemory(ext2
->data
, ext
->data
, ext2
->datalen
);
350 ext2
->unique
= FALSE
;
351 ext2
->ignore
= FALSE
;
352 ext2
->inserted
= TRUE
;
356 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
358 if (ext
->data
->compression
== BTRFS_COMPRESSION_NONE
)
359 len
= ed2
->num_bytes
;
363 len
= len
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
;
365 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
367 ERR("out of memory\n");
369 return STATUS_INSUFFICIENT_RESOURCES
;
372 RtlCopyMemory(ext2
->csum
, ext
->csum
, len
);
376 InsertTailList(&fcb
->extents
, &ext2
->list_entry
);
382 le
= oldfcb
->hardlinks
.Flink
;
383 while (le
!= &oldfcb
->hardlinks
) {
384 hardlink
*hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
), *hl2
;
386 hl2
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
389 ERR("out of memory\n");
391 return STATUS_INSUFFICIENT_RESOURCES
;
394 hl2
->parent
= hl
->parent
;
395 hl2
->index
= hl
->index
;
397 hl2
->name
.Length
= hl2
->name
.MaximumLength
= hl
->name
.Length
;
398 hl2
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->name
.MaximumLength
, ALLOC_TAG
);
400 if (!hl2
->name
.Buffer
) {
401 ERR("out of memory\n");
404 return STATUS_INSUFFICIENT_RESOURCES
;
407 RtlCopyMemory(hl2
->name
.Buffer
, hl
->name
.Buffer
, hl
->name
.Length
);
409 hl2
->utf8
.Length
= hl2
->utf8
.MaximumLength
= hl
->utf8
.Length
;
410 hl2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->utf8
.MaximumLength
, ALLOC_TAG
);
412 if (!hl2
->utf8
.Buffer
) {
413 ERR("out of memory\n");
414 ExFreePool(hl2
->name
.Buffer
);
417 return STATUS_INSUFFICIENT_RESOURCES
;
420 RtlCopyMemory(hl2
->utf8
.Buffer
, hl
->utf8
.Buffer
, hl
->utf8
.Length
);
422 InsertTailList(&fcb
->hardlinks
, &hl2
->list_entry
);
427 fcb
->last_dir_index
= oldfcb
->last_dir_index
;
429 if (oldfcb
->reparse_xattr
.Buffer
&& oldfcb
->reparse_xattr
.Length
> 0) {
430 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= oldfcb
->reparse_xattr
.Length
;
432 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.MaximumLength
, ALLOC_TAG
);
433 if (!fcb
->reparse_xattr
.Buffer
) {
434 ERR("out of memory\n");
436 return STATUS_INSUFFICIENT_RESOURCES
;
439 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, oldfcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
442 if (oldfcb
->ea_xattr
.Buffer
&& oldfcb
->ea_xattr
.Length
> 0) {
443 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= oldfcb
->ea_xattr
.Length
;
445 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->ea_xattr
.MaximumLength
, ALLOC_TAG
);
446 if (!fcb
->ea_xattr
.Buffer
) {
447 ERR("out of memory\n");
449 return STATUS_INSUFFICIENT_RESOURCES
;
452 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, oldfcb
->ea_xattr
.Buffer
, fcb
->ea_xattr
.Length
);
458 return STATUS_SUCCESS
;
461 typedef struct _move_entry
{
464 file_ref
* dummyfileref
;
465 struct _move_entry
* parent
;
466 LIST_ENTRY list_entry
;
469 static NTSTATUS
add_children_to_move_list(move_entry
* me
, PIRP Irp
) {
477 static char xapref
[] = "user.";
478 ULONG xapreflen
= strlen(xapref
);
480 ExAcquireResourceSharedLite(&me
->fileref
->nonpaged
->children_lock
, TRUE
);
482 le
= me
->fileref
->children
.Flink
;
483 while (le
!= &me
->fileref
->children
) {
484 file_ref
* fr
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
487 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
489 ERR("out of memory\n");
490 Status
= STATUS_INSUFFICIENT_RESOURCES
;
496 increase_fileref_refcount(fr
);
498 me2
->dummyfcb
= NULL
;
499 me2
->dummyfileref
= NULL
;
502 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
508 searchkey
.obj_id
= me
->fileref
->fcb
->inode
;
509 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
510 searchkey
.offset
= 0;
512 Status
= find_item(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
513 if (!NT_SUCCESS(Status
)) {
514 ERR("error - find_item returned %08x\n", Status
);
519 traverse_ptr next_tp
;
521 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
522 DIR_ITEM
* xa
= (DIR_ITEM
*)tp
.item
->data
;
525 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
526 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
));
527 Status
= STATUS_INTERNAL_ERROR
;
534 if (len
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
535 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
536 Status
= STATUS_INTERNAL_ERROR
;
540 if (xa
->n
> xapreflen
&& RtlCompareMemory(xa
->name
, xapref
, xapreflen
) == xapreflen
&&
541 (tp
.item
->key
.offset
!= EA_DOSATTRIB_HASH
|| xa
->n
!= strlen(EA_DOSATTRIB
) || RtlCompareMemory(xa
->name
, EA_DOSATTRIB
, xa
->n
) != xa
->n
) &&
542 (tp
.item
->key
.offset
!= EA_EA_HASH
|| xa
->n
!= strlen(EA_EA
) || RtlCompareMemory(xa
->name
, EA_EA
, xa
->n
) != xa
->n
)
546 le
= me
->fileref
->children
.Flink
;
548 while (le
!= &me
->fileref
->children
) {
549 file_ref
* fr
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
551 if (fr
->fcb
->ads
&& fr
->fcb
->adshash
== tp
.item
->key
.offset
&& fr
->fcb
->adsxattr
.Length
== xa
->n
&&
552 RtlCompareMemory(fr
->fcb
->adsxattr
.Buffer
, xa
->name
, xa
->n
) == xa
->n
) {
566 xattr
.Length
= xa
->n
;
567 xattr
.MaximumLength
= xattr
.Length
+ 1;
568 xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, xattr
.MaximumLength
, ALLOC_TAG
);
571 ERR("out of memory\n");
572 Status
= STATUS_INSUFFICIENT_RESOURCES
;
576 RtlCopyMemory(xattr
.Buffer
, xa
->name
, xa
->n
);
577 xattr
.Buffer
[xa
->n
] = 0;
579 Status
= open_fcb_stream(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, me
->fileref
->fcb
->inode
, &xattr
,
580 tp
.item
->key
.offset
, me
->fileref
->fcb
, &fcb
, Irp
);
582 if (!NT_SUCCESS(Status
)) {
583 ERR("open_fcb_stream returned %08x\n", Status
);
584 ExFreePool(xattr
.Buffer
);
588 fr
= create_fileref();
590 ERR("out of memory\n");
592 Status
= STATUS_INSUFFICIENT_RESOURCES
;
598 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, &xa
->name
[xapreflen
], xa
->n
- xapreflen
);
599 if (!NT_SUCCESS(Status
)) {
600 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
605 fr
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
606 if (!fr
->filepart
.Buffer
) {
607 ERR("out of memory\n");
608 Status
= STATUS_INSUFFICIENT_RESOURCES
;
613 Status
= RtlUTF8ToUnicodeN(fr
->filepart
.Buffer
, stringlen
, &stringlen
, &xa
->name
[xapreflen
], xa
->n
- xapreflen
);
614 if (!NT_SUCCESS(Status
)) {
615 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
620 fr
->filepart
.Length
= fr
->filepart
.MaximumLength
= stringlen
;
622 Status
= RtlUpcaseUnicodeString(&fr
->filepart_uc
, &fr
->filepart
, TRUE
);
623 if (!NT_SUCCESS(Status
)) {
624 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
629 fr
->parent
= (struct _file_ref
*)me
->fileref
;
630 increase_fileref_refcount(fr
->parent
);
632 insert_fileref_child(me
->fileref
, fr
, FALSE
);
634 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
636 ERR("out of memory\n");
637 Status
= STATUS_INSUFFICIENT_RESOURCES
;
643 me2
->dummyfcb
= NULL
;
644 me2
->dummyfileref
= NULL
;
647 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
651 len
-= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
654 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
658 b
= find_next_item(me
->fileref
->fcb
->Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
662 if (next_tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (next_tp
.item
->key
.obj_id
== searchkey
.obj_id
&& next_tp
.item
->key
.obj_type
> searchkey
.obj_type
))
667 if (me
->fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
&& me
->fileref
->fcb
->inode_item
.st_size
!= 0) {
668 searchkey
.obj_id
= me
->fileref
->fcb
->inode
;
669 searchkey
.obj_type
= TYPE_DIR_INDEX
;
670 searchkey
.offset
= 2;
672 Status
= find_item(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
673 if (!NT_SUCCESS(Status
)) {
674 ERR("error - find_item returned %08x\n", Status
);
679 traverse_ptr next_tp
;
681 // FIXME - both lists are ordered; we can make this more efficient
683 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
686 le
= me
->fileref
->children
.Flink
;
688 while (le
!= &me
->fileref
->children
) {
689 file_ref
* fr
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
692 if (fr
->index
== tp
.item
->key
.offset
) {
695 } else if (fr
->index
> tp
.item
->key
.offset
)
703 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
705 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
706 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
));
707 Status
= STATUS_INTERNAL_ERROR
;
711 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->m
+ di
->n
) {
712 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
) - 1 + di
->m
+ di
->n
);
713 Status
= STATUS_INTERNAL_ERROR
;
718 ERR("(%llx,%x,%llx): filename length was 0\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
719 Status
= STATUS_INTERNAL_ERROR
;
723 if (di
->key
.obj_type
== TYPE_INODE_ITEM
|| di
->key
.obj_type
== TYPE_ROOT_ITEM
) {
730 dir_child
* dc
= NULL
;
732 utf8
.Length
= utf8
.MaximumLength
= di
->n
;
733 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
735 ERR("out of memory\n");
736 Status
= STATUS_INSUFFICIENT_RESOURCES
;
740 RtlCopyMemory(utf8
.Buffer
, di
->name
, di
->n
);
742 if (di
->key
.obj_type
== TYPE_ROOT_ITEM
) {
747 le2
= me
->fileref
->fcb
->Vcb
->roots
.Flink
;
748 while (le2
!= &me
->fileref
->fcb
->Vcb
->roots
) {
749 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
751 if (r2
->id
== di
->key
.obj_id
) {
760 ERR("could not find subvol %llx\n", di
->key
.obj_id
);
761 Status
= STATUS_INTERNAL_ERROR
;
765 inode
= SUBVOL_ROOT_INODE
;
767 subvol
= me
->fileref
->fcb
->subvol
;
768 inode
= di
->key
.obj_id
;
771 Status
= open_fcb(me
->fileref
->fcb
->Vcb
, subvol
, inode
, di
->type
, &utf8
, me
->fileref
->fcb
, &fcb
, PagedPool
, Irp
);
773 if (!NT_SUCCESS(Status
)) {
774 ERR("open_fcb returned %08x\n", Status
);
775 ExFreePool(utf8
.Buffer
);
779 fr
= create_fileref();
781 ERR("out of memory\n");
782 Status
= STATUS_INSUFFICIENT_RESOURCES
;
783 ExFreePool(utf8
.Buffer
);
791 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, utf8
.Buffer
, utf8
.Length
);
792 if (!NT_SUCCESS(Status
)) {
793 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
798 fr
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
799 if (!fr
->filepart
.Buffer
) {
800 ERR("out of memory\n");
801 Status
= STATUS_INSUFFICIENT_RESOURCES
;
806 Status
= RtlUTF8ToUnicodeN(fr
->filepart
.Buffer
, stringlen
, &stringlen
, utf8
.Buffer
, utf8
.Length
);
808 if (!NT_SUCCESS(Status
)) {
809 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
814 fr
->filepart
.Length
= fr
->filepart
.MaximumLength
= stringlen
;
816 Status
= RtlUpcaseUnicodeString(&fr
->filepart_uc
, &fr
->filepart
, TRUE
);
818 if (!NT_SUCCESS(Status
)) {
819 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
824 fr
->parent
= me
->fileref
;
826 fr
->index
= tp
.item
->key
.offset
;
827 increase_fileref_refcount(me
->fileref
);
829 Status
= add_dir_child(me
->fileref
->fcb
, di
->key
.obj_type
== TYPE_ROOT_ITEM
? subvol
->id
: fr
->fcb
->inode
,
830 di
->key
.obj_type
== TYPE_ROOT_ITEM
? TRUE
: FALSE
, fr
->index
, &utf8
, &fr
->filepart
, &fr
->filepart_uc
, BTRFS_TYPE_DIRECTORY
, &dc
);
831 if (!NT_SUCCESS(Status
))
832 WARN("add_dir_child returned %08x\n", Status
);
837 insert_fileref_child(fr
->parent
, fr
, FALSE
);
839 if (fr
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
840 fr
->fcb
->fileref
= fr
;
842 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
844 ERR("out of memory\n");
845 Status
= STATUS_INSUFFICIENT_RESOURCES
;
851 me2
->dummyfcb
= NULL
;
852 me2
->dummyfileref
= NULL
;
855 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
857 ERR("unrecognized key (%llx,%x,%llx)\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
858 Status
= STATUS_INTERNAL_ERROR
;
864 b
= find_next_item(me
->fileref
->fcb
->Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
868 if (next_tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (next_tp
.item
->key
.obj_id
== searchkey
.obj_id
&& next_tp
.item
->key
.obj_type
> searchkey
.obj_type
))
874 Status
= STATUS_SUCCESS
;
877 ExReleaseResourceLite(&me
->fileref
->nonpaged
->children_lock
);
882 void remove_dir_child_from_hash_lists(fcb
* fcb
, dir_child
* dc
) {
887 if (fcb
->hash_ptrs
[c
] == &dc
->list_entry_hash
) {
888 if (dc
->list_entry_hash
.Flink
== &fcb
->dir_children_hash
)
889 fcb
->hash_ptrs
[c
] = NULL
;
891 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash
.Flink
, dir_child
, list_entry_hash
);
893 if (dc2
->hash
>> 24 == c
)
894 fcb
->hash_ptrs
[c
] = &dc2
->list_entry_hash
;
896 fcb
->hash_ptrs
[c
] = NULL
;
900 RemoveEntryList(&dc
->list_entry_hash
);
902 c
= dc
->hash_uc
>> 24;
904 if (fcb
->hash_ptrs_uc
[c
] == &dc
->list_entry_hash_uc
) {
905 if (dc
->list_entry_hash_uc
.Flink
== &fcb
->dir_children_hash_uc
)
906 fcb
->hash_ptrs_uc
[c
] = NULL
;
908 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash_uc
.Flink
, dir_child
, list_entry_hash_uc
);
910 if (dc2
->hash_uc
>> 24 == c
)
911 fcb
->hash_ptrs_uc
[c
] = &dc2
->list_entry_hash_uc
;
913 fcb
->hash_ptrs_uc
[c
] = NULL
;
917 RemoveEntryList(&dc
->list_entry_hash_uc
);
920 static NTSTATUS
move_across_subvols(file_ref
* fileref
, file_ref
* destdir
, PANSI_STRING utf8
, PUNICODE_STRING fnus
, PIRP Irp
, LIST_ENTRY
* rollback
) {
922 LIST_ENTRY move_list
, *le
;
926 file_ref
* origparent
;
928 InitializeListHead(&move_list
);
930 me
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
933 ERR("out of memory\n");
934 Status
= STATUS_INSUFFICIENT_RESOURCES
;
938 origparent
= fileref
->parent
;
940 me
->fileref
= fileref
;
941 increase_fileref_refcount(me
->fileref
);
943 me
->dummyfileref
= NULL
;
946 InsertTailList(&move_list
, &me
->list_entry
);
948 le
= move_list
.Flink
;
949 while (le
!= &move_list
) {
950 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
952 ExAcquireResourceSharedLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
954 if (!me
->fileref
->fcb
->ads
&& me
->fileref
->fcb
->subvol
== origparent
->fcb
->subvol
) {
955 Status
= add_children_to_move_list(me
, Irp
);
957 if (!NT_SUCCESS(Status
)) {
958 ERR("add_children_to_move_list returned %08x\n", Status
);
963 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
968 // loop through list and create new inodes
970 le
= move_list
.Flink
;
971 while (le
!= &move_list
) {
972 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
974 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
) {
977 BOOL inserted
= FALSE
;
980 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
982 Status
= duplicate_fcb(me
->fileref
->fcb
, &me
->dummyfcb
);
983 if (!NT_SUCCESS(Status
)) {
984 ERR("duplicate_fcb returned %08x\n", Status
);
985 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
989 me
->dummyfcb
->subvol
= me
->fileref
->fcb
->subvol
;
990 me
->dummyfcb
->inode
= me
->fileref
->fcb
->inode
;
992 if (!me
->dummyfcb
->ads
) {
993 me
->dummyfcb
->sd_dirty
= me
->fileref
->fcb
->sd_dirty
;
994 me
->dummyfcb
->atts_changed
= me
->fileref
->fcb
->atts_changed
;
995 me
->dummyfcb
->atts_deleted
= me
->fileref
->fcb
->atts_deleted
;
996 me
->dummyfcb
->extents_changed
= me
->fileref
->fcb
->extents_changed
;
997 me
->dummyfcb
->reparse_xattr_changed
= me
->fileref
->fcb
->reparse_xattr_changed
;
998 me
->dummyfcb
->ea_changed
= me
->fileref
->fcb
->ea_changed
;
1001 me
->dummyfcb
->created
= me
->fileref
->fcb
->created
;
1002 me
->dummyfcb
->deleted
= me
->fileref
->fcb
->deleted
;
1003 mark_fcb_dirty(me
->dummyfcb
);
1005 if (!me
->fileref
->fcb
->ads
) {
1008 me
->fileref
->fcb
->subvol
= destdir
->fcb
->subvol
;
1009 me
->fileref
->fcb
->inode
= InterlockedIncrement64(&destdir
->fcb
->subvol
->lastinode
);
1010 me
->fileref
->fcb
->inode_item
.st_nlink
= 1;
1012 defda
= get_file_attributes(me
->fileref
->fcb
->Vcb
, &me
->fileref
->fcb
->inode_item
, me
->fileref
->fcb
->subvol
, me
->fileref
->fcb
->inode
,
1013 me
->fileref
->fcb
->type
, me
->fileref
->filepart
.Length
> 0 && me
->fileref
->filepart
.Buffer
[0] == '.', TRUE
, Irp
);
1015 me
->fileref
->fcb
->sd_dirty
= !!me
->fileref
->fcb
->sd
;
1016 me
->fileref
->fcb
->atts_changed
= defda
!= me
->fileref
->fcb
->atts
;
1017 me
->fileref
->fcb
->extents_changed
= !IsListEmpty(&me
->fileref
->fcb
->extents
);
1018 me
->fileref
->fcb
->reparse_xattr_changed
= !!me
->fileref
->fcb
->reparse_xattr
.Buffer
;
1019 me
->fileref
->fcb
->ea_changed
= !!me
->fileref
->fcb
->ea_xattr
.Buffer
;
1020 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
1022 le2
= me
->fileref
->fcb
->extents
.Flink
;
1023 while (le2
!= &me
->fileref
->fcb
->extents
) {
1024 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
1026 if (!ext
->ignore
&& ext
->datalen
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
) &&
1027 (ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
)) {
1028 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
1030 if (ed2
->size
!= 0) {
1031 chunk
* c
= get_chunk_from_address(me
->fileref
->fcb
->Vcb
, ed2
->address
);
1034 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
1036 Status
= update_changed_extent_ref(me
->fileref
->fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, me
->fileref
->fcb
->subvol
->id
, me
->fileref
->fcb
->inode
,
1037 ext
->offset
- ed2
->offset
, 1, me
->fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
1039 if (!NT_SUCCESS(Status
)) {
1040 ERR("update_changed_extent_ref returned %08x\n", Status
);
1041 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
1052 me
->fileref
->fcb
->subvol
= me
->parent
->fileref
->fcb
->subvol
;
1053 me
->fileref
->fcb
->inode
= me
->parent
->fileref
->fcb
->inode
;
1056 me
->fileref
->fcb
->created
= TRUE
;
1058 InsertHeadList(&me
->fileref
->fcb
->list_entry
, &me
->dummyfcb
->list_entry
);
1059 RemoveEntryList(&me
->fileref
->fcb
->list_entry
);
1061 le
= destdir
->fcb
->subvol
->fcbs
.Flink
;
1062 while (le
!= &destdir
->fcb
->subvol
->fcbs
) {
1063 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
1065 if (fcb
->inode
> me
->fileref
->fcb
->inode
) {
1066 InsertHeadList(le
->Blink
, &me
->fileref
->fcb
->list_entry
);
1075 InsertTailList(&destdir
->fcb
->subvol
->fcbs
, &me
->fileref
->fcb
->list_entry
);
1077 InsertTailList(&me
->fileref
->fcb
->Vcb
->all_fcbs
, &me
->dummyfcb
->list_entry_all
);
1079 while (!IsListEmpty(&me
->fileref
->fcb
->hardlinks
)) {
1080 LIST_ENTRY
* le
= RemoveHeadList(&me
->fileref
->fcb
->hardlinks
);
1081 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1083 if (hl
->name
.Buffer
)
1084 ExFreePool(hl
->name
.Buffer
);
1086 if (hl
->utf8
.Buffer
)
1087 ExFreePool(hl
->utf8
.Buffer
);
1092 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
1093 mark_fcb_dirty(me
->fileref
->fcb
);
1095 if ((!me
->dummyfcb
->ads
&& me
->dummyfcb
->inode_item
.st_nlink
> 1) || (me
->dummyfcb
->ads
&& me
->parent
->dummyfcb
->inode_item
.st_nlink
> 1)) {
1096 LIST_ENTRY
* le2
= le
->Flink
;
1098 while (le2
!= &move_list
) {
1099 move_entry
* me2
= CONTAINING_RECORD(le2
, move_entry
, list_entry
);
1101 if (me2
->fileref
->fcb
== me
->fileref
->fcb
&& !me2
->fileref
->fcb
->ads
) {
1102 me2
->dummyfcb
= me
->dummyfcb
;
1103 InterlockedIncrement(&me
->dummyfcb
->refcount
);
1110 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
1112 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
1113 me
->fileref
->fcb
->inode_item
.st_nlink
++;
1114 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
1115 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
1122 KeQuerySystemTime(&time
);
1123 win_time_to_unix(time
, &now
);
1125 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
1126 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
1128 // loop through list and create new filerefs
1130 le
= move_list
.Flink
;
1131 while (le
!= &move_list
) {
1133 BOOL name_changed
= FALSE
;
1135 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1137 me
->dummyfileref
= create_fileref();
1138 if (!me
->dummyfileref
) {
1139 ERR("out of memory\n");
1140 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1144 if (me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
)
1145 me
->dummyfileref
->fcb
= me
->fileref
->fcb
;
1147 me
->dummyfileref
->fcb
= me
->dummyfcb
;
1149 InterlockedIncrement(&me
->dummyfileref
->fcb
->refcount
);
1151 me
->dummyfileref
->filepart
= me
->fileref
->filepart
;
1153 if (le
== move_list
.Flink
) // first item
1154 me
->fileref
->filepart
.Length
= me
->fileref
->filepart
.MaximumLength
= fnus
->Length
;
1156 me
->fileref
->filepart
.MaximumLength
= me
->fileref
->filepart
.Length
;
1158 me
->fileref
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->filepart
.MaximumLength
, ALLOC_TAG
);
1160 if (!me
->fileref
->filepart
.Buffer
) {
1161 ERR("out of memory\n");
1162 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1166 RtlCopyMemory(me
->fileref
->filepart
.Buffer
, le
== move_list
.Flink
? fnus
->Buffer
: me
->dummyfileref
->filepart
.Buffer
, me
->fileref
->filepart
.Length
);
1168 Status
= RtlUpcaseUnicodeString(&me
->fileref
->filepart_uc
, &me
->fileref
->filepart
, TRUE
);
1169 if (!NT_SUCCESS(Status
)) {
1170 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1174 me
->dummyfileref
->utf8
= me
->fileref
->utf8
;
1175 me
->dummyfileref
->oldutf8
= me
->fileref
->oldutf8
;
1177 if (le
== move_list
.Flink
) {
1178 if (me
->fileref
->utf8
.Length
!= utf8
->Length
|| RtlCompareMemory(me
->fileref
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
) != utf8
->Length
)
1179 name_changed
= TRUE
;
1181 me
->fileref
->utf8
.Length
= me
->fileref
->utf8
.MaximumLength
= utf8
->Length
;
1183 me
->fileref
->utf8
.MaximumLength
= me
->fileref
->utf8
.Length
;
1185 if (me
->fileref
->utf8
.MaximumLength
> 0) {
1186 me
->fileref
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->utf8
.MaximumLength
, ALLOC_TAG
);
1188 if (!me
->fileref
->utf8
.Buffer
) {
1189 ERR("out of memory\n");
1190 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1194 RtlCopyMemory(me
->fileref
->utf8
.Buffer
, le
== move_list
.Flink
? utf8
->Buffer
: me
->dummyfileref
->utf8
.Buffer
, me
->fileref
->utf8
.Length
);
1197 me
->dummyfileref
->delete_on_close
= me
->fileref
->delete_on_close
;
1198 me
->dummyfileref
->deleted
= me
->fileref
->deleted
;
1200 me
->dummyfileref
->created
= me
->fileref
->created
;
1201 me
->fileref
->created
= TRUE
;
1203 me
->dummyfileref
->parent
= me
->parent
? me
->parent
->dummyfileref
: origparent
;
1204 increase_fileref_refcount(me
->dummyfileref
->parent
);
1206 me
->dummyfileref
->index
= me
->fileref
->index
;
1208 insert_fileref_child(me
->dummyfileref
->parent
, me
->dummyfileref
, TRUE
);
1210 me
->dummyfileref
->debug_desc
= me
->fileref
->debug_desc
;
1212 if (me
->dummyfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1213 me
->dummyfileref
->fcb
->fileref
= me
->dummyfileref
;
1216 RemoveEntryList(&me
->fileref
->list_entry
);
1218 free_fileref(me
->fileref
->parent
);
1220 increase_fileref_refcount(destdir
);
1222 Status
= fcb_get_last_dir_index(destdir
->fcb
, &me
->fileref
->index
, Irp
);
1223 if (!NT_SUCCESS(Status
)) {
1224 ERR("fcb_get_last_dir_index returned %08x\n", Status
);
1228 if (me
->fileref
->dc
) {
1229 // remove from old parent
1230 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1231 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1232 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1233 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1236 ExFreePool(me
->fileref
->dc
->utf8
.Buffer
);
1237 ExFreePool(me
->fileref
->dc
->name
.Buffer
);
1238 ExFreePool(me
->fileref
->dc
->name_uc
.Buffer
);
1240 me
->fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1241 if (!me
->fileref
->dc
->utf8
.Buffer
) {
1242 ERR("out of memory\n");
1243 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1247 me
->fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->filepart
.Length
, ALLOC_TAG
);
1248 if (!me
->fileref
->dc
->name
.Buffer
) {
1249 ERR("out of memory\n");
1250 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1254 me
->fileref
->dc
->name_uc
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->filepart_uc
.Length
, ALLOC_TAG
);
1255 if (!me
->fileref
->dc
->name_uc
.Buffer
) {
1256 ERR("out of memory\n");
1257 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1261 me
->fileref
->dc
->utf8
.Length
= me
->fileref
->dc
->utf8
.MaximumLength
= utf8
->Length
;
1262 RtlCopyMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1264 me
->fileref
->dc
->name
.Length
= me
->fileref
->dc
->name
.MaximumLength
= me
->fileref
->filepart
.Length
;
1265 RtlCopyMemory(me
->fileref
->dc
->name
.Buffer
, me
->fileref
->filepart
.Buffer
, me
->fileref
->filepart
.Length
);
1267 me
->fileref
->dc
->name_uc
.Length
= me
->fileref
->dc
->name_uc
.MaximumLength
= me
->fileref
->filepart_uc
.Length
;
1268 RtlCopyMemory(me
->fileref
->dc
->name_uc
.Buffer
, me
->fileref
->filepart_uc
.Buffer
, me
->fileref
->filepart_uc
.Length
);
1270 me
->fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1271 me
->fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name_uc
.Buffer
, me
->fileref
->dc
->name_uc
.Length
);
1274 // add to new parent
1275 ExAcquireResourceExclusiveLite(&destdir
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1276 InsertTailList(&destdir
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1277 insert_dir_child_into_hash_lists(destdir
->fcb
, me
->fileref
->dc
);
1278 ExReleaseResourceLite(&destdir
->fcb
->nonpaged
->dir_children_lock
);
1281 me
->fileref
->parent
= destdir
;
1283 insert_fileref_child(me
->fileref
->parent
, me
->fileref
, TRUE
);
1285 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me
->fileref
->parent
->fcb
->inode
, me
->fileref
->parent
->fcb
->inode_item
.st_size
);
1286 me
->fileref
->parent
->fcb
->inode_item
.st_size
+= me
->fileref
->utf8
.Length
* 2;
1287 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me
->fileref
->parent
->fcb
->inode
, me
->fileref
->parent
->fcb
->inode_item
.st_size
);
1288 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1289 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1290 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1291 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1292 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1293 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1296 if (me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
)
1297 me
->fileref
->fcb
->subvol
->root_item
.num_references
++;
1299 if (!me
->dummyfileref
->fcb
->ads
) {
1300 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1301 if (!NT_SUCCESS(Status
)) {
1302 ERR("delete_fileref returned %08x\n", Status
);
1307 if (me
->fileref
->fcb
->inode_item
.st_nlink
> 1) {
1308 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1310 ERR("out of memory\n");
1311 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1315 hl
->parent
= me
->fileref
->parent
->fcb
->inode
;
1316 hl
->index
= me
->fileref
->index
;
1318 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= me
->fileref
->utf8
.Length
;
1319 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1320 if (!hl
->utf8
.Buffer
) {
1321 ERR("out of memory\n");
1322 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1327 RtlCopyMemory(hl
->utf8
.Buffer
, me
->fileref
->utf8
.Buffer
, me
->fileref
->utf8
.Length
);
1329 hl
->name
.Length
= hl
->name
.MaximumLength
= me
->fileref
->filepart
.Length
;
1330 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1331 if (!hl
->name
.Buffer
) {
1332 ERR("out of memory\n");
1333 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1334 ExFreePool(hl
->utf8
.Buffer
);
1339 RtlCopyMemory(hl
->name
.Buffer
, me
->fileref
->filepart
.Buffer
, me
->fileref
->filepart
.Length
);
1341 InsertTailList(&me
->fileref
->fcb
->hardlinks
, &hl
->list_entry
);
1344 mark_fileref_dirty(me
->fileref
);
1349 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1351 le
= move_list
.Flink
;
1352 while (le
!= &move_list
) {
1353 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1355 if (me
->dummyfileref
->fcb
->ads
&& me
->parent
->dummyfileref
->fcb
->deleted
) {
1356 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1357 if (!NT_SUCCESS(Status
)) {
1358 ERR("delete_fileref returned %08x\n", Status
);
1366 destdir
->fcb
->subvol
->root_item
.ctransid
= destdir
->fcb
->Vcb
->superblock
.generation
;
1367 destdir
->fcb
->subvol
->root_item
.ctime
= now
;
1369 me
= CONTAINING_RECORD(move_list
.Flink
, move_entry
, list_entry
);
1370 send_notification_fileref(me
->dummyfileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
);
1371 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
);
1372 send_notification_fileref(me
->dummyfileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
1373 send_notification_fileref(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
1375 Status
= STATUS_SUCCESS
;
1378 while (!IsListEmpty(&move_list
)) {
1379 le
= RemoveHeadList(&move_list
);
1380 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1383 free_fcb(me
->dummyfcb
);
1385 if (me
->dummyfileref
)
1386 free_fileref(me
->dummyfileref
);
1388 free_fileref(me
->fileref
);
1396 void insert_dir_child_into_hash_lists(fcb
* fcb
, dir_child
* dc
) {
1407 le
= fcb
->hash_ptrs
[d
];
1416 le
= fcb
->dir_children_hash
.Flink
;
1418 while (le
!= &fcb
->dir_children_hash
) {
1419 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
1421 if (dc2
->hash
> dc
->hash
) {
1422 InsertHeadList(le
->Blink
, &dc
->list_entry_hash
);
1431 InsertTailList(&fcb
->dir_children_hash
, &dc
->list_entry_hash
);
1433 if (!fcb
->hash_ptrs
[c
])
1434 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1436 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs
[c
], dir_child
, list_entry_hash
);
1438 if (dc2
->hash
> dc
->hash
)
1439 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1442 c
= dc
->hash_uc
>> 24;
1448 le
= fcb
->hash_ptrs_uc
[d
];
1457 le
= fcb
->dir_children_hash_uc
.Flink
;
1459 while (le
!= &fcb
->dir_children_hash_uc
) {
1460 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
1462 if (dc2
->hash_uc
> dc
->hash_uc
) {
1463 InsertHeadList(le
->Blink
, &dc
->list_entry_hash_uc
);
1472 InsertTailList(&fcb
->dir_children_hash_uc
, &dc
->list_entry_hash_uc
);
1474 if (!fcb
->hash_ptrs_uc
[c
])
1475 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1477 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs_uc
[c
], dir_child
, list_entry_hash_uc
);
1479 if (dc2
->hash_uc
> dc
->hash_uc
)
1480 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1484 static NTSTATUS STDCALL
set_rename_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
1485 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1486 FILE_RENAME_INFORMATION
* fri
= Irp
->AssociatedIrp
.SystemBuffer
;
1487 fcb
*fcb
= FileObject
->FsContext
;
1488 ccb
* ccb
= FileObject
->FsContext2
;
1489 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
1492 ULONG fnlen
, utf8len
;
1493 UNICODE_STRING fnus
;
1498 LIST_ENTRY rollback
, *le
;
1501 InitializeListHead(&rollback
);
1503 TRACE("tfo = %p\n", tfo
);
1504 TRACE("ReplaceIfExists = %u\n", IrpSp
->Parameters
.SetFile
.ReplaceIfExists
);
1505 TRACE("RootDirectory = %p\n", fri
->RootDirectory
);
1506 TRACE("FileName = %.*S\n", fri
->FileNameLength
/ sizeof(WCHAR
), fri
->FileName
);
1509 fnlen
= fri
->FileNameLength
/ sizeof(WCHAR
);
1512 if (!fileref
|| !fileref
->parent
) {
1513 ERR("no fileref set and no directory given\n");
1514 return STATUS_INVALID_PARAMETER
;
1519 while (fnlen
> 0 && (fri
->FileName
[fnlen
- 1] == '/' || fri
->FileName
[fnlen
- 1] == '\\'))
1523 return STATUS_INVALID_PARAMETER
;
1525 for (i
= fnlen
- 1; i
>= 0; i
--) {
1526 if (fri
->FileName
[i
] == '\\' || fri
->FileName
[i
] == '/') {
1527 fn
= &fri
->FileName
[i
+1];
1528 fnlen
= (fri
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
1534 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1535 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1536 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1539 FIXME("FIXME - renaming streams\n"); // FIXME
1540 Status
= STATUS_NOT_IMPLEMENTED
;
1545 fnus
.Length
= fnus
.MaximumLength
= fnlen
* sizeof(WCHAR
);
1547 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1549 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1550 if (!NT_SUCCESS(Status
))
1553 utf8
.MaximumLength
= utf8
.Length
= utf8len
;
1554 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
1556 ERR("out of memory\n");
1557 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1561 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1562 if (!NT_SUCCESS(Status
))
1565 if (tfo
&& tfo
->FsContext2
) {
1566 struct _ccb
* relatedccb
= tfo
->FsContext2
;
1568 related
= relatedccb
->fileref
;
1569 increase_fileref_refcount(related
);
1570 } else if (fnus
.Length
>= sizeof(WCHAR
) && fnus
.Buffer
[0] != '\\') {
1571 related
= fileref
->parent
;
1572 increase_fileref_refcount(related
);
1575 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1577 if (NT_SUCCESS(Status
)) {
1578 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref
));
1580 if (fileref
!= oldfileref
&& !oldfileref
->deleted
) {
1581 if (!IrpSp
->Parameters
.SetFile
.ReplaceIfExists
) {
1582 Status
= STATUS_OBJECT_NAME_COLLISION
;
1584 } else if ((oldfileref
->open_count
>= 1 || has_open_children(oldfileref
)) && !oldfileref
->deleted
) {
1585 WARN("trying to overwrite open file\n");
1586 Status
= STATUS_ACCESS_DENIED
;
1590 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1591 WARN("trying to overwrite directory\n");
1592 Status
= STATUS_ACCESS_DENIED
;
1597 if (fileref
== oldfileref
|| oldfileref
->deleted
) {
1598 free_fileref(oldfileref
);
1604 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1606 if (!NT_SUCCESS(Status
)) {
1607 ERR("open_fileref returned %08x\n", Status
);
1612 if (has_open_children(fileref
)) {
1613 WARN("trying to rename file with open children\n");
1614 Status
= STATUS_ACCESS_DENIED
;
1620 SECURITY_SUBJECT_CONTEXT subjcont
;
1622 SeCaptureSubjectContext(&subjcont
);
1624 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
1625 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1626 SeReleaseSubjectContext(&subjcont
);
1627 WARN("SeAccessCheck failed, returning %08x\n", Status
);
1631 SeReleaseSubjectContext(&subjcont
);
1633 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
1634 if (!NT_SUCCESS(Status
)) {
1635 ERR("delete_fileref returned %08x\n", Status
);
1640 if (fileref
->parent
->fcb
->subvol
!= related
->fcb
->subvol
&& fileref
->fcb
->subvol
== fileref
->parent
->fcb
->subvol
) {
1641 Status
= move_across_subvols(fileref
, related
, &utf8
, &fnus
, Irp
, &rollback
);
1642 if (!NT_SUCCESS(Status
)) {
1643 ERR("move_across_subvols returned %08x\n", Status
);
1648 if (related
== fileref
->parent
) { // keeping file in same directory
1649 UNICODE_STRING fnus2
, oldfn
, newfn
;
1653 fnus2
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1654 if (!fnus2
.Buffer
) {
1655 ERR("out of memory\n");
1656 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1660 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
);
1661 if (!NT_SUCCESS(Status
)) {
1662 ERR("fileref_get_filename returned %08x\n", Status
);
1666 fnus2
.Length
= fnus2
.MaximumLength
= fnus
.Length
;
1667 RtlCopyMemory(fnus2
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1669 oldutf8len
= fileref
->utf8
.Length
;
1671 if (!fileref
->created
&& !fileref
->oldutf8
.Buffer
)
1672 fileref
->oldutf8
= fileref
->utf8
;
1674 ExFreePool(fileref
->utf8
.Buffer
);
1676 TRACE("renaming %.*S to %.*S\n", fileref
->filepart
.Length
/ sizeof(WCHAR
), fileref
->filepart
.Buffer
, fnus2
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1678 fileref
->utf8
= utf8
;
1679 fileref
->filepart
= fnus2
;
1681 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
);
1682 if (!NT_SUCCESS(Status
)) {
1683 ERR("fileref_get_filename returned %08x\n", Status
);
1684 ExFreePool(oldfn
.Buffer
);
1688 if (fileref
->filepart_uc
.Buffer
)
1689 ExFreePool(fileref
->filepart_uc
.Buffer
);
1691 Status
= RtlUpcaseUnicodeString(&fileref
->filepart_uc
, &fileref
->filepart
, TRUE
);
1692 if (!NT_SUCCESS(Status
)) {
1693 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1694 ExFreePool(oldfn
.Buffer
);
1695 ExFreePool(newfn
.Buffer
);
1699 mark_fileref_dirty(fileref
);
1702 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1704 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1705 ExFreePool(fileref
->dc
->name
.Buffer
);
1706 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1708 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1709 if (!fileref
->dc
->utf8
.Buffer
) {
1710 ERR("out of memory\n");
1711 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1712 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1716 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->filepart
.Length
, ALLOC_TAG
);
1717 if (!fileref
->dc
->name
.Buffer
) {
1718 ERR("out of memory\n");
1719 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1720 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1724 fileref
->dc
->name_uc
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->filepart_uc
.Length
, ALLOC_TAG
);
1725 if (!fileref
->dc
->name_uc
.Buffer
) {
1726 ERR("out of memory\n");
1727 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1728 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1732 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1733 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1735 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fileref
->filepart
.Length
;
1736 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
1738 fileref
->dc
->name_uc
.Length
= fileref
->dc
->name_uc
.MaximumLength
= fileref
->filepart_uc
.Length
;
1739 RtlCopyMemory(fileref
->dc
->name_uc
.Buffer
, fileref
->filepart_uc
.Buffer
, fileref
->filepart_uc
.Length
);
1741 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1743 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1744 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1746 insert_dir_child_into_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1748 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1751 KeQuerySystemTime(&time
);
1752 win_time_to_unix(time
, &now
);
1754 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1755 fcb
->inode_item
.sequence
++;
1757 if (!ccb
->user_set_change_time
)
1758 fcb
->inode_item
.st_ctime
= now
;
1760 fcb
->inode_item_changed
= TRUE
;
1761 mark_fcb_dirty(fcb
);
1763 // update parent's INODE_ITEM
1765 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1766 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1767 related
->fcb
->inode_item
.st_size
= related
->fcb
->inode_item
.st_size
+ (2 * utf8
.Length
) - (2* oldutf8len
);
1768 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1769 related
->fcb
->inode_item
.sequence
++;
1770 related
->fcb
->inode_item
.st_ctime
= now
;
1771 related
->fcb
->inode_item
.st_mtime
= now
;
1773 related
->fcb
->inode_item_changed
= TRUE
;
1774 mark_fcb_dirty(related
->fcb
);
1775 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
1777 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&oldfn
, name_offset
, NULL
, NULL
,
1778 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_OLD_NAME
, NULL
, NULL
);
1779 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&newfn
, name_offset
, NULL
, NULL
,
1780 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_NEW_NAME
, NULL
, NULL
);
1782 ExFreePool(oldfn
.Buffer
);
1783 ExFreePool(newfn
.Buffer
);
1785 Status
= STATUS_SUCCESS
;
1789 // We move files by moving the existing fileref to the new directory, and
1790 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1792 fr2
= create_fileref();
1794 fr2
->fcb
= fileref
->fcb
;
1795 fr2
->fcb
->refcount
++;
1797 fr2
->filepart
= fileref
->filepart
;
1798 fr2
->filepart_uc
= fileref
->filepart_uc
;
1799 fr2
->utf8
= fileref
->utf8
;
1800 fr2
->oldutf8
= fileref
->oldutf8
;
1801 fr2
->index
= fileref
->index
;
1802 fr2
->delete_on_close
= fileref
->delete_on_close
;
1803 fr2
->deleted
= TRUE
;
1804 fr2
->created
= fileref
->created
;
1805 fr2
->parent
= fileref
->parent
;
1808 if (fr2
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1809 fr2
->fcb
->fileref
= fr2
;
1811 Status
= fcb_get_last_dir_index(related
->fcb
, &index
, Irp
);
1812 if (!NT_SUCCESS(Status
)) {
1813 ERR("fcb_get_last_dir_index returned %08x\n", Status
);
1817 fileref
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1818 if (!fileref
->filepart
.Buffer
) {
1819 ERR("out of memory\n");
1820 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1824 fileref
->filepart
.Length
= fileref
->filepart
.MaximumLength
= fnus
.Length
;
1825 RtlCopyMemory(fileref
->filepart
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1827 Status
= RtlUpcaseUnicodeString(&fileref
->filepart_uc
, &fileref
->filepart
, TRUE
);
1828 if (!NT_SUCCESS(Status
)) {
1829 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1833 fileref
->utf8
= utf8
;
1834 fileref
->oldutf8
.Buffer
= NULL
;
1835 fileref
->index
= index
;
1836 fileref
->deleted
= FALSE
;
1837 fileref
->created
= TRUE
;
1838 fileref
->parent
= related
;
1840 ExAcquireResourceExclusiveLite(&fileref
->parent
->nonpaged
->children_lock
, TRUE
);
1841 InsertHeadList(&fileref
->list_entry
, &fr2
->list_entry
);
1842 RemoveEntryList(&fileref
->list_entry
);
1843 ExReleaseResourceLite(&fileref
->parent
->nonpaged
->children_lock
);
1845 insert_fileref_child(related
, fileref
, TRUE
);
1847 mark_fileref_dirty(fr2
);
1848 mark_fileref_dirty(fileref
);
1851 // remove from old parent
1852 ExAcquireResourceExclusiveLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1853 RemoveEntryList(&fileref
->dc
->list_entry_index
);
1854 remove_dir_child_from_hash_lists(fr2
->parent
->fcb
, fileref
->dc
);
1855 ExReleaseResourceLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
);
1857 if (fileref
->utf8
.Length
!= fr2
->utf8
.Length
|| RtlCompareMemory(fileref
->utf8
.Buffer
, fr2
->utf8
.Buffer
, fr2
->utf8
.Length
) != fr2
->utf8
.Length
) {
1858 // handle changed name
1860 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1861 ExFreePool(fileref
->dc
->name
.Buffer
);
1862 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1864 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1865 if (!fileref
->dc
->utf8
.Buffer
) {
1866 ERR("out of memory\n");
1867 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1871 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->filepart
.Length
, ALLOC_TAG
);
1872 if (!fileref
->dc
->name
.Buffer
) {
1873 ERR("out of memory\n");
1874 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1878 fileref
->dc
->name_uc
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->filepart_uc
.Length
, ALLOC_TAG
);
1879 if (!fileref
->dc
->name_uc
.Buffer
) {
1880 ERR("out of memory\n");
1881 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1885 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1886 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1888 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fileref
->filepart
.Length
;
1889 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
1891 fileref
->dc
->name_uc
.Length
= fileref
->dc
->name_uc
.MaximumLength
= fileref
->filepart_uc
.Length
;
1892 RtlCopyMemory(fileref
->dc
->name_uc
.Buffer
, fileref
->filepart_uc
.Buffer
, fileref
->filepart_uc
.Length
);
1894 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1895 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1898 // add to new parent
1899 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1900 InsertTailList(&related
->fcb
->dir_children_index
, &fileref
->dc
->list_entry_index
);
1901 insert_dir_child_into_hash_lists(related
->fcb
, fileref
->dc
);
1902 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
1905 if (fcb
->inode_item
.st_nlink
> 1) {
1906 // add new hardlink entry to fcb
1908 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1910 ERR("out of memory\n");
1911 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1915 hl
->parent
= related
->fcb
->inode
;
1918 hl
->name
.Length
= hl
->name
.MaximumLength
= fileref
->filepart
.Length
;
1919 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1921 if (!hl
->name
.Buffer
) {
1922 ERR("out of memory\n");
1924 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1928 RtlCopyMemory(hl
->name
.Buffer
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
1930 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->utf8
.Length
;
1931 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1933 if (!hl
->utf8
.Buffer
) {
1934 ERR("out of memory\n");
1935 ExFreePool(hl
->name
.Buffer
);
1937 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1941 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
1943 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
1946 // delete old hardlink entry from fcb
1948 le
= fcb
->hardlinks
.Flink
;
1949 while (le
!= &fcb
->hardlinks
) {
1950 hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1952 if (hl
->parent
== fr2
->parent
->fcb
->inode
&& hl
->index
== fr2
->index
) {
1953 RemoveEntryList(&hl
->list_entry
);
1955 if (hl
->utf8
.Buffer
)
1956 ExFreePool(hl
->utf8
.Buffer
);
1958 if (hl
->name
.Buffer
)
1959 ExFreePool(hl
->name
.Buffer
);
1968 // update inode's INODE_ITEM
1970 KeQuerySystemTime(&time
);
1971 win_time_to_unix(time
, &now
);
1973 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1974 fcb
->inode_item
.sequence
++;
1976 if (!ccb
->user_set_change_time
)
1977 fcb
->inode_item
.st_ctime
= now
;
1979 fcb
->inode_item_changed
= TRUE
;
1980 mark_fcb_dirty(fcb
);
1982 // update new parent's INODE_ITEM
1984 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1985 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1986 related
->fcb
->inode_item
.st_size
+= 2 * utf8len
;
1987 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1988 related
->fcb
->inode_item
.sequence
++;
1989 related
->fcb
->inode_item
.st_ctime
= now
;
1990 related
->fcb
->inode_item
.st_mtime
= now
;
1992 related
->fcb
->inode_item_changed
= TRUE
;
1993 mark_fcb_dirty(related
->fcb
);
1995 // update old parent's INODE_ITEM
1997 fr2
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1998 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1999 fr2
->parent
->fcb
->inode_item
.st_size
-= 2 * fr2
->utf8
.Length
;
2000 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
2001 fr2
->parent
->fcb
->inode_item
.sequence
++;
2002 fr2
->parent
->fcb
->inode_item
.st_ctime
= now
;
2003 fr2
->parent
->fcb
->inode_item
.st_mtime
= now
;
2007 fr2
->parent
->fcb
->inode_item_changed
= TRUE
;
2008 mark_fcb_dirty(fr2
->parent
->fcb
);
2010 send_notification_fileref(fr2
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
);
2011 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
);
2012 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
2013 send_notification_fileref(fr2
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
2015 Status
= STATUS_SUCCESS
;
2019 free_fileref(oldfileref
);
2021 if (!NT_SUCCESS(Status
) && related
)
2022 free_fileref(related
);
2024 if (!NT_SUCCESS(Status
) && fr2
)
2027 if (NT_SUCCESS(Status
))
2028 clear_rollback(Vcb
, &rollback
);
2030 do_rollback(Vcb
, &rollback
);
2032 ExReleaseResourceLite(fcb
->Header
.Resource
);
2033 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2034 ExReleaseResourceLite(&Vcb
->tree_lock
);
2039 NTSTATUS STDCALL
stream_set_end_of_file_information(device_extension
* Vcb
, UINT64 end
, fcb
* fcb
, file_ref
* fileref
, PFILE_OBJECT FileObject
, BOOL advance_only
, LIST_ENTRY
* rollback
) {
2044 TRACE("setting new end to %llx bytes (currently %x)\n", end
, fcb
->adsdata
.Length
);
2046 if (!fileref
|| !fileref
->parent
) {
2047 ERR("no fileref for stream\n");
2048 return STATUS_INTERNAL_ERROR
;
2051 if (end
< fcb
->adsdata
.Length
) {
2053 return STATUS_SUCCESS
;
2055 TRACE("truncating stream to %llx bytes\n", end
);
2057 fcb
->adsdata
.Length
= end
;
2058 } else if (end
> fcb
->adsdata
.Length
) {
2059 TRACE("extending stream to %llx bytes\n", end
);
2061 if (end
> fcb
->adsmaxlen
) {
2062 ERR("error - xattr too long (%llu > %u)\n", end
, fcb
->adsmaxlen
);
2063 return STATUS_DISK_FULL
;
2066 if (end
> fcb
->adsdata
.MaximumLength
) {
2067 char* data
= ExAllocatePoolWithTag(PagedPool
, end
, ALLOC_TAG
);
2069 ERR("out of memory\n");
2071 return STATUS_INSUFFICIENT_RESOURCES
;
2074 if (fcb
->adsdata
.Buffer
) {
2075 RtlCopyMemory(data
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
2076 ExFreePool(fcb
->adsdata
.Buffer
);
2079 fcb
->adsdata
.Buffer
= data
;
2080 fcb
->adsdata
.MaximumLength
= end
;
2083 RtlZeroMemory(&fcb
->adsdata
.Buffer
[fcb
->adsdata
.Length
], end
- fcb
->adsdata
.Length
);
2085 fcb
->adsdata
.Length
= end
;
2088 mark_fcb_dirty(fcb
);
2090 fcb
->Header
.AllocationSize
.QuadPart
= end
;
2091 fcb
->Header
.FileSize
.QuadPart
= end
;
2092 fcb
->Header
.ValidDataLength
.QuadPart
= end
;
2095 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2096 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2097 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2099 CcSetFileSizes(FileObject
, &ccfs
);
2102 KeQuerySystemTime(&time
);
2103 win_time_to_unix(time
, &now
);
2105 fileref
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2106 fileref
->parent
->fcb
->inode_item
.sequence
++;
2107 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2109 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2110 mark_fcb_dirty(fileref
->parent
->fcb
);
2112 fileref
->parent
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2113 fileref
->parent
->fcb
->subvol
->root_item
.ctime
= now
;
2115 return STATUS_SUCCESS
;
2118 static NTSTATUS STDCALL
set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
) {
2119 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
2120 fcb
* fcb
= FileObject
->FsContext
;
2121 ccb
* ccb
= FileObject
->FsContext2
;
2122 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2126 LIST_ENTRY rollback
;
2127 BOOL set_size
= FALSE
;
2130 ERR("fileref is NULL\n");
2131 return STATUS_INVALID_PARAMETER
;
2134 InitializeListHead(&rollback
);
2136 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2138 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2140 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2141 Status
= STATUS_FILE_CLOSED
;
2146 Status
= stream_set_end_of_file_information(Vcb
, feofi
->EndOfFile
.QuadPart
, fcb
, fileref
, FileObject
, advance_only
, &rollback
);
2150 TRACE("file: %S\n", file_desc(FileObject
));
2151 TRACE("paging IO: %s\n", Irp
->Flags
& IRP_PAGING_IO
? "TRUE" : "FALSE");
2152 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2153 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2156 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi
->EndOfFile
.QuadPart
, fcb
->inode_item
.st_size
);
2158 // if (feofi->EndOfFile.QuadPart==0x36c000)
2161 if (feofi
->EndOfFile
.QuadPart
< fcb
->inode_item
.st_size
) {
2163 Status
= STATUS_SUCCESS
;
2167 TRACE("truncating file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2169 if (!MmCanFileBeTruncated(&fcb
->nonpaged
->segment_object
, &feofi
->EndOfFile
)) {
2170 Status
= STATUS_USER_MAPPED_FILE
;
2174 Status
= truncate_file(fcb
, feofi
->EndOfFile
.QuadPart
, Irp
, &rollback
);
2175 if (!NT_SUCCESS(Status
)) {
2176 ERR("error - truncate_file failed\n");
2179 } else if (feofi
->EndOfFile
.QuadPart
> fcb
->inode_item
.st_size
) {
2180 if (Irp
->Flags
& IRP_PAGING_IO
) {
2181 TRACE("paging IO tried to extend file size\n");
2182 Status
= STATUS_SUCCESS
;
2186 TRACE("extending file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2188 Status
= extend_file(fcb
, fileref
, feofi
->EndOfFile
.QuadPart
, TRUE
, NULL
, &rollback
);
2189 if (!NT_SUCCESS(Status
)) {
2190 ERR("error - extend_file failed\n");
2195 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2196 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2197 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2200 if (!ccb
->user_set_write_time
) {
2201 KeQuerySystemTime(&time
);
2202 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2205 fcb
->inode_item_changed
= TRUE
;
2206 mark_fcb_dirty(fcb
);
2207 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_SIZE
, FILE_ACTION_MODIFIED
);
2209 Status
= STATUS_SUCCESS
;
2212 if (NT_SUCCESS(Status
))
2213 clear_rollback(Vcb
, &rollback
);
2215 do_rollback(Vcb
, &rollback
);
2217 ExReleaseResourceLite(fcb
->Header
.Resource
);
2220 CcSetFileSizes(FileObject
, &ccfs
);
2222 ExReleaseResourceLite(&Vcb
->tree_lock
);
2227 // static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2228 // FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2229 // fcb* fcb = FileObject->FsContext;
2231 // FIXME("FIXME\n");
2232 // ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
2233 // ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
2235 // return STATUS_NOT_IMPLEMENTED;
2238 static NTSTATUS STDCALL
set_position_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
2239 FILE_POSITION_INFORMATION
* fpi
= (FILE_POSITION_INFORMATION
*)Irp
->AssociatedIrp
.SystemBuffer
;
2241 TRACE("setting the position on %S to %llx\n", file_desc(FileObject
), fpi
->CurrentByteOffset
.QuadPart
);
2243 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2245 FileObject
->CurrentByteOffset
= fpi
->CurrentByteOffset
;
2247 return STATUS_SUCCESS
;
2250 static NTSTATUS STDCALL
set_link_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
2251 FILE_LINK_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
2252 fcb
*fcb
= FileObject
->FsContext
, *tfofcb
, *parfcb
;
2253 ccb
* ccb
= FileObject
->FsContext2
;
2254 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
2257 ULONG fnlen
, utf8len
;
2258 UNICODE_STRING fnus
;
2263 LIST_ENTRY rollback
;
2266 SECURITY_SUBJECT_CONTEXT subjcont
;
2267 dir_child
* dc
= NULL
;
2269 InitializeListHead(&rollback
);
2271 // FIXME - check fli length
2272 // FIXME - don't ignore fli->RootDirectory
2274 TRACE("ReplaceIfExists = %x\n", fli
->ReplaceIfExists
);
2275 TRACE("RootDirectory = %p\n", fli
->RootDirectory
);
2276 TRACE("FileNameLength = %x\n", fli
->FileNameLength
);
2277 TRACE("FileName = %.*S\n", fli
->FileNameLength
/ sizeof(WCHAR
), fli
->FileName
);
2280 fnlen
= fli
->FileNameLength
/ sizeof(WCHAR
);
2283 if (!fileref
|| !fileref
->parent
) {
2284 ERR("no fileref set and no directory given\n");
2285 return STATUS_INVALID_PARAMETER
;
2288 parfcb
= fileref
->parent
->fcb
;
2293 tfofcb
= tfo
->FsContext
;
2296 while (fnlen
> 0 && (fli
->FileName
[fnlen
- 1] == '/' || fli
->FileName
[fnlen
- 1] == '\\'))
2300 return STATUS_INVALID_PARAMETER
;
2302 for (i
= fnlen
- 1; i
>= 0; i
--) {
2303 if (fli
->FileName
[i
] == '\\' || fli
->FileName
[i
] == '/') {
2304 fn
= &fli
->FileName
[i
+1];
2305 fnlen
= (fli
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
2311 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2312 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2313 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2315 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2316 WARN("tried to create hard link on directory\n");
2317 Status
= STATUS_FILE_IS_A_DIRECTORY
;
2322 WARN("tried to create hard link on stream\n");
2323 Status
= STATUS_INVALID_PARAMETER
;
2328 fnus
.Length
= fnus
.MaximumLength
= fnlen
* sizeof(WCHAR
);
2330 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
2332 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2333 if (!NT_SUCCESS(Status
))
2336 utf8
.MaximumLength
= utf8
.Length
= utf8len
;
2337 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
2339 ERR("out of memory\n");
2340 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2344 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2345 if (!NT_SUCCESS(Status
))
2348 if (tfo
&& tfo
->FsContext2
) {
2349 struct _ccb
* relatedccb
= tfo
->FsContext2
;
2351 related
= relatedccb
->fileref
;
2352 increase_fileref_refcount(related
);
2355 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2357 if (NT_SUCCESS(Status
)) {
2358 if (!oldfileref
->deleted
) {
2359 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref
));
2361 if (!fli
->ReplaceIfExists
) {
2362 Status
= STATUS_OBJECT_NAME_COLLISION
;
2364 } else if (oldfileref
->open_count
>= 1 && !oldfileref
->deleted
) {
2365 WARN("trying to overwrite open file\n");
2366 Status
= STATUS_ACCESS_DENIED
;
2368 } else if (fileref
== oldfileref
) {
2369 Status
= STATUS_ACCESS_DENIED
;
2373 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2374 WARN("trying to overwrite directory\n");
2375 Status
= STATUS_ACCESS_DENIED
;
2379 free_fileref(oldfileref
);
2385 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2387 if (!NT_SUCCESS(Status
)) {
2388 ERR("open_fileref returned %08x\n", Status
);
2393 SeCaptureSubjectContext(&subjcont
);
2395 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, FILE_ADD_FILE
, 0, NULL
,
2396 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2397 SeReleaseSubjectContext(&subjcont
);
2398 WARN("SeAccessCheck failed, returning %08x\n", Status
);
2402 SeReleaseSubjectContext(&subjcont
);
2404 if (fcb
->subvol
!= parfcb
->subvol
) {
2405 WARN("can't create hard link over subvolume boundary\n");
2406 Status
= STATUS_INVALID_PARAMETER
;
2411 SeCaptureSubjectContext(&subjcont
);
2413 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
2414 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2415 SeReleaseSubjectContext(&subjcont
);
2416 WARN("SeAccessCheck failed, returning %08x\n", Status
);
2420 SeReleaseSubjectContext(&subjcont
);
2422 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
2423 if (!NT_SUCCESS(Status
)) {
2424 ERR("delete_fileref returned %08x\n", Status
);
2429 Status
= fcb_get_last_dir_index(related
->fcb
, &index
, Irp
);
2430 if (!NT_SUCCESS(Status
)) {
2431 ERR("fcb_get_last_dir_index returned %08x\n", Status
);
2435 fr2
= create_fileref();
2442 fr2
->created
= TRUE
;
2443 fr2
->parent
= related
;
2445 fr2
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
2446 if (!fr2
->filepart
.Buffer
) {
2447 ERR("out of memory\n");
2448 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2452 fr2
->filepart
.Length
= fr2
->filepart
.MaximumLength
= fnus
.Length
;
2453 RtlCopyMemory(fr2
->filepart
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2455 Status
= RtlUpcaseUnicodeString(&fr2
->filepart_uc
, &fr2
->filepart
, TRUE
);
2456 if (!NT_SUCCESS(Status
)) {
2457 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2461 insert_fileref_child(related
, fr2
, TRUE
);
2463 Status
= add_dir_child(related
->fcb
, fcb
->inode
, FALSE
, index
, &utf8
, &fr2
->filepart
, &fr2
->filepart_uc
, fcb
->type
, &dc
);
2464 if (!NT_SUCCESS(Status
))
2465 WARN("add_dir_child returned %08x\n", Status
);
2470 // add hardlink for existing fileref, if it's not there already
2471 if (IsListEmpty(&fcb
->hardlinks
)) {
2472 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2474 ERR("out of memory\n");
2475 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2479 hl
->parent
= fileref
->parent
->fcb
->inode
;
2480 hl
->index
= fileref
->index
;
2482 hl
->name
.Length
= hl
->name
.MaximumLength
= fileref
->filepart
.Length
;
2483 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
2485 if (!hl
->name
.Buffer
) {
2486 ERR("out of memory\n");
2488 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2492 RtlCopyMemory(hl
->name
.Buffer
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
2494 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->utf8
.Length
;
2495 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2497 if (!hl
->utf8
.Buffer
) {
2498 ERR("out of memory\n");
2499 ExFreePool(hl
->name
.Buffer
);
2501 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2505 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
2507 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2510 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2512 ERR("out of memory\n");
2513 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2517 hl
->parent
= related
->fcb
->inode
;
2520 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2521 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
2523 if (!hl
->name
.Buffer
) {
2524 ERR("out of memory\n");
2526 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2530 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2532 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= utf8
.Length
;
2533 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2535 if (!hl
->utf8
.Buffer
) {
2536 ERR("out of memory\n");
2537 ExFreePool(hl
->name
.Buffer
);
2539 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2543 RtlCopyMemory(hl
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
2545 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2547 mark_fileref_dirty(fr2
);
2550 // update inode's INODE_ITEM
2552 KeQuerySystemTime(&time
);
2553 win_time_to_unix(time
, &now
);
2555 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2556 fcb
->inode_item
.sequence
++;
2557 fcb
->inode_item
.st_nlink
++;
2559 if (!ccb
->user_set_change_time
)
2560 fcb
->inode_item
.st_ctime
= now
;
2562 fcb
->inode_item_changed
= TRUE
;
2563 mark_fcb_dirty(fcb
);
2565 // update parent's INODE_ITEM
2567 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2568 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2569 parfcb
->inode_item
.st_size
+= 2 * utf8len
;
2570 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2571 parfcb
->inode_item
.sequence
++;
2572 parfcb
->inode_item
.st_ctime
= now
;
2574 parfcb
->inode_item_changed
= TRUE
;
2575 mark_fcb_dirty(parfcb
);
2577 send_notification_fileref(fr2
, FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
);
2579 Status
= STATUS_SUCCESS
;
2583 free_fileref(oldfileref
);
2585 if (!NT_SUCCESS(Status
) && related
)
2586 free_fileref(related
);
2588 if (!NT_SUCCESS(Status
) && fr2
)
2591 if (NT_SUCCESS(Status
))
2592 clear_rollback(Vcb
, &rollback
);
2594 do_rollback(Vcb
, &rollback
);
2596 ExReleaseResourceLite(fcb
->Header
.Resource
);
2597 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2598 ExReleaseResourceLite(&Vcb
->tree_lock
);
2603 NTSTATUS STDCALL
drv_set_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2605 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2606 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2607 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
2608 ccb
* ccb
= IrpSp
->FileObject
->FsContext2
;
2611 FsRtlEnterFileSystem();
2613 top_level
= is_top_level(Irp
);
2615 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
2616 Status
= part0_passthrough(DeviceObject
, Irp
);
2620 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
2621 Status
= STATUS_ACCESS_DENIED
;
2625 if (Vcb
->readonly
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
) {
2626 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2632 Status
= STATUS_INVALID_PARAMETER
;
2638 Status
= STATUS_INVALID_PARAMETER
;
2642 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
&&
2643 (fcb
->inode
!= SUBVOL_ROOT_INODE
|| IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileBasicInformation
)) {
2644 Status
= STATUS_ACCESS_DENIED
;
2648 Irp
->IoStatus
.Information
= 0;
2650 Status
= STATUS_NOT_IMPLEMENTED
;
2652 TRACE("set information\n");
2654 switch (IrpSp
->Parameters
.SetFile
.FileInformationClass
) {
2655 case FileAllocationInformation
:
2657 TRACE("FileAllocationInformation\n");
2659 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
2660 WARN("insufficient privileges\n");
2661 Status
= STATUS_ACCESS_DENIED
;
2665 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, FALSE
);
2669 case FileBasicInformation
:
2671 TRACE("FileBasicInformation\n");
2673 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
2674 WARN("insufficient privileges\n");
2675 Status
= STATUS_ACCESS_DENIED
;
2679 Status
= set_basic_information(Vcb
, Irp
, IrpSp
->FileObject
);
2684 case FileDispositionInformation
:
2686 TRACE("FileDispositionInformation\n");
2688 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& DELETE
)) {
2689 WARN("insufficient privileges\n");
2690 Status
= STATUS_ACCESS_DENIED
;
2694 Status
= set_disposition_information(Vcb
, Irp
, IrpSp
->FileObject
);
2699 case FileEndOfFileInformation
:
2701 TRACE("FileEndOfFileInformation\n");
2703 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2704 WARN("insufficient privileges\n");
2705 Status
= STATUS_ACCESS_DENIED
;
2709 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.AdvanceOnly
);
2714 case FileLinkInformation
:
2715 TRACE("FileLinkInformation\n");
2716 Status
= set_link_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2719 case FilePositionInformation
:
2721 TRACE("FilePositionInformation\n");
2723 Status
= set_position_information(Vcb
, Irp
, IrpSp
->FileObject
);
2728 case FileRenameInformation
:
2729 TRACE("FileRenameInformation\n");
2730 // FIXME - make this work with streams
2731 Status
= set_rename_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2734 case FileValidDataLengthInformation
:
2735 FIXME("STUB: FileValidDataLengthInformation\n");
2738 #if (NTDDI_VERSION >= NTDDI_VISTA)
2739 case FileNormalizedNameInformation
:
2740 FIXME("STUB: FileNormalizedNameInformation\n");
2744 #if (NTDDI_VERSION >= NTDDI_WIN7)
2745 case FileStandardLinkInformation
:
2746 FIXME("STUB: FileStandardLinkInformation\n");
2749 case FileRemoteProtocolInformation
:
2750 TRACE("FileRemoteProtocolInformation\n");
2755 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.SetFile
.FileInformationClass
);
2759 Irp
->IoStatus
.Status
= Status
;
2761 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2765 IoSetTopLevelIrp(NULL
);
2767 FsRtlExitFileSystem();
2772 static NTSTATUS STDCALL
fill_in_file_basic_information(FILE_BASIC_INFORMATION
* fbi
, INODE_ITEM
* ii
, LONG
* length
, fcb
* fcb
, file_ref
* fileref
) {
2773 RtlZeroMemory(fbi
, sizeof(FILE_BASIC_INFORMATION
));
2775 *length
-= sizeof(FILE_BASIC_INFORMATION
);
2777 fbi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2778 fbi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2779 fbi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2780 fbi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2783 if (!fileref
|| !fileref
->parent
) {
2784 ERR("no fileref for stream\n");
2785 return STATUS_INTERNAL_ERROR
;
2787 fbi
->FileAttributes
= fileref
->parent
->fcb
->atts
;
2789 fbi
->FileAttributes
= fcb
->atts
;
2791 return STATUS_SUCCESS
;
2794 static NTSTATUS STDCALL
fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION
* fnoi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2797 if (*length
< sizeof(FILE_NETWORK_OPEN_INFORMATION
)) {
2799 return STATUS_BUFFER_OVERFLOW
;
2802 RtlZeroMemory(fnoi
, sizeof(FILE_NETWORK_OPEN_INFORMATION
));
2804 *length
-= sizeof(FILE_NETWORK_OPEN_INFORMATION
);
2807 if (!fileref
|| !fileref
->parent
) {
2808 ERR("no fileref for stream\n");
2809 return STATUS_INTERNAL_ERROR
;
2812 ii
= &fileref
->parent
->fcb
->inode_item
;
2814 ii
= &fcb
->inode_item
;
2816 fnoi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2817 fnoi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2818 fnoi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2819 fnoi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2822 fnoi
->AllocationSize
.QuadPart
= fnoi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2823 fnoi
->FileAttributes
= fileref
->parent
->fcb
->atts
;
2825 fnoi
->AllocationSize
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2826 fnoi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2827 fnoi
->FileAttributes
= fcb
->atts
;
2830 return STATUS_SUCCESS
;
2833 static NTSTATUS STDCALL
fill_in_file_standard_information(FILE_STANDARD_INFORMATION
* fsi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2834 RtlZeroMemory(fsi
, sizeof(FILE_STANDARD_INFORMATION
));
2836 *length
-= sizeof(FILE_STANDARD_INFORMATION
);
2839 if (!fileref
|| !fileref
->parent
) {
2840 ERR("no fileref for stream\n");
2841 return STATUS_INTERNAL_ERROR
;
2844 fsi
->AllocationSize
.QuadPart
= fsi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2845 fsi
->NumberOfLinks
= fileref
->parent
->fcb
->inode_item
.st_nlink
;
2846 fsi
->Directory
= S_ISDIR(fileref
->parent
->fcb
->inode_item
.st_mode
);
2848 fsi
->AllocationSize
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2849 fsi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2850 fsi
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
2851 fsi
->Directory
= S_ISDIR(fcb
->inode_item
.st_mode
);
2854 TRACE("length = %llu\n", fsi
->EndOfFile
.QuadPart
);
2856 fsi
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
2858 return STATUS_SUCCESS
;
2861 static NTSTATUS STDCALL
fill_in_file_internal_information(FILE_INTERNAL_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
2862 *length
-= sizeof(FILE_INTERNAL_INFORMATION
);
2864 fii
->IndexNumber
.QuadPart
= make_file_id(fcb
->subvol
, fcb
->inode
);
2866 return STATUS_SUCCESS
;
2869 static NTSTATUS STDCALL
fill_in_file_ea_information(FILE_EA_INFORMATION
* eai
, fcb
* fcb
, LONG
* length
) {
2870 *length
-= sizeof(FILE_EA_INFORMATION
);
2872 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2873 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2874 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2877 eai
->EaSize
= fcb
->ealen
;
2879 return STATUS_SUCCESS
;
2882 static NTSTATUS STDCALL
fill_in_file_access_information(FILE_ACCESS_INFORMATION
* fai
, LONG
* length
) {
2883 *length
-= sizeof(FILE_ACCESS_INFORMATION
);
2885 fai
->AccessFlags
= GENERIC_READ
;
2887 return STATUS_NOT_IMPLEMENTED
;
2890 static NTSTATUS STDCALL
fill_in_file_position_information(FILE_POSITION_INFORMATION
* fpi
, PFILE_OBJECT FileObject
, LONG
* length
) {
2891 RtlZeroMemory(fpi
, sizeof(FILE_POSITION_INFORMATION
));
2893 *length
-= sizeof(FILE_POSITION_INFORMATION
);
2895 fpi
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
2897 return STATUS_SUCCESS
;
2900 static NTSTATUS STDCALL
fill_in_file_mode_information(FILE_MODE_INFORMATION
* fmi
, ccb
* ccb
, LONG
* length
) {
2901 RtlZeroMemory(fmi
, sizeof(FILE_MODE_INFORMATION
));
2903 *length
-= sizeof(FILE_MODE_INFORMATION
);
2905 if (ccb
->options
& FILE_WRITE_THROUGH
)
2906 fmi
->Mode
|= FILE_WRITE_THROUGH
;
2908 if (ccb
->options
& FILE_SEQUENTIAL_ONLY
)
2909 fmi
->Mode
|= FILE_SEQUENTIAL_ONLY
;
2911 if (ccb
->options
& FILE_NO_INTERMEDIATE_BUFFERING
)
2912 fmi
->Mode
|= FILE_NO_INTERMEDIATE_BUFFERING
;
2914 if (ccb
->options
& FILE_SYNCHRONOUS_IO_ALERT
)
2915 fmi
->Mode
|= FILE_SYNCHRONOUS_IO_ALERT
;
2917 if (ccb
->options
& FILE_SYNCHRONOUS_IO_NONALERT
)
2918 fmi
->Mode
|= FILE_SYNCHRONOUS_IO_NONALERT
;
2920 if (ccb
->options
& FILE_DELETE_ON_CLOSE
)
2921 fmi
->Mode
|= FILE_DELETE_ON_CLOSE
;
2923 return STATUS_SUCCESS
;
2926 static NTSTATUS STDCALL
fill_in_file_alignment_information(FILE_ALIGNMENT_INFORMATION
* fai
, device_extension
* Vcb
, LONG
* length
) {
2927 RtlZeroMemory(fai
, sizeof(FILE_ALIGNMENT_INFORMATION
));
2929 *length
-= sizeof(FILE_ALIGNMENT_INFORMATION
);
2931 fai
->AlignmentRequirement
= first_device(Vcb
)->devobj
->AlignmentRequirement
;
2933 return STATUS_SUCCESS
;
2938 LIST_ENTRY list_entry
;
2941 NTSTATUS
fileref_get_filename(file_ref
* fileref
, PUNICODE_STRING fn
, USHORT
* name_offset
) {
2942 LIST_ENTRY fr_list
, *le
;
2947 // FIXME - we need a lock on filerefs' filepart
2949 if (fileref
== fileref
->fcb
->Vcb
->root_fileref
) {
2950 fn
->Buffer
= ExAllocatePoolWithTag(PagedPool
, sizeof(WCHAR
), ALLOC_TAG
);
2952 ERR("out of memory\n");
2953 return STATUS_INSUFFICIENT_RESOURCES
;
2956 fn
->Length
= fn
->MaximumLength
= sizeof(WCHAR
);
2957 fn
->Buffer
[0] = '\\';
2962 return STATUS_SUCCESS
;
2965 InitializeListHead(&fr_list
);
2973 frl
= ExAllocatePoolWithTag(PagedPool
, sizeof(fileref_list
), ALLOC_TAG
);
2975 ERR("out of memory\n");
2976 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2981 InsertTailList(&fr_list
, &frl
->list_entry
);
2983 len
+= fr
->filepart
.Length
;
2985 if (fr
!= fileref
->fcb
->Vcb
->root_fileref
)
2986 len
+= sizeof(WCHAR
);
2991 fn
->Buffer
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
2993 ERR("out of memory\n");
2994 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2998 fn
->Length
= fn
->MaximumLength
= len
;
3003 while (le
!= &fr_list
) {
3004 fileref_list
* frl
= CONTAINING_RECORD(le
, fileref_list
, list_entry
);
3006 if (frl
->fileref
!= fileref
->fcb
->Vcb
->root_fileref
) {
3007 fn
->Buffer
[i
] = frl
->fileref
->fcb
->ads
? ':' : '\\';
3010 if (name_offset
&& frl
->fileref
== fileref
)
3011 *name_offset
= i
* sizeof(WCHAR
);
3013 RtlCopyMemory(&fn
->Buffer
[i
], frl
->fileref
->filepart
.Buffer
, frl
->fileref
->filepart
.Length
);
3014 i
+= frl
->fileref
->filepart
.Length
/ sizeof(WCHAR
);
3020 Status
= STATUS_SUCCESS
;
3023 while (!IsListEmpty(&fr_list
)) {
3026 le
= RemoveHeadList(&fr_list
);
3027 frl
= CONTAINING_RECORD(le
, fileref_list
, list_entry
);
3035 static NTSTATUS STDCALL
fill_in_file_name_information(FILE_NAME_INFORMATION
* fni
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3041 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
3042 ULONG datasuflen
= wcslen(datasuf
) * sizeof(WCHAR
);
3045 ERR("called without fileref\n");
3046 return STATUS_INVALID_PARAMETER
;
3049 RtlZeroMemory(fni
, sizeof(FILE_NAME_INFORMATION
));
3051 *length
-= (LONG
)offsetof(FILE_NAME_INFORMATION
, FileName
[0]);
3053 TRACE("maximum length is %u\n", *length
);
3054 fni
->FileNameLength
= 0;
3056 fni
->FileName
[0] = 0;
3058 Status
= fileref_get_filename(fileref
, &fn
, NULL
);
3059 if (!NT_SUCCESS(Status
)) {
3060 ERR("fileref_get_filename returned %08x\n", Status
);
3064 if (*length
>= (LONG
)fn
.Length
) {
3065 RtlCopyMemory(fni
->FileName
, fn
.Buffer
, fn
.Length
);
3069 *length
-= fn
.Length
;
3072 RtlCopyMemory(fni
->FileName
, fn
.Buffer
, *length
);
3080 fni
->FileNameLength
= fn
.Length
;
3083 if (*length
>= (LONG
)datasuflen
) {
3084 RtlCopyMemory(&fni
->FileName
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, datasuflen
);
3086 retlen
+= datasuflen
;
3088 *length
-= datasuflen
;
3091 RtlCopyMemory(&fni
->FileName
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, *length
);
3100 ExFreePool(fn
.Buffer
);
3102 TRACE("%.*S\n", retlen
/ sizeof(WCHAR
), fni
->FileName
);
3104 return STATUS_SUCCESS
;
3107 static NTSTATUS STDCALL
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION
* ati
, fcb
* fcb
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3108 *length
-= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
);
3111 if (!fileref
|| !fileref
->parent
) {
3112 ERR("no fileref for stream\n");
3113 return STATUS_INTERNAL_ERROR
;
3116 ati
->FileAttributes
= fileref
->parent
->fcb
->atts
;
3118 ati
->FileAttributes
= fcb
->atts
;
3120 if (!(ati
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3121 ati
->ReparseTag
= 0;
3123 ati
->ReparseTag
= get_reparse_tag(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fcb
->atts
, Irp
);
3125 return STATUS_SUCCESS
;
3129 UNICODE_STRING name
;
3132 LIST_ENTRY list_entry
;
3135 static NTSTATUS STDCALL
fill_in_file_stream_information(FILE_STREAM_INFORMATION
* fsi
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3137 LIST_ENTRY streamlist
, *le
;
3138 FILE_STREAM_INFORMATION
*entry
, *lastentry
;
3141 traverse_ptr tp
, next_tp
;
3145 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
3146 static char xapref
[] = "user.";
3150 ERR("fileref was NULL\n");
3151 return STATUS_INVALID_PARAMETER
;
3154 InitializeListHead(&streamlist
);
3156 ExAcquireResourceSharedLite(&fileref
->fcb
->Vcb
->tree_lock
, TRUE
);
3157 ExAcquireResourceSharedLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3159 suf
.Buffer
= datasuf
;
3160 suf
.Length
= suf
.MaximumLength
= wcslen(datasuf
) * sizeof(WCHAR
);
3162 searchkey
.obj_id
= fileref
->fcb
->inode
;
3163 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
3164 searchkey
.offset
= 0;
3166 Status
= find_item(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3167 if (!NT_SUCCESS(Status
)) {
3168 ERR("error - find_item returned %08x\n", Status
);
3172 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3173 si
= ExAllocatePoolWithTag(PagedPool
, sizeof(stream_info
), ALLOC_TAG
);
3175 ERR("out of memory\n");
3176 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3180 si
->name
.Length
= si
->name
.MaximumLength
= 0;
3181 si
->name
.Buffer
= NULL
;
3182 si
->size
= fileref
->fcb
->inode_item
.st_size
;
3185 InsertTailList(&streamlist
, &si
->list_entry
);
3189 if (tp
.item
->key
.obj_id
== fileref
->fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
3190 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3191 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
));
3193 ULONG len
= tp
.item
->size
;
3194 DIR_ITEM
* xa
= (DIR_ITEM
*)tp
.item
->data
;
3198 if (len
< sizeof(DIR_ITEM
) || len
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
3199 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3203 if (xa
->n
> strlen(xapref
) && RtlCompareMemory(xa
->name
, xapref
, strlen(xapref
)) == strlen(xapref
) &&
3204 (tp
.item
->key
.offset
!= EA_DOSATTRIB_HASH
|| xa
->n
!= strlen(EA_DOSATTRIB
) || RtlCompareMemory(xa
->name
, EA_DOSATTRIB
, xa
->n
) != xa
->n
) &&
3205 (tp
.item
->key
.offset
!= EA_EA_HASH
|| xa
->n
!= strlen(EA_EA
) || RtlCompareMemory(xa
->name
, EA_EA
, xa
->n
) != xa
->n
)
3207 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, &xa
->name
[strlen(xapref
)], xa
->n
- strlen(xapref
));
3208 if (!NT_SUCCESS(Status
)) {
3209 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3213 si
= ExAllocatePoolWithTag(PagedPool
, sizeof(stream_info
), ALLOC_TAG
);
3215 ERR("out of memory\n");
3216 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3220 si
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
3221 if (!si
->name
.Buffer
) {
3222 ERR("out of memory\n");
3223 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3228 Status
= RtlUTF8ToUnicodeN(si
->name
.Buffer
, stringlen
, &stringlen
, &xa
->name
[strlen(xapref
)], xa
->n
- strlen(xapref
));
3230 if (!NT_SUCCESS(Status
)) {
3231 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3232 ExFreePool(si
->name
.Buffer
);
3237 si
->name
.Length
= si
->name
.MaximumLength
= stringlen
;
3243 TRACE("stream name = %.*S (length = %u)\n", si
->name
.Length
/ sizeof(WCHAR
), si
->name
.Buffer
, si
->name
.Length
/ sizeof(WCHAR
));
3245 InsertTailList(&streamlist
, &si
->list_entry
);
3248 len
-= sizeof(DIR_ITEM
) - sizeof(char) + xa
->n
+ xa
->m
;
3249 xa
= (DIR_ITEM
*)&xa
->name
[xa
->n
+ xa
->m
]; // FIXME - test xattr hash collisions work
3254 b
= find_next_item(fileref
->fcb
->Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
3258 if (next_tp
.item
->key
.obj_id
> fileref
->fcb
->inode
|| next_tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
)
3263 ExAcquireResourceSharedLite(&fileref
->nonpaged
->children_lock
, TRUE
);
3265 le
= fileref
->children
.Flink
;
3266 while (le
!= &fileref
->children
) {
3267 file_ref
* fr
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
3269 if (fr
->fcb
&& fr
->fcb
->ads
) {
3270 LIST_ENTRY
* le2
= streamlist
.Flink
;
3273 while (le2
!= &streamlist
) {
3274 si
= CONTAINING_RECORD(le2
, stream_info
, list_entry
);
3276 if (si
&& si
->name
.Buffer
&& si
->name
.Length
== fr
->filepart
.Length
&&
3277 RtlCompareMemory(si
->name
.Buffer
, fr
->filepart
.Buffer
, si
->name
.Length
) == si
->name
.Length
) {
3279 si
->size
= fr
->fcb
->adsdata
.Length
;
3280 si
->ignore
= fr
->fcb
->deleted
;
3289 if (!found
&& !fr
->fcb
->deleted
) {
3290 si
= ExAllocatePoolWithTag(PagedPool
, sizeof(stream_info
), ALLOC_TAG
);
3292 ERR("out of memory\n");
3293 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3297 si
->name
.Length
= si
->name
.MaximumLength
= fr
->filepart
.Length
;
3299 si
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, si
->name
.MaximumLength
, ALLOC_TAG
);
3300 if (!si
->name
.Buffer
) {
3301 ERR("out of memory\n");
3302 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3307 RtlCopyMemory(si
->name
.Buffer
, fr
->filepart
.Buffer
, fr
->filepart
.Length
);
3309 si
->size
= fr
->fcb
->adsdata
.Length
;
3312 InsertTailList(&streamlist
, &si
->list_entry
);
3319 ExReleaseResourceLite(&fileref
->nonpaged
->children_lock
);
3323 le
= streamlist
.Flink
;
3324 while (le
!= &streamlist
) {
3325 si
= CONTAINING_RECORD(le
, stream_info
, list_entry
);
3328 reqsize
= sector_align(reqsize
, sizeof(LONGLONG
));
3329 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + si
->name
.Length
;
3335 TRACE("length = %i, reqsize = %u\n", *length
, reqsize
);
3337 if (reqsize
> *length
) {
3338 Status
= STATUS_BUFFER_OVERFLOW
;
3345 le
= streamlist
.Flink
;
3346 while (le
!= &streamlist
) {
3347 si
= CONTAINING_RECORD(le
, stream_info
, list_entry
);
3352 entry
->NextEntryOffset
= 0;
3353 entry
->StreamNameLength
= si
->name
.Length
+ suf
.Length
+ sizeof(WCHAR
);
3354 entry
->StreamSize
.QuadPart
= si
->size
;
3356 if (le
== streamlist
.Flink
)
3357 entry
->StreamAllocationSize
.QuadPart
= sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
);
3359 entry
->StreamAllocationSize
.QuadPart
= si
->size
;
3361 entry
->StreamName
[0] = ':';
3363 if (si
->name
.Length
> 0)
3364 RtlCopyMemory(&entry
->StreamName
[1], si
->name
.Buffer
, si
->name
.Length
);
3366 RtlCopyMemory(&entry
->StreamName
[1 + (si
->name
.Length
/ sizeof(WCHAR
))], suf
.Buffer
, suf
.Length
);
3369 lastentry
->NextEntryOffset
= (UINT8
*)entry
- (UINT8
*)lastentry
;
3371 off
= sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + si
->name
.Length
, sizeof(LONGLONG
));
3374 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3382 Status
= STATUS_SUCCESS
;
3385 while (!IsListEmpty(&streamlist
)) {
3386 le
= RemoveHeadList(&streamlist
);
3387 si
= CONTAINING_RECORD(le
, stream_info
, list_entry
);
3389 if (si
->name
.Buffer
)
3390 ExFreePool(si
->name
.Buffer
);
3395 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3396 ExReleaseResourceLite(&fileref
->fcb
->Vcb
->tree_lock
);
3402 static NTSTATUS STDCALL
fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION
* fsli
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3403 TRACE("FileStandardLinkInformation\n");
3405 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3407 fsli
->NumberOfAccessibleLinks
= fcb
->inode_item
.st_nlink
;
3408 fsli
->TotalNumberOfLinks
= fcb
->inode_item
.st_nlink
;
3409 fsli
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
3410 fsli
->Directory
= fcb
->type
== BTRFS_TYPE_DIRECTORY
? TRUE
: FALSE
;
3412 *length
-= sizeof(FILE_STANDARD_LINK_INFORMATION
);
3414 return STATUS_SUCCESS
;
3416 #endif /* __REACTOS__ */
3419 UNICODE_STRING name
;
3421 LIST_ENTRY list_entry
;
3424 static NTSTATUS
get_subvol_path(device_extension
* Vcb
, root
* subvol
, PIRP Irp
) {
3430 UNICODE_STRING dirpath
;
3434 // FIXME - add subvol->parent field
3436 if (subvol
== Vcb
->root_fileref
->fcb
->subvol
) {
3437 subvol
->path
.Length
= subvol
->path
.MaximumLength
= sizeof(WCHAR
);
3438 subvol
->path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, subvol
->path
.Length
, ALLOC_TAG
);
3439 subvol
->path
.Buffer
[0] = '\\';
3440 return STATUS_SUCCESS
;
3443 searchkey
.obj_id
= subvol
->id
;
3444 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
3445 searchkey
.offset
= 0xffffffffffffffff;
3447 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3448 if (!NT_SUCCESS(Status
)) {
3449 ERR("error - find_item returned %08x\n", Status
);
3453 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) { // top subvol
3454 subvol
->path
.Length
= subvol
->path
.MaximumLength
= sizeof(WCHAR
);
3455 subvol
->path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, subvol
->path
.Length
, ALLOC_TAG
);
3456 subvol
->path
.Buffer
[0] = '\\';
3457 return STATUS_SUCCESS
;
3460 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
3461 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
));
3462 return STATUS_INTERNAL_ERROR
;
3465 rr
= (ROOT_REF
*)tp
.item
->data
;
3467 if (tp
.item
->size
< sizeof(ROOT_REF
) - 1 + rr
->n
) {
3468 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
) - 1 + rr
->n
);
3469 return STATUS_INTERNAL_ERROR
;
3472 le
= Vcb
->roots
.Flink
;
3476 while (le
!= &Vcb
->roots
) {
3477 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
3479 if (r2
->id
== tp
.item
->key
.offset
) {
3488 ERR("unable to find subvol %llx\n", tp
.item
->key
.offset
);
3489 return STATUS_INTERNAL_ERROR
;
3492 // FIXME - recursion
3494 Status
= get_inode_dir_path(Vcb
, parsubvol
, rr
->dir
, &dirpath
, Irp
);
3495 if (!NT_SUCCESS(Status
)) {
3496 ERR("get_inode_dir_path returned %08x\n", Status
);
3500 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &namelen
, rr
->name
, rr
->n
);
3501 if (!NT_SUCCESS(Status
)) {
3502 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
3507 ERR("length was 0\n");
3508 Status
= STATUS_INTERNAL_ERROR
;
3512 subvol
->path
.Length
= subvol
->path
.MaximumLength
= dirpath
.Length
+ namelen
;
3513 subvol
->path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, subvol
->path
.Length
, ALLOC_TAG
);
3515 if (!subvol
->path
.Buffer
) {
3516 ERR("out of memory\n");
3518 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3522 RtlCopyMemory(subvol
->path
.Buffer
, dirpath
.Buffer
, dirpath
.Length
);
3524 Status
= RtlUTF8ToUnicodeN(&subvol
->path
.Buffer
[dirpath
.Length
/ sizeof(WCHAR
)], namelen
, &namelen
, rr
->name
, rr
->n
);
3525 if (!NT_SUCCESS(Status
)) {
3526 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
3530 Status
= STATUS_SUCCESS
;
3534 ExFreePool(dirpath
.Buffer
);
3536 if (!NT_SUCCESS(Status
) && subvol
->path
.Buffer
) {
3537 ExFreePool(subvol
->path
.Buffer
);
3538 subvol
->path
.Buffer
= NULL
;
3544 static NTSTATUS
get_inode_dir_path(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PUNICODE_STRING us
, PIRP Irp
) {
3549 LIST_ENTRY name_trail
, *le
;
3554 InitializeListHead(&name_trail
);
3558 // FIXME - start with subvol prefix
3559 if (!subvol
->path
.Buffer
) {
3560 Status
= get_subvol_path(Vcb
, subvol
, Irp
);
3561 if (!NT_SUCCESS(Status
)) {
3562 ERR("get_subvol_path returned %08x\n", Status
);
3567 while (in
!= subvol
->root_item
.objid
) {
3568 searchkey
.obj_id
= in
;
3569 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
3570 searchkey
.offset
= 0xffffffffffffffff;
3572 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3573 if (!NT_SUCCESS(Status
)) {
3574 ERR("error - find_item returned %08x\n", Status
);
3578 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
) {
3579 ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey
.obj_id
, subvol
->id
);
3580 Status
= STATUS_INTERNAL_ERROR
;
3584 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3585 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
3589 if (tp
.item
->size
< sizeof(INODE_REF
)) {
3590 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(INODE_REF
));
3591 Status
= STATUS_INTERNAL_ERROR
;
3595 if (tp
.item
->size
< sizeof(INODE_REF
) - 1 + ir
->n
) {
3596 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(INODE_REF
) - 1 + ir
->n
);
3597 Status
= STATUS_INTERNAL_ERROR
;
3601 nb
= ExAllocatePoolWithTag(PagedPool
, sizeof(name_bit
), ALLOC_TAG
);
3603 ERR("out of memory\n");
3604 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3608 nb
->name
.Buffer
= NULL
;
3610 InsertTailList(&name_trail
, &nb
->list_entry
);
3613 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &len
, ir
->name
, ir
->n
);
3614 if (!NT_SUCCESS(Status
)) {
3615 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
3620 ERR("length was 0\n");
3621 Status
= STATUS_INTERNAL_ERROR
;
3625 nb
->name
.Length
= nb
->name
.MaximumLength
= len
;
3627 nb
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, nb
->name
.Length
, ALLOC_TAG
);
3628 if (!nb
->name
.Buffer
) {
3629 ERR("out of memory\n");
3631 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3635 Status
= RtlUTF8ToUnicodeN(nb
->name
.Buffer
, len
, &len
, ir
->name
, ir
->n
);
3636 if (!NT_SUCCESS(Status
)) {
3637 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
3641 in
= tp
.item
->key
.offset
;
3642 namelen
+= nb
->name
.Length
;
3644 // } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3647 ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey
.obj_id
, subvol
->id
);
3648 Status
= STATUS_INTERNAL_ERROR
;
3653 namelen
+= (levels
+ 1) * sizeof(WCHAR
);
3655 us
->Length
= us
->MaximumLength
= namelen
;
3656 us
->Buffer
= ExAllocatePoolWithTag(PagedPool
, us
->Length
, ALLOC_TAG
);
3659 ERR("out of memory\n");
3660 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3664 us
->Buffer
[0] = '\\';
3665 usbuf
= &us
->Buffer
[1];
3667 le
= name_trail
.Blink
;
3668 while (le
!= &name_trail
) {
3669 name_bit
* nb
= CONTAINING_RECORD(le
, name_bit
, list_entry
);
3671 RtlCopyMemory(usbuf
, nb
->name
.Buffer
, nb
->name
.Length
);
3672 usbuf
+= nb
->name
.Length
/ sizeof(WCHAR
);
3680 Status
= STATUS_SUCCESS
;
3683 while (!IsListEmpty(&name_trail
)) {
3684 name_bit
* nb
= CONTAINING_RECORD(name_trail
.Flink
, name_bit
, list_entry
);
3686 if (nb
->name
.Buffer
)
3687 ExFreePool(nb
->name
.Buffer
);
3689 RemoveEntryList(&nb
->list_entry
);
3697 NTSTATUS
open_fileref_by_inode(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, file_ref
** pfr
, PIRP Irp
) {
3701 file_ref
*parfr
, *fr
;
3702 dir_child
* dc
= NULL
;
3704 Status
= open_fcb(Vcb
, subvol
, inode
, 0, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
3705 if (!NT_SUCCESS(Status
)) {
3706 ERR("open_fcb returned %08x\n", Status
);
3711 *pfr
= fcb
->fileref
;
3712 increase_fileref_refcount(fcb
->fileref
);
3713 return STATUS_SUCCESS
;
3716 // find hardlink if fcb doesn't have any loaded
3717 if (IsListEmpty(&fcb
->hardlinks
)) {
3721 searchkey
.obj_id
= fcb
->inode
;
3722 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
3723 searchkey
.offset
= 0xffffffffffffffff;
3725 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3726 if (!NT_SUCCESS(Status
)) {
3727 ERR("find_item returned %08x\n", Status
);
3732 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
3733 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3737 ir
= (INODE_REF
*)tp
.item
->data
;
3739 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
3741 ERR("out of memory\n");
3743 return STATUS_INSUFFICIENT_RESOURCES
;
3746 hl
->parent
= tp
.item
->key
.offset
;
3747 hl
->index
= ir
->index
;
3749 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ir
->n
;
3751 if (hl
->utf8
.Length
> 0) {
3752 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
3753 RtlCopyMemory(hl
->utf8
.Buffer
, ir
->name
, ir
->n
);
3756 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
3757 if (!NT_SUCCESS(Status
)) {
3758 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3764 hl
->name
.Length
= hl
->name
.MaximumLength
= stringlen
;
3767 hl
->name
.Buffer
= NULL
;
3769 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
3771 if (!hl
->name
.Buffer
) {
3772 ERR("out of memory\n");
3775 return STATUS_INSUFFICIENT_RESOURCES
;
3778 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
3779 if (!NT_SUCCESS(Status
)) {
3780 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3781 ExFreePool(hl
->name
.Buffer
);
3788 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
3789 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3794 ier
= (INODE_EXTREF
*)tp
.item
->data
;
3796 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
3798 ERR("out of memory\n");
3800 return STATUS_INSUFFICIENT_RESOURCES
;
3803 hl
->parent
= ier
->dir
;
3804 hl
->index
= ier
->index
;
3806 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ier
->n
;
3808 if (hl
->utf8
.Length
> 0) {
3809 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
3810 RtlCopyMemory(hl
->utf8
.Buffer
, ier
->name
, ier
->n
);
3813 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
3814 if (!NT_SUCCESS(Status
)) {
3815 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3821 hl
->name
.Length
= hl
->name
.MaximumLength
= stringlen
;
3824 hl
->name
.Buffer
= NULL
;
3826 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
3828 if (!hl
->name
.Buffer
) {
3829 ERR("out of memory\n");
3832 return STATUS_INSUFFICIENT_RESOURCES
;
3835 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
3836 if (!NT_SUCCESS(Status
)) {
3837 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3838 ExFreePool(hl
->name
.Buffer
);
3845 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
3850 if (IsListEmpty(&fcb
->hardlinks
)) {
3851 ERR("subvol %llx, inode %llx has no hardlinks\n", subvol
->id
, inode
);
3853 return STATUS_INTERNAL_ERROR
;
3856 hl
= CONTAINING_RECORD(fcb
->hardlinks
.Flink
, hardlink
, list_entry
);
3858 // FIXME - does this work with subvols?
3860 if (hl
->parent
== inode
) // root of subvol
3863 Status
= open_fileref_by_inode(Vcb
, subvol
, hl
->parent
, &parfr
, Irp
);
3864 if (!NT_SUCCESS(Status
)) {
3865 ERR("open_fileref_by_inode returned %08x\n", Status
);
3871 fr
= create_fileref();
3873 ERR("out of memory\n");
3875 return STATUS_INSUFFICIENT_RESOURCES
;
3881 fr
->index
= hl
->index
;
3883 fr
->utf8
.Length
= fr
->utf8
.MaximumLength
= hl
->utf8
.Length
;
3884 if (fr
->utf8
.Length
> 0) {
3885 fr
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fr
->utf8
.Length
, ALLOC_TAG
);
3887 if (!fr
->utf8
.Buffer
) {
3888 ERR("out of memory\n");
3890 return STATUS_INSUFFICIENT_RESOURCES
;
3893 RtlCopyMemory(fr
->utf8
.Buffer
, hl
->utf8
.Buffer
, hl
->utf8
.Length
);
3896 fr
->filepart
.MaximumLength
= fr
->filepart
.Length
= hl
->name
.Length
;
3898 if (fr
->filepart
.Length
> 0) {
3899 fr
->filepart
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fr
->filepart
.MaximumLength
, ALLOC_TAG
);
3900 if (!fr
->filepart
.Buffer
) {
3901 ERR("out of memory\n");
3903 return STATUS_INSUFFICIENT_RESOURCES
;
3906 RtlCopyMemory(fr
->filepart
.Buffer
, hl
->name
.Buffer
, hl
->name
.Length
);
3909 Status
= RtlUpcaseUnicodeString(&fr
->filepart_uc
, &fr
->filepart
, TRUE
);
3910 if (!NT_SUCCESS(Status
)) {
3911 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
3918 Status
= add_dir_child(parfr
->fcb
, fr
->fcb
->inode
== SUBVOL_ROOT_INODE
? fr
->fcb
->subvol
->id
: fr
->fcb
->inode
, fr
->fcb
->inode
== SUBVOL_ROOT_INODE
,
3919 fr
->index
, &fr
->utf8
, &fr
->filepart
, &fr
->filepart_uc
, fr
->fcb
->type
, &dc
);
3920 if (!NT_SUCCESS(Status
))
3921 WARN("add_dir_child returned %08x\n", Status
);
3926 insert_fileref_child(parfr
, fr
, TRUE
);
3930 return STATUS_SUCCESS
;
3934 static NTSTATUS STDCALL
fill_in_hard_link_information(FILE_LINKS_INFORMATION
* fli
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3938 FILE_LINK_ENTRY_INFORMATION
* feli
;
3939 BOOL overflow
= FALSE
;
3940 fcb
* fcb
= fileref
->fcb
;
3944 return STATUS_INVALID_PARAMETER
;
3946 if (*length
< offsetof(FILE_LINKS_INFORMATION
, Entry
))
3947 return STATUS_INVALID_PARAMETER
;
3949 RtlZeroMemory(fli
, *length
);
3951 bytes_needed
= offsetof(FILE_LINKS_INFORMATION
, Entry
);
3955 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3957 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
3960 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
)
3961 namelen
= sizeof(WCHAR
);
3963 namelen
= fileref
->filepart
.Length
;
3965 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) - sizeof(WCHAR
) + namelen
;
3967 if (bytes_needed
> *length
)
3973 feli
->NextEntryOffset
= 0;
3974 feli
->ParentFileId
= 0; // we use an inode of 0 to mean the parent of a subvolume
3976 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
) {
3977 feli
->FileNameLength
= 1;
3978 feli
->FileName
[0] = '.';
3980 feli
->FileNameLength
= fileref
->filepart
.Length
/ sizeof(WCHAR
);
3981 RtlCopyMemory(feli
->FileName
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
3984 fli
->EntriesReturned
++;
3989 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
3991 if (IsListEmpty(&fcb
->hardlinks
)) {
3992 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fileref
->filepart
.Length
- sizeof(WCHAR
);
3994 if (bytes_needed
> *length
)
4000 feli
->NextEntryOffset
= 0;
4001 feli
->ParentFileId
= fileref
->parent
->fcb
->inode
;
4002 feli
->FileNameLength
= fileref
->filepart
.Length
/ sizeof(WCHAR
);
4003 RtlCopyMemory(feli
->FileName
, fileref
->filepart
.Buffer
, fileref
->filepart
.Length
);
4005 fli
->EntriesReturned
++;
4010 le
= fcb
->hardlinks
.Flink
;
4011 while (le
!= &fcb
->hardlinks
) {
4012 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
4015 TRACE("parent %llx, index %llx, name %.*S\n", hl
->parent
, hl
->index
, hl
->name
.Length
/ sizeof(WCHAR
), hl
->name
.Buffer
);
4017 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, Irp
);
4019 if (!NT_SUCCESS(Status
)) {
4020 ERR("open_fileref_by_inode returned %08x\n", Status
);
4021 } else if (!parfr
->deleted
) {
4023 BOOL found
= FALSE
, deleted
= FALSE
;
4026 le2
= parfr
->children
.Flink
;
4027 while (le2
!= &parfr
->children
) {
4028 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
4030 if (fr2
->index
== hl
->index
) {
4032 deleted
= fr2
->deleted
;
4035 fn
= &fr2
->filepart
;
4047 TRACE("fn = %.*S (found = %u)\n", fn
->Length
/ sizeof(WCHAR
), fn
->Buffer
, found
);
4050 bytes_needed
= sector_align(bytes_needed
, 8);
4052 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fn
->Length
- sizeof(WCHAR
);
4054 if (bytes_needed
> *length
)
4059 feli
->NextEntryOffset
= sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION
) + ((feli
->FileNameLength
- 1) * sizeof(WCHAR
)), 8);
4060 feli
= (FILE_LINK_ENTRY_INFORMATION
*)((UINT8
*)feli
+ feli
->NextEntryOffset
);
4064 feli
->NextEntryOffset
= 0;
4065 feli
->ParentFileId
= parfr
->fcb
->inode
;
4066 feli
->FileNameLength
= fn
->Length
/ sizeof(WCHAR
);
4067 RtlCopyMemory(feli
->FileName
, fn
->Buffer
, fn
->Length
);
4069 fli
->EntriesReturned
++;
4075 free_fileref(parfr
);
4082 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
4085 fli
->BytesNeeded
= bytes_needed
;
4089 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
4091 ExReleaseResourceLite(fcb
->Header
.Resource
);
4095 #endif /* __REACTOS__ */
4097 #if (NTDDI_VERSION >= NTDDI_WIN10)
4099 typedef struct _FILE_ID_128
{
4100 UCHAR Identifier
[16];
4101 } FILE_ID_128
, *PFILE_ID_128
;
4103 typedef struct _FILE_ID_INFORMATION
{
4104 ULONGLONG VolumeSerialNumber
;
4106 } FILE_ID_INFORMATION
, *PFILE_ID_INFORMATION
;
4109 static NTSTATUS
fill_in_file_id_information(FILE_ID_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
4110 RtlCopyMemory(&fii
->VolumeSerialNumber
, &fcb
->Vcb
->superblock
.uuid
.uuid
[8], sizeof(UINT64
));
4111 RtlCopyMemory(&fii
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
4112 RtlCopyMemory(&fii
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
4114 *length
-= sizeof(FILE_ID_INFORMATION
);
4116 return STATUS_SUCCESS
;
4120 static NTSTATUS STDCALL
query_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
4121 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4122 LONG length
= IrpSp
->Parameters
.QueryFile
.Length
;
4123 fcb
* fcb
= FileObject
->FsContext
;
4124 ccb
* ccb
= FileObject
->FsContext2
;
4125 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
4128 TRACE("(%p, %p, %p)\n", Vcb
, FileObject
, Irp
);
4129 TRACE("fcb = %p\n", fcb
);
4131 if (fcb
== Vcb
->volume_fcb
)
4132 return STATUS_INVALID_PARAMETER
;
4135 ERR("ccb is NULL\n");
4136 return STATUS_INVALID_PARAMETER
;
4139 switch (IrpSp
->Parameters
.QueryFile
.FileInformationClass
) {
4140 case FileAllInformation
:
4142 FILE_ALL_INFORMATION
* fai
= Irp
->AssociatedIrp
.SystemBuffer
;
4145 TRACE("FileAllInformation\n");
4147 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
4148 WARN("insufficient privileges\n");
4149 Status
= STATUS_ACCESS_DENIED
;
4154 if (!fileref
|| !fileref
->parent
) {
4155 ERR("no fileref for stream\n");
4156 Status
= STATUS_INTERNAL_ERROR
;
4160 ii
= &fileref
->parent
->fcb
->inode_item
;
4162 ii
= &fcb
->inode_item
;
4165 fill_in_file_basic_information(&fai
->BasicInformation
, ii
, &length
, fcb
, fileref
);
4168 fill_in_file_standard_information(&fai
->StandardInformation
, fcb
, fileref
, &length
);
4171 fill_in_file_internal_information(&fai
->InternalInformation
, fcb
, &length
);
4174 fill_in_file_ea_information(&fai
->EaInformation
, fcb
, &length
);
4177 fill_in_file_access_information(&fai
->AccessInformation
, &length
);
4180 fill_in_file_position_information(&fai
->PositionInformation
, FileObject
, &length
);
4183 fill_in_file_mode_information(&fai
->ModeInformation
, ccb
, &length
);
4186 fill_in_file_alignment_information(&fai
->AlignmentInformation
, Vcb
, &length
);
4189 fill_in_file_name_information(&fai
->NameInformation
, fcb
, fileref
, &length
);
4191 Status
= STATUS_SUCCESS
;
4196 case FileAttributeTagInformation
:
4198 FILE_ATTRIBUTE_TAG_INFORMATION
* ati
= Irp
->AssociatedIrp
.SystemBuffer
;
4200 TRACE("FileAttributeTagInformation\n");
4202 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
4203 WARN("insufficient privileges\n");
4204 Status
= STATUS_ACCESS_DENIED
;
4208 Status
= fill_in_file_attribute_information(ati
, fcb
, fileref
, Irp
, &length
);
4213 case FileBasicInformation
:
4215 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
4218 TRACE("FileBasicInformation\n");
4220 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
4221 WARN("insufficient privileges\n");
4222 Status
= STATUS_ACCESS_DENIED
;
4226 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_BASIC_INFORMATION
)) {
4228 Status
= STATUS_BUFFER_OVERFLOW
;
4233 if (!fileref
|| !fileref
->parent
) {
4234 ERR("no fileref for stream\n");
4235 Status
= STATUS_INTERNAL_ERROR
;
4239 ii
= &fileref
->parent
->fcb
->inode_item
;
4241 ii
= &fcb
->inode_item
;
4243 Status
= fill_in_file_basic_information(fbi
, ii
, &length
, fcb
, fileref
);
4247 case FileCompressionInformation
:
4248 FIXME("STUB: FileCompressionInformation\n");
4249 Status
= STATUS_INVALID_PARAMETER
;
4252 case FileEaInformation
:
4254 FILE_EA_INFORMATION
* eai
= Irp
->AssociatedIrp
.SystemBuffer
;
4256 TRACE("FileEaInformation\n");
4258 Status
= fill_in_file_ea_information(eai
, fcb
, &length
);
4263 case FileInternalInformation
:
4265 FILE_INTERNAL_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
4267 TRACE("FileInternalInformation\n");
4269 Status
= fill_in_file_internal_information(fii
, fcb
, &length
);
4274 case FileNameInformation
:
4276 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
4278 TRACE("FileNameInformation\n");
4280 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
4285 case FileNetworkOpenInformation
:
4287 FILE_NETWORK_OPEN_INFORMATION
* fnoi
= Irp
->AssociatedIrp
.SystemBuffer
;
4289 TRACE("FileNetworkOpenInformation\n");
4291 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
4292 WARN("insufficient privileges\n");
4293 Status
= STATUS_ACCESS_DENIED
;
4297 Status
= fill_in_file_network_open_information(fnoi
, fcb
, fileref
, &length
);
4302 case FilePositionInformation
:
4304 FILE_POSITION_INFORMATION
* fpi
= Irp
->AssociatedIrp
.SystemBuffer
;
4306 TRACE("FilePositionInformation\n");
4308 Status
= fill_in_file_position_information(fpi
, FileObject
, &length
);
4313 case FileStandardInformation
:
4315 FILE_STANDARD_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
4317 TRACE("FileStandardInformation\n");
4319 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STANDARD_INFORMATION
)) {
4321 Status
= STATUS_BUFFER_OVERFLOW
;
4325 Status
= fill_in_file_standard_information(fsi
, fcb
, ccb
->fileref
, &length
);
4330 case FileStreamInformation
:
4332 FILE_STREAM_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
4334 TRACE("FileStreamInformation\n");
4336 Status
= fill_in_file_stream_information(fsi
, fileref
, Irp
, &length
);
4341 #if (NTDDI_VERSION >= NTDDI_VISTA)
4342 case FileHardLinkInformation
:
4344 FILE_LINKS_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
4346 TRACE("FileHardLinkInformation\n");
4348 Status
= fill_in_hard_link_information(fli
, fileref
, Irp
, &length
);
4353 case FileNormalizedNameInformation
:
4355 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
4357 TRACE("FileNormalizedNameInformation\n");
4359 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
4365 #if (NTDDI_VERSION >= NTDDI_WIN7)
4366 case FileStandardLinkInformation
:
4368 FILE_STANDARD_LINK_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
4370 TRACE("FileStandardLinkInformation\n");
4372 Status
= fill_in_file_standard_link_information(fsli
, fcb
, ccb
->fileref
, &length
);
4377 case FileRemoteProtocolInformation
:
4378 TRACE("FileRemoteProtocolInformation\n");
4379 Status
= STATUS_INVALID_PARAMETER
;
4383 #if (NTDDI_VERSION >= NTDDI_WIN10)
4384 #pragma GCC diagnostic push
4385 #pragma GCC diagnostic ignored "-Wswitch"
4386 case FileIdInformation
:
4388 FILE_ID_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
4390 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_ID_INFORMATION
)) {
4392 Status
= STATUS_BUFFER_OVERFLOW
;
4396 TRACE("FileIdInformation\n");
4398 Status
= fill_in_file_id_information(fii
, fcb
, &length
);
4402 #pragma GCC diagnostic pop
4406 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryFile
.FileInformationClass
);
4407 Status
= STATUS_INVALID_PARAMETER
;
4413 Status
= STATUS_BUFFER_OVERFLOW
;
4416 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- length
;
4419 TRACE("query_info returning %08x\n", Status
);
4424 NTSTATUS STDCALL
drv_query_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4425 PIO_STACK_LOCATION IrpSp
;
4428 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4431 FsRtlEnterFileSystem();
4433 top_level
= is_top_level(Irp
);
4435 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4436 Status
= part0_passthrough(DeviceObject
, Irp
);
4440 Irp
->IoStatus
.Information
= 0;
4442 TRACE("query information\n");
4444 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4446 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4448 fcb
= IrpSp
->FileObject
->FsContext
;
4449 TRACE("fcb = %p\n", fcb
);
4450 TRACE("fcb->subvol = %p\n", fcb
->subvol
);
4452 Status
= query_info(fcb
->Vcb
, IrpSp
->FileObject
, Irp
);
4454 TRACE("returning %08x\n", Status
);
4456 Irp
->IoStatus
.Status
= Status
;
4458 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4460 ExReleaseResourceLite(&Vcb
->tree_lock
);
4464 IoSetTopLevelIrp(NULL
);
4466 FsRtlExitFileSystem();
4471 NTSTATUS STDCALL
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4474 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4475 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4476 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4479 FILE_FULL_EA_INFORMATION
* ffei
;
4482 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4484 FsRtlEnterFileSystem();
4486 top_level
= is_top_level(Irp
);
4488 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4489 Status
= part0_passthrough(DeviceObject
, Irp
);
4493 ffei
= map_user_buffer(Irp
);
4495 ERR("could not get output buffer\n");
4496 Status
= STATUS_INVALID_PARAMETER
;
4501 ERR("no file object\n");
4502 Status
= STATUS_INVALID_PARAMETER
;
4506 fcb
= FileObject
->FsContext
;
4510 Status
= STATUS_INVALID_PARAMETER
;
4514 ccb
= FileObject
->FsContext2
;
4518 Status
= STATUS_INVALID_PARAMETER
;
4522 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_READ_EA
| FILE_WRITE_EA
))) {
4523 WARN("insufficient privileges\n");
4524 Status
= STATUS_ACCESS_DENIED
;
4528 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4530 Status
= STATUS_SUCCESS
;
4532 if (fcb
->ea_xattr
.Length
== 0)
4535 if (IrpSp
->Parameters
.QueryEa
.EaList
) {
4536 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4537 FILE_GET_EA_INFORMATION
* in
;
4539 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4543 s
.Length
= s
.MaximumLength
= in
->EaNameLength
;
4544 s
.Buffer
= in
->EaName
;
4546 RtlUpperString(&s
, &s
);
4548 if (in
->NextEntryOffset
== 0)
4551 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4554 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4560 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4562 if (in
->EaNameLength
== ea
->EaNameLength
&&
4563 RtlCompareMemory(in
->EaName
, ea
->EaName
, in
->EaNameLength
) == in
->EaNameLength
) {
4568 if (in
->NextEntryOffset
== 0)
4571 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4575 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4577 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4578 Status
= STATUS_BUFFER_OVERFLOW
;
4586 out
->NextEntryOffset
= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4587 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4591 out
->NextEntryOffset
= 0;
4592 out
->Flags
= ea
->Flags
;
4593 out
->EaNameLength
= ea
->EaNameLength
;
4594 out
->EaValueLength
= ea
->EaValueLength
;
4595 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4597 retlen
+= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4599 if (IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4603 if (ea
->NextEntryOffset
== 0)
4606 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4609 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4612 if (IrpSp
->Flags
& SL_INDEX_SPECIFIED
) {
4613 // The index is 1-based
4614 if (IrpSp
->Parameters
.QueryEa
.EaIndex
== 0) {
4615 Status
= STATUS_NONEXISTENT_EA_ENTRY
;
4618 index
= IrpSp
->Parameters
.QueryEa
.EaIndex
- 1;
4619 } else if (IrpSp
->Flags
& SL_RESTART_SCAN
)
4620 index
= ccb
->ea_index
= 0;
4622 index
= ccb
->ea_index
;
4624 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4629 for (i
= 0; i
< index
; i
++) {
4630 if (ea
->NextEntryOffset
== 0) // last item
4633 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4640 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4642 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4643 Status
= retlen
== 0 ? STATUS_BUFFER_TOO_SMALL
: STATUS_BUFFER_OVERFLOW
;
4650 out
->NextEntryOffset
= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4651 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4655 out
->NextEntryOffset
= 0;
4656 out
->Flags
= ea
->Flags
;
4657 out
->EaNameLength
= ea
->EaNameLength
;
4658 out
->EaValueLength
= ea
->EaValueLength
;
4659 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4661 retlen
+= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4663 if (!(IrpSp
->Flags
& SL_INDEX_SPECIFIED
))
4666 if (ea
->NextEntryOffset
== 0 || IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4669 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4674 ExReleaseResourceLite(fcb
->Header
.Resource
);
4677 Irp
->IoStatus
.Status
= Status
;
4678 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
? retlen
: 0;
4680 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4684 IoSetTopLevelIrp(NULL
);
4686 FsRtlExitFileSystem();
4695 LIST_ENTRY list_entry
;
4698 NTSTATUS STDCALL
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4699 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4702 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4703 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4706 FILE_FULL_EA_INFORMATION
* ffei
;
4710 FILE_FULL_EA_INFORMATION
* ea
;
4715 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4717 FsRtlEnterFileSystem();
4719 top_level
= is_top_level(Irp
);
4721 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4722 Status
= part0_passthrough(DeviceObject
, Irp
);
4726 if (Vcb
->readonly
) {
4727 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4731 ffei
= map_user_buffer(Irp
);
4733 ERR("could not get output buffer\n");
4734 Status
= STATUS_INVALID_PARAMETER
;
4738 Status
= IoCheckEaBufferValidity(ffei
, IrpSp
->Parameters
.SetEa
.Length
, &offset
);
4739 if (!NT_SUCCESS(Status
)) {
4740 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4745 ERR("no file object\n");
4746 Status
= STATUS_INVALID_PARAMETER
;
4750 fcb
= FileObject
->FsContext
;
4754 Status
= STATUS_INVALID_PARAMETER
;
4758 ccb
= FileObject
->FsContext2
;
4762 Status
= STATUS_INVALID_PARAMETER
;
4766 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_EA
)) {
4767 WARN("insufficient privileges\n");
4768 Status
= STATUS_ACCESS_DENIED
;
4772 InitializeListHead(&ealist
);
4774 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4776 if (fcb
->ea_xattr
.Length
> 0) {
4777 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4780 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4782 ERR("out of memory\n");
4783 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4787 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4788 item
->name
.Buffer
= ea
->EaName
;
4790 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4791 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4793 item
->flags
= ea
->Flags
;
4795 InsertTailList(&ealist
, &item
->list_entry
);
4797 if (ea
->NextEntryOffset
== 0)
4800 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4810 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
4811 s
.Buffer
= ea
->EaName
;
4813 RtlUpperString(&s
, &s
);
4816 while (le
!= &ealist
) {
4817 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4819 if (item
->name
.Length
== s
.Length
&&
4820 RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
4821 item
->flags
= ea
->Flags
;
4822 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4823 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4832 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4834 ERR("out of memory\n");
4835 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4839 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4840 item
->name
.Buffer
= ea
->EaName
;
4842 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4843 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4845 item
->flags
= ea
->Flags
;
4847 InsertTailList(&ealist
, &item
->list_entry
);
4850 if (ea
->NextEntryOffset
== 0)
4853 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4856 // remove entries with zero-length value
4858 while (le
!= &ealist
) {
4859 LIST_ENTRY
* le2
= le
->Flink
;
4861 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4863 if (item
->value
.Length
== 0) {
4864 RemoveEntryList(&item
->list_entry
);
4871 if (IsListEmpty(&ealist
)) {
4874 if (fcb
->ea_xattr
.Buffer
)
4875 ExFreePool(fcb
->ea_xattr
.Buffer
);
4877 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4878 fcb
->ea_xattr
.Buffer
= NULL
;
4884 while (le
!= &ealist
) {
4885 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4888 size
+= 4 - (size
% 4);
4890 size
+= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
4895 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
4897 ERR("out of memory\n");
4898 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4902 oldbuf
= fcb
->ea_xattr
.Buffer
;
4904 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
4905 fcb
->ea_xattr
.Buffer
= buf
;
4911 while (le
!= &ealist
) {
4912 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4915 ea
->NextEntryOffset
= offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
4917 if (ea
->NextEntryOffset
% 4 > 0)
4918 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
4920 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4922 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4924 ea
->NextEntryOffset
= 0;
4925 ea
->Flags
= item
->flags
;
4926 ea
->EaNameLength
= item
->name
.Length
;
4927 ea
->EaValueLength
= item
->value
.Length
;
4929 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
4930 ea
->EaName
[item
->name
.Length
] = 0;
4931 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
4933 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
4942 fcb
->ea_changed
= TRUE
;
4944 KeQuerySystemTime(&time
);
4945 win_time_to_unix(time
, &now
);
4947 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
4948 fcb
->inode_item
.sequence
++;
4950 if (!ccb
->user_set_change_time
)
4951 fcb
->inode_item
.st_ctime
= now
;
4953 fcb
->inode_item_changed
= TRUE
;
4954 mark_fcb_dirty(fcb
);
4956 send_notification_fileref(ccb
->fileref
, FILE_NOTIFY_CHANGE_EA
, FILE_ACTION_MODIFIED
);
4958 Status
= STATUS_SUCCESS
;
4961 ExReleaseResourceLite(fcb
->Header
.Resource
);
4963 while (!IsListEmpty(&ealist
)) {
4964 le
= RemoveHeadList(&ealist
);
4966 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4972 Irp
->IoStatus
.Status
= Status
;
4973 Irp
->IoStatus
.Information
= 0;
4975 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4979 IoSetTopLevelIrp(NULL
);
4981 FsRtlExitFileSystem();