1 /* Copyright (c) Mark Harmstone 2016-17
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
set_basic_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
28 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
29 fcb
* fcb
= FileObject
->FsContext
;
30 ccb
* ccb
= FileObject
->FsContext2
;
31 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
32 ULONG defda
, filter
= 0;
33 BOOL inode_item_changed
= FALSE
;
37 if (fileref
&& fileref
->parent
)
38 fcb
= fileref
->parent
->fcb
;
40 ERR("stream did not have fileref\n");
41 return STATUS_INTERNAL_ERROR
;
46 ERR("ccb was NULL\n");
47 return STATUS_INVALID_PARAMETER
;
50 TRACE("file = %S, attributes = %x\n", file_desc(FileObject
), fbi
->FileAttributes
);
52 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
54 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
55 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
56 Status
= STATUS_INVALID_PARAMETER
;
60 if (fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fcb
->subvol
, Irp
) &&
61 (fbi
->FileAttributes
== 0 || fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)) {
62 Status
= STATUS_ACCESS_DENIED
;
66 // don't allow readonly subvol to be made r/w if send operation running on it
67 if (fcb
->inode
== SUBVOL_ROOT_INODE
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
&&
68 fcb
->subvol
->send_ops
> 0) {
69 Status
= STATUS_DEVICE_NOT_READY
;
73 if (fbi
->CreationTime
.QuadPart
== -1)
74 ccb
->user_set_creation_time
= TRUE
;
75 else if (fbi
->CreationTime
.QuadPart
!= 0) {
76 win_time_to_unix(fbi
->CreationTime
, &fcb
->inode_item
.otime
);
77 inode_item_changed
= TRUE
;
78 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
80 ccb
->user_set_creation_time
= TRUE
;
83 if (fbi
->LastAccessTime
.QuadPart
== -1)
84 ccb
->user_set_access_time
= TRUE
;
85 else if (fbi
->LastAccessTime
.QuadPart
!= 0) {
86 win_time_to_unix(fbi
->LastAccessTime
, &fcb
->inode_item
.st_atime
);
87 inode_item_changed
= TRUE
;
88 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
90 ccb
->user_set_access_time
= TRUE
;
93 if (fbi
->LastWriteTime
.QuadPart
== -1)
94 ccb
->user_set_write_time
= TRUE
;
95 else if (fbi
->LastWriteTime
.QuadPart
!= 0) {
96 win_time_to_unix(fbi
->LastWriteTime
, &fcb
->inode_item
.st_mtime
);
97 inode_item_changed
= TRUE
;
98 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
100 ccb
->user_set_write_time
= TRUE
;
103 if (fbi
->ChangeTime
.QuadPart
== -1)
104 ccb
->user_set_change_time
= TRUE
;
105 else if (fbi
->ChangeTime
.QuadPart
!= 0) {
106 win_time_to_unix(fbi
->ChangeTime
, &fcb
->inode_item
.st_ctime
);
107 inode_item_changed
= TRUE
;
108 // no filter for this
110 ccb
->user_set_change_time
= TRUE
;
113 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
114 if (fbi
->FileAttributes
!= 0) {
118 fbi
->FileAttributes
&= ~FILE_ATTRIBUTE_NORMAL
;
120 defda
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fileref
&& fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.',
123 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
124 fbi
->FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
125 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
126 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
128 fcb
->atts_changed
= TRUE
;
130 if (fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)
131 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
133 if (defda
== fbi
->FileAttributes
)
134 fcb
->atts_deleted
= TRUE
;
135 else if (fcb
->inode
== SUBVOL_ROOT_INODE
&& (defda
| FILE_ATTRIBUTE_READONLY
) == (fbi
->FileAttributes
| FILE_ATTRIBUTE_READONLY
))
136 fcb
->atts_deleted
= TRUE
;
138 fcb
->atts
= fbi
->FileAttributes
;
140 KeQuerySystemTime(&time
);
141 win_time_to_unix(time
, &now
);
143 if (!ccb
->user_set_change_time
)
144 fcb
->inode_item
.st_ctime
= now
;
146 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
147 fcb
->subvol
->root_item
.ctime
= now
;
149 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
150 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
151 fcb
->subvol
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
153 fcb
->subvol
->root_item
.flags
&= ~BTRFS_SUBVOL_READONLY
;
156 inode_item_changed
= TRUE
;
158 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
161 if (inode_item_changed
) {
162 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
163 fcb
->inode_item
.sequence
++;
164 fcb
->inode_item_changed
= TRUE
;
170 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
172 Status
= STATUS_SUCCESS
;
175 ExReleaseResourceLite(fcb
->Header
.Resource
);
180 static NTSTATUS
set_disposition_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
181 FILE_DISPOSITION_INFORMATION
* fdi
= Irp
->AssociatedIrp
.SystemBuffer
;
182 fcb
* fcb
= FileObject
->FsContext
;
183 ccb
* ccb
= FileObject
->FsContext2
;
184 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
189 return STATUS_INVALID_PARAMETER
;
191 acquire_fcb_lock_exclusive(Vcb
);
193 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
195 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi
->DeleteFile
? "TRUE" : "FALSE", file_desc(FileObject
), fcb
);
199 atts
= fileref
->parent
->fcb
->atts
;
201 ERR("no fileref for stream\n");
202 Status
= STATUS_INTERNAL_ERROR
;
208 TRACE("atts = %x\n", atts
);
210 if (atts
& FILE_ATTRIBUTE_READONLY
) {
211 TRACE("not allowing readonly file to be deleted\n");
212 Status
= STATUS_CANNOT_DELETE
;
216 // FIXME - can we skip this bit for subvols?
217 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && (!fileref
|| fileref
->fcb
!= Vcb
->dummy_fcb
)) {
218 TRACE("directory not empty\n");
219 Status
= STATUS_DIRECTORY_NOT_EMPTY
;
223 if (!MmFlushImageSection(&fcb
->nonpaged
->segment_object
, MmFlushForDelete
)) {
224 TRACE("trying to delete file which is being mapped as an image\n");
225 Status
= STATUS_CANNOT_DELETE
;
229 ccb
->fileref
->delete_on_close
= fdi
->DeleteFile
;
231 FileObject
->DeletePending
= fdi
->DeleteFile
;
233 Status
= STATUS_SUCCESS
;
236 ExReleaseResourceLite(fcb
->Header
.Resource
);
238 release_fcb_lock(Vcb
);
240 // send notification that directory is about to be deleted
241 if (NT_SUCCESS(Status
) && fdi
->DeleteFile
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
242 FsRtlNotifyFullChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext
,
243 NULL
, FALSE
, FALSE
, 0, NULL
, NULL
, NULL
);
249 BOOL
has_open_children(file_ref
* fileref
) {
250 LIST_ENTRY
* le
= fileref
->children
.Flink
;
252 if (IsListEmpty(&fileref
->children
))
255 while (le
!= &fileref
->children
) {
256 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
258 if (c
->open_count
> 0)
261 if (has_open_children(c
))
270 static NTSTATUS
duplicate_fcb(fcb
* oldfcb
, fcb
** pfcb
) {
271 device_extension
* Vcb
= oldfcb
->Vcb
;
275 // FIXME - we can skip a lot of this if the inode is about to be deleted
277 fcb
= create_fcb(Vcb
, PagedPool
); // FIXME - what if we duplicate the paging file?
279 ERR("out of memory\n");
280 return STATUS_INSUFFICIENT_RESOURCES
;
285 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
286 fcb
->Header
.AllocationSize
= oldfcb
->Header
.AllocationSize
;
287 fcb
->Header
.FileSize
= oldfcb
->Header
.FileSize
;
288 fcb
->Header
.ValidDataLength
= oldfcb
->Header
.ValidDataLength
;
290 fcb
->type
= oldfcb
->type
;
294 fcb
->adshash
= oldfcb
->adshash
;
295 fcb
->adsmaxlen
= oldfcb
->adsmaxlen
;
297 if (oldfcb
->adsxattr
.Buffer
&& oldfcb
->adsxattr
.Length
> 0) {
298 fcb
->adsxattr
.Length
= oldfcb
->adsxattr
.Length
;
299 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
300 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
302 if (!fcb
->adsxattr
.Buffer
) {
303 ERR("out of memory\n");
305 return STATUS_INSUFFICIENT_RESOURCES
;
308 RtlCopyMemory(fcb
->adsxattr
.Buffer
, oldfcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
309 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
312 if (oldfcb
->adsdata
.Buffer
&& oldfcb
->adsdata
.Length
> 0) {
313 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= oldfcb
->adsdata
.Length
;
314 fcb
->adsdata
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsdata
.MaximumLength
, ALLOC_TAG
);
316 if (!fcb
->adsdata
.Buffer
) {
317 ERR("out of memory\n");
319 return STATUS_INSUFFICIENT_RESOURCES
;
322 RtlCopyMemory(fcb
->adsdata
.Buffer
, oldfcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
328 RtlCopyMemory(&fcb
->inode_item
, &oldfcb
->inode_item
, sizeof(INODE_ITEM
));
329 fcb
->inode_item_changed
= TRUE
;
331 if (oldfcb
->sd
&& RtlLengthSecurityDescriptor(oldfcb
->sd
) > 0) {
332 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, RtlLengthSecurityDescriptor(oldfcb
->sd
), ALLOC_TAG
);
334 ERR("out of memory\n");
336 return STATUS_INSUFFICIENT_RESOURCES
;
339 RtlCopyMemory(fcb
->sd
, oldfcb
->sd
, RtlLengthSecurityDescriptor(oldfcb
->sd
));
342 fcb
->atts
= oldfcb
->atts
;
344 le
= oldfcb
->extents
.Flink
;
345 while (le
!= &oldfcb
->extents
) {
346 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
349 extent
* ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
352 ERR("out of memory\n");
354 return STATUS_INSUFFICIENT_RESOURCES
;
357 ext2
->offset
= ext
->offset
;
358 ext2
->datalen
= ext
->datalen
;
360 if (ext2
->datalen
> 0)
361 RtlCopyMemory(&ext2
->extent_data
, &ext
->extent_data
, ext2
->datalen
);
363 ext2
->unique
= FALSE
;
364 ext2
->ignore
= FALSE
;
365 ext2
->inserted
= TRUE
;
369 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
371 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
)
372 len
= (ULONG
)ed2
->num_bytes
;
374 len
= (ULONG
)ed2
->size
;
376 len
= len
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
;
378 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
380 ERR("out of memory\n");
382 return STATUS_INSUFFICIENT_RESOURCES
;
385 RtlCopyMemory(ext2
->csum
, ext
->csum
, len
);
389 InsertTailList(&fcb
->extents
, &ext2
->list_entry
);
395 le
= oldfcb
->hardlinks
.Flink
;
396 while (le
!= &oldfcb
->hardlinks
) {
397 hardlink
*hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
), *hl2
;
399 hl2
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
402 ERR("out of memory\n");
404 return STATUS_INSUFFICIENT_RESOURCES
;
407 hl2
->parent
= hl
->parent
;
408 hl2
->index
= hl
->index
;
410 hl2
->name
.Length
= hl2
->name
.MaximumLength
= hl
->name
.Length
;
411 hl2
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->name
.MaximumLength
, ALLOC_TAG
);
413 if (!hl2
->name
.Buffer
) {
414 ERR("out of memory\n");
417 return STATUS_INSUFFICIENT_RESOURCES
;
420 RtlCopyMemory(hl2
->name
.Buffer
, hl
->name
.Buffer
, hl
->name
.Length
);
422 hl2
->utf8
.Length
= hl2
->utf8
.MaximumLength
= hl
->utf8
.Length
;
423 hl2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->utf8
.MaximumLength
, ALLOC_TAG
);
425 if (!hl2
->utf8
.Buffer
) {
426 ERR("out of memory\n");
427 ExFreePool(hl2
->name
.Buffer
);
430 return STATUS_INSUFFICIENT_RESOURCES
;
433 RtlCopyMemory(hl2
->utf8
.Buffer
, hl
->utf8
.Buffer
, hl
->utf8
.Length
);
435 InsertTailList(&fcb
->hardlinks
, &hl2
->list_entry
);
440 if (oldfcb
->reparse_xattr
.Buffer
&& oldfcb
->reparse_xattr
.Length
> 0) {
441 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= oldfcb
->reparse_xattr
.Length
;
443 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.MaximumLength
, ALLOC_TAG
);
444 if (!fcb
->reparse_xattr
.Buffer
) {
445 ERR("out of memory\n");
447 return STATUS_INSUFFICIENT_RESOURCES
;
450 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, oldfcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
453 if (oldfcb
->ea_xattr
.Buffer
&& oldfcb
->ea_xattr
.Length
> 0) {
454 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= oldfcb
->ea_xattr
.Length
;
456 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->ea_xattr
.MaximumLength
, ALLOC_TAG
);
457 if (!fcb
->ea_xattr
.Buffer
) {
458 ERR("out of memory\n");
460 return STATUS_INSUFFICIENT_RESOURCES
;
463 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, oldfcb
->ea_xattr
.Buffer
, fcb
->ea_xattr
.Length
);
466 fcb
->prop_compression
= oldfcb
->prop_compression
;
468 le
= oldfcb
->xattrs
.Flink
;
469 while (le
!= &oldfcb
->xattrs
) {
470 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
472 if (xa
->valuelen
> 0) {
475 xa2
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
, ALLOC_TAG
);
478 ERR("out of memory\n");
480 return STATUS_INSUFFICIENT_RESOURCES
;
483 xa2
->namelen
= xa
->namelen
;
484 xa2
->valuelen
= xa
->valuelen
;
485 xa2
->dirty
= xa
->dirty
;
486 memcpy(xa2
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
488 InsertTailList(&fcb
->xattrs
, &xa2
->list_entry
);
497 return STATUS_SUCCESS
;
500 typedef struct _move_entry
{
503 file_ref
* dummyfileref
;
504 struct _move_entry
* parent
;
505 LIST_ENTRY list_entry
;
508 static NTSTATUS
add_children_to_move_list(device_extension
* Vcb
, move_entry
* me
, PIRP Irp
) {
512 ExAcquireResourceSharedLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
514 le
= me
->fileref
->fcb
->dir_children_index
.Flink
;
516 while (le
!= &me
->fileref
->fcb
->dir_children_index
) {
517 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
521 Status
= open_fileref_child(Vcb
, me
->fileref
, &dc
->name
, TRUE
, TRUE
, dc
->index
== 0 ? TRUE
: FALSE
, PagedPool
, &fr
, Irp
);
523 if (!NT_SUCCESS(Status
)) {
524 ERR("open_fileref_child returned %08x\n", Status
);
525 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
529 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
531 ERR("out of memory\n");
532 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
533 return STATUS_INSUFFICIENT_RESOURCES
;
537 me2
->dummyfcb
= NULL
;
538 me2
->dummyfileref
= NULL
;
541 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
546 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
548 return STATUS_SUCCESS
;
551 void remove_dir_child_from_hash_lists(fcb
* fcb
, dir_child
* dc
) {
556 if (fcb
->hash_ptrs
[c
] == &dc
->list_entry_hash
) {
557 if (dc
->list_entry_hash
.Flink
== &fcb
->dir_children_hash
)
558 fcb
->hash_ptrs
[c
] = NULL
;
560 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash
.Flink
, dir_child
, list_entry_hash
);
562 if (dc2
->hash
>> 24 == c
)
563 fcb
->hash_ptrs
[c
] = &dc2
->list_entry_hash
;
565 fcb
->hash_ptrs
[c
] = NULL
;
569 RemoveEntryList(&dc
->list_entry_hash
);
571 c
= dc
->hash_uc
>> 24;
573 if (fcb
->hash_ptrs_uc
[c
] == &dc
->list_entry_hash_uc
) {
574 if (dc
->list_entry_hash_uc
.Flink
== &fcb
->dir_children_hash_uc
)
575 fcb
->hash_ptrs_uc
[c
] = NULL
;
577 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash_uc
.Flink
, dir_child
, list_entry_hash_uc
);
579 if (dc2
->hash_uc
>> 24 == c
)
580 fcb
->hash_ptrs_uc
[c
] = &dc2
->list_entry_hash_uc
;
582 fcb
->hash_ptrs_uc
[c
] = NULL
;
586 RemoveEntryList(&dc
->list_entry_hash_uc
);
589 static NTSTATUS
create_directory_fcb(device_extension
* Vcb
, root
* r
, fcb
* parfcb
, fcb
** pfcb
) {
592 SECURITY_SUBJECT_CONTEXT subjcont
;
598 fcb
= create_fcb(Vcb
, PagedPool
);
600 ERR("out of memory\n");
601 return STATUS_INSUFFICIENT_RESOURCES
;
604 KeQuerySystemTime(&time
);
605 win_time_to_unix(time
, &now
);
610 fcb
->inode
= InterlockedIncrement64(&r
->lastinode
);
611 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
613 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
614 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
615 fcb
->inode_item
.st_nlink
= 1;
616 fcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(parfcb
, TRUE
);
617 fcb
->inode_item
.st_atime
= fcb
->inode_item
.st_ctime
= fcb
->inode_item
.st_mtime
= fcb
->inode_item
.otime
= now
;
618 fcb
->inode_item
.st_gid
= GID_NOBODY
;
620 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, FALSE
, TRUE
, NULL
);
622 SeCaptureSubjectContext(&subjcont
);
624 Status
= SeAssignSecurity(parfcb
->sd
, NULL
, (void**)&fcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
626 if (!NT_SUCCESS(Status
)) {
627 ERR("SeAssignSecurity returned %08x\n", Status
);
632 ERR("SeAssignSecurity returned NULL security descriptor\n");
633 return STATUS_INTERNAL_ERROR
;
636 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
637 if (!NT_SUCCESS(Status
)) {
638 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
639 fcb
->inode_item
.st_uid
= UID_NOBODY
;
640 fcb
->sd_dirty
= TRUE
;
642 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
643 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
646 find_gid(fcb
, parfcb
, &subjcont
);
648 fcb
->inode_item_changed
= TRUE
;
650 InsertTailList(&r
->fcbs
, &fcb
->list_entry
);
651 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
653 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
654 fcb
->Header
.AllocationSize
.QuadPart
= 0;
655 fcb
->Header
.FileSize
.QuadPart
= 0;
656 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
661 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
662 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
664 fcb
->prop_compression
= parfcb
->prop_compression
;
665 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
667 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
668 if (!fcb
->hash_ptrs
) {
669 ERR("out of memory\n");
670 return STATUS_INSUFFICIENT_RESOURCES
;
673 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
675 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
676 if (!fcb
->hash_ptrs_uc
) {
677 ERR("out of memory\n");
678 return STATUS_INSUFFICIENT_RESOURCES
;
681 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
685 return STATUS_SUCCESS
;
688 static NTSTATUS
move_across_subvols(file_ref
* fileref
, ccb
* ccb
, file_ref
* destdir
, PANSI_STRING utf8
, PUNICODE_STRING fnus
, PIRP Irp
, LIST_ENTRY
* rollback
) {
690 LIST_ENTRY move_list
, *le
;
694 file_ref
* origparent
;
696 InitializeListHead(&move_list
);
698 KeQuerySystemTime(&time
);
699 win_time_to_unix(time
, &now
);
701 me
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
704 ERR("out of memory\n");
705 Status
= STATUS_INSUFFICIENT_RESOURCES
;
709 origparent
= fileref
->parent
;
711 me
->fileref
= fileref
;
712 increase_fileref_refcount(me
->fileref
);
714 me
->dummyfileref
= NULL
;
717 InsertTailList(&move_list
, &me
->list_entry
);
719 le
= move_list
.Flink
;
720 while (le
!= &move_list
) {
721 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
723 ExAcquireResourceSharedLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
725 if (!me
->fileref
->fcb
->ads
&& me
->fileref
->fcb
->subvol
== origparent
->fcb
->subvol
) {
726 Status
= add_children_to_move_list(fileref
->fcb
->Vcb
, me
, Irp
);
728 if (!NT_SUCCESS(Status
)) {
729 ERR("add_children_to_move_list returned %08x\n", Status
);
734 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
739 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
741 // loop through list and create new inodes
743 le
= move_list
.Flink
;
744 while (le
!= &move_list
) {
745 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
747 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
&& me
->fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) {
750 BOOL inserted
= FALSE
;
753 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
755 Status
= duplicate_fcb(me
->fileref
->fcb
, &me
->dummyfcb
);
756 if (!NT_SUCCESS(Status
)) {
757 ERR("duplicate_fcb returned %08x\n", Status
);
758 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
762 me
->dummyfcb
->subvol
= me
->fileref
->fcb
->subvol
;
763 me
->dummyfcb
->inode
= me
->fileref
->fcb
->inode
;
765 if (!me
->dummyfcb
->ads
) {
766 me
->dummyfcb
->sd_dirty
= me
->fileref
->fcb
->sd_dirty
;
767 me
->dummyfcb
->atts_changed
= me
->fileref
->fcb
->atts_changed
;
768 me
->dummyfcb
->atts_deleted
= me
->fileref
->fcb
->atts_deleted
;
769 me
->dummyfcb
->extents_changed
= me
->fileref
->fcb
->extents_changed
;
770 me
->dummyfcb
->reparse_xattr_changed
= me
->fileref
->fcb
->reparse_xattr_changed
;
771 me
->dummyfcb
->ea_changed
= me
->fileref
->fcb
->ea_changed
;
774 me
->dummyfcb
->created
= me
->fileref
->fcb
->created
;
775 me
->dummyfcb
->deleted
= me
->fileref
->fcb
->deleted
;
776 mark_fcb_dirty(me
->dummyfcb
);
778 if (!me
->fileref
->fcb
->ads
) {
781 me
->fileref
->fcb
->subvol
= destdir
->fcb
->subvol
;
782 me
->fileref
->fcb
->inode
= InterlockedIncrement64(&destdir
->fcb
->subvol
->lastinode
);
783 me
->fileref
->fcb
->inode_item
.st_nlink
= 1;
785 defda
= get_file_attributes(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, me
->fileref
->fcb
->inode
,
786 me
->fileref
->fcb
->type
, me
->fileref
->dc
&& me
->fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && me
->fileref
->dc
->name
.Buffer
[0] == '.',
789 me
->fileref
->fcb
->sd_dirty
= !!me
->fileref
->fcb
->sd
;
790 me
->fileref
->fcb
->atts_changed
= defda
!= me
->fileref
->fcb
->atts
;
791 me
->fileref
->fcb
->extents_changed
= !IsListEmpty(&me
->fileref
->fcb
->extents
);
792 me
->fileref
->fcb
->reparse_xattr_changed
= !!me
->fileref
->fcb
->reparse_xattr
.Buffer
;
793 me
->fileref
->fcb
->ea_changed
= !!me
->fileref
->fcb
->ea_xattr
.Buffer
;
794 me
->fileref
->fcb
->xattrs_changed
= !IsListEmpty(&me
->fileref
->fcb
->xattrs
);
795 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
797 le2
= me
->fileref
->fcb
->xattrs
.Flink
;
798 while (le2
!= &me
->fileref
->fcb
->xattrs
) {
799 xattr
* xa
= CONTAINING_RECORD(le2
, xattr
, list_entry
);
806 if (le
== move_list
.Flink
) { // first entry
807 me
->fileref
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
808 me
->fileref
->fcb
->inode_item
.sequence
++;
810 if (!ccb
->user_set_change_time
)
811 me
->fileref
->fcb
->inode_item
.st_ctime
= now
;
814 le2
= me
->fileref
->fcb
->extents
.Flink
;
815 while (le2
!= &me
->fileref
->fcb
->extents
) {
816 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
818 if (!ext
->ignore
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
819 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
821 if (ed2
->size
!= 0) {
822 chunk
* c
= get_chunk_from_address(me
->fileref
->fcb
->Vcb
, ed2
->address
);
825 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
827 Status
= update_changed_extent_ref(me
->fileref
->fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, me
->fileref
->fcb
->subvol
->id
, me
->fileref
->fcb
->inode
,
828 ext
->offset
- ed2
->offset
, 1, me
->fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
830 if (!NT_SUCCESS(Status
)) {
831 ERR("update_changed_extent_ref returned %08x\n", Status
);
832 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
843 me
->fileref
->fcb
->subvol
= me
->parent
->fileref
->fcb
->subvol
;
844 me
->fileref
->fcb
->inode
= me
->parent
->fileref
->fcb
->inode
;
847 me
->fileref
->fcb
->created
= TRUE
;
849 InsertHeadList(&me
->fileref
->fcb
->list_entry
, &me
->dummyfcb
->list_entry
);
850 RemoveEntryList(&me
->fileref
->fcb
->list_entry
);
852 le3
= destdir
->fcb
->subvol
->fcbs
.Flink
;
853 while (le3
!= &destdir
->fcb
->subvol
->fcbs
) {
854 fcb
* fcb
= CONTAINING_RECORD(le3
, struct _fcb
, list_entry
);
856 if (fcb
->inode
> me
->fileref
->fcb
->inode
) {
857 InsertHeadList(le3
->Blink
, &me
->fileref
->fcb
->list_entry
);
866 InsertTailList(&destdir
->fcb
->subvol
->fcbs
, &me
->fileref
->fcb
->list_entry
);
868 InsertTailList(&me
->fileref
->fcb
->Vcb
->all_fcbs
, &me
->dummyfcb
->list_entry_all
);
870 while (!IsListEmpty(&me
->fileref
->fcb
->hardlinks
)) {
871 hardlink
* hl
= CONTAINING_RECORD(RemoveHeadList(&me
->fileref
->fcb
->hardlinks
), hardlink
, list_entry
);
874 ExFreePool(hl
->name
.Buffer
);
877 ExFreePool(hl
->utf8
.Buffer
);
882 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
883 mark_fcb_dirty(me
->fileref
->fcb
);
885 if ((!me
->dummyfcb
->ads
&& me
->dummyfcb
->inode_item
.st_nlink
> 1) || (me
->dummyfcb
->ads
&& me
->parent
->dummyfcb
->inode_item
.st_nlink
> 1)) {
886 LIST_ENTRY
* le2
= le
->Flink
;
888 while (le2
!= &move_list
) {
889 move_entry
* me2
= CONTAINING_RECORD(le2
, move_entry
, list_entry
);
891 if (me2
->fileref
->fcb
== me
->fileref
->fcb
&& !me2
->fileref
->fcb
->ads
) {
892 me2
->dummyfcb
= me
->dummyfcb
;
893 InterlockedIncrement(&me
->dummyfcb
->refcount
);
900 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
902 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
903 me
->fileref
->fcb
->inode_item
.st_nlink
++;
904 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
905 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
912 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
913 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
915 // loop through list and create new filerefs
917 le
= move_list
.Flink
;
918 while (le
!= &move_list
) {
920 BOOL name_changed
= FALSE
;
922 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
924 me
->dummyfileref
= create_fileref(fileref
->fcb
->Vcb
);
925 if (!me
->dummyfileref
) {
926 ERR("out of memory\n");
927 Status
= STATUS_INSUFFICIENT_RESOURCES
;
931 if (me
->fileref
->fcb
== me
->fileref
->fcb
->Vcb
->dummy_fcb
) {
932 root
* r
= me
->parent
? me
->parent
->fileref
->fcb
->subvol
: destdir
->fcb
->subvol
;
934 Status
= create_directory_fcb(me
->fileref
->fcb
->Vcb
, r
, me
->fileref
->parent
->fcb
, &me
->fileref
->fcb
);
935 if (!NT_SUCCESS(Status
)) {
936 ERR("create_directory_fcb returnd %08x\n", Status
);
940 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
941 me
->fileref
->dc
->key
.obj_type
= TYPE_INODE_ITEM
;
943 me
->dummyfileref
->fcb
= me
->fileref
->fcb
->Vcb
->dummy_fcb
;
944 } else if (me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) {
945 me
->dummyfileref
->fcb
= me
->fileref
->fcb
;
947 me
->fileref
->fcb
->subvol
->parent
= le
== move_list
.Flink
? destdir
->fcb
->subvol
->id
: me
->parent
->fileref
->fcb
->subvol
->id
;
949 me
->dummyfileref
->fcb
= me
->dummyfcb
;
951 InterlockedIncrement(&me
->dummyfileref
->fcb
->refcount
);
953 me
->dummyfileref
->oldutf8
= me
->fileref
->oldutf8
;
954 me
->dummyfileref
->oldindex
= me
->fileref
->dc
->index
;
956 if (le
== move_list
.Flink
&& (me
->fileref
->dc
->utf8
.Length
!= utf8
->Length
|| RtlCompareMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
) != utf8
->Length
))
959 if ((le
== move_list
.Flink
|| me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) && !me
->dummyfileref
->oldutf8
.Buffer
) {
960 me
->dummyfileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
961 if (!me
->dummyfileref
->oldutf8
.Buffer
) {
962 ERR("out of memory\n");
963 Status
= STATUS_INSUFFICIENT_RESOURCES
;
967 RtlCopyMemory(me
->dummyfileref
->oldutf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
969 me
->dummyfileref
->oldutf8
.Length
= me
->dummyfileref
->oldutf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
972 me
->dummyfileref
->delete_on_close
= me
->fileref
->delete_on_close
;
973 me
->dummyfileref
->deleted
= me
->fileref
->deleted
;
975 me
->dummyfileref
->created
= me
->fileref
->created
;
976 me
->fileref
->created
= TRUE
;
978 me
->dummyfileref
->parent
= me
->parent
? me
->parent
->dummyfileref
: origparent
;
979 increase_fileref_refcount(me
->dummyfileref
->parent
);
981 ExAcquireResourceExclusiveLite(&me
->dummyfileref
->parent
->nonpaged
->children_lock
, TRUE
);
982 InsertTailList(&me
->dummyfileref
->parent
->children
, &me
->dummyfileref
->list_entry
);
983 ExReleaseResourceLite(&me
->dummyfileref
->parent
->nonpaged
->children_lock
);
985 me
->dummyfileref
->debug_desc
= me
->fileref
->debug_desc
;
987 if (me
->dummyfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
988 me
->dummyfileref
->fcb
->fileref
= me
->dummyfileref
;
991 RemoveEntryList(&me
->fileref
->list_entry
);
993 increase_fileref_refcount(destdir
);
995 if (me
->fileref
->dc
) {
996 // remove from old parent
997 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
998 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
999 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1000 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1002 me
->fileref
->parent
->fcb
->inode_item
.st_size
-= me
->fileref
->dc
->utf8
.Length
* 2;
1003 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1004 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1005 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1006 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1007 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1008 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1011 ExFreePool(me
->fileref
->dc
->utf8
.Buffer
);
1012 ExFreePool(me
->fileref
->dc
->name
.Buffer
);
1013 ExFreePool(me
->fileref
->dc
->name_uc
.Buffer
);
1015 me
->fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1016 if (!me
->fileref
->dc
->utf8
.Buffer
) {
1017 ERR("out of memory\n");
1018 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1022 me
->fileref
->dc
->utf8
.Length
= me
->fileref
->dc
->utf8
.MaximumLength
= utf8
->Length
;
1023 RtlCopyMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1025 me
->fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
->Length
, ALLOC_TAG
);
1026 if (!me
->fileref
->dc
->name
.Buffer
) {
1027 ERR("out of memory\n");
1028 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1032 me
->fileref
->dc
->name
.Length
= me
->fileref
->dc
->name
.MaximumLength
= fnus
->Length
;
1033 RtlCopyMemory(me
->fileref
->dc
->name
.Buffer
, fnus
->Buffer
, fnus
->Length
);
1035 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1036 if (!NT_SUCCESS(Status
)) {
1037 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1041 me
->fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1042 me
->fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name_uc
.Buffer
, me
->fileref
->dc
->name_uc
.Length
);
1045 if (me
->fileref
->dc
->key
.obj_type
== TYPE_INODE_ITEM
)
1046 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1048 // add to new parent
1050 ExAcquireResourceExclusiveLite(&destdir
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1052 if (IsListEmpty(&destdir
->fcb
->dir_children_index
))
1053 me
->fileref
->dc
->index
= 2;
1055 dir_child
* dc2
= CONTAINING_RECORD(destdir
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1057 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1060 InsertTailList(&destdir
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1061 insert_dir_child_into_hash_lists(destdir
->fcb
, me
->fileref
->dc
);
1062 ExReleaseResourceLite(&destdir
->fcb
->nonpaged
->dir_children_lock
);
1065 free_fileref(fileref
->fcb
->Vcb
, me
->fileref
->parent
);
1066 me
->fileref
->parent
= destdir
;
1068 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->nonpaged
->children_lock
, TRUE
);
1069 InsertTailList(&me
->fileref
->parent
->children
, &me
->fileref
->list_entry
);
1070 ExReleaseResourceLite(&me
->fileref
->parent
->nonpaged
->children_lock
);
1072 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
);
1073 me
->fileref
->parent
->fcb
->inode_item
.st_size
+= me
->fileref
->dc
->utf8
.Length
* 2;
1074 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
);
1075 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1076 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1077 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1078 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1079 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1080 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1082 if (me
->fileref
->dc
) {
1083 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1084 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1086 if (!me
->fileref
->fcb
->ads
)
1087 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1089 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1091 ExAcquireResourceExclusiveLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1093 if (me
->fileref
->fcb
->ads
)
1094 InsertHeadList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1096 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
)
1097 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1099 if (IsListEmpty(&me
->parent
->fileref
->fcb
->dir_children_index
))
1100 me
->fileref
->dc
->index
= 2;
1102 dir_child
* dc2
= CONTAINING_RECORD(me
->parent
->fileref
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1104 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1107 InsertTailList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1108 insert_dir_child_into_hash_lists(me
->parent
->fileref
->fcb
, me
->fileref
->dc
);
1111 ExReleaseResourceLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
);
1115 if (!me
->dummyfileref
->fcb
->ads
) {
1116 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1117 if (!NT_SUCCESS(Status
)) {
1118 ERR("delete_fileref returned %08x\n", Status
);
1123 if (me
->fileref
->fcb
->inode_item
.st_nlink
> 1) {
1124 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1126 ERR("out of memory\n");
1127 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1131 hl
->parent
= me
->fileref
->parent
->fcb
->inode
;
1132 hl
->index
= me
->fileref
->dc
->index
;
1134 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
1135 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1136 if (!hl
->utf8
.Buffer
) {
1137 ERR("out of memory\n");
1138 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1143 RtlCopyMemory(hl
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
1145 hl
->name
.Length
= hl
->name
.MaximumLength
= me
->fileref
->dc
->name
.Length
;
1146 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1147 if (!hl
->name
.Buffer
) {
1148 ERR("out of memory\n");
1149 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1150 ExFreePool(hl
->utf8
.Buffer
);
1155 RtlCopyMemory(hl
->name
.Buffer
, me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1157 InsertTailList(&me
->fileref
->fcb
->hardlinks
, &hl
->list_entry
);
1160 mark_fileref_dirty(me
->fileref
);
1165 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1167 le
= move_list
.Flink
;
1168 while (le
!= &move_list
) {
1169 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1171 if (me
->dummyfileref
->fcb
->ads
&& me
->parent
->dummyfileref
->fcb
->deleted
) {
1172 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1173 if (!NT_SUCCESS(Status
)) {
1174 ERR("delete_fileref returned %08x\n", Status
);
1182 destdir
->fcb
->subvol
->root_item
.ctransid
= destdir
->fcb
->Vcb
->superblock
.generation
;
1183 destdir
->fcb
->subvol
->root_item
.ctime
= now
;
1185 me
= CONTAINING_RECORD(move_list
.Flink
, move_entry
, list_entry
);
1186 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1187 send_notification_fileref(me
->dummyfileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1188 send_notification_fileref(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1190 Status
= STATUS_SUCCESS
;
1193 while (!IsListEmpty(&move_list
)) {
1194 le
= RemoveHeadList(&move_list
);
1195 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1198 free_fcb(fileref
->fcb
->Vcb
, me
->dummyfcb
);
1200 if (me
->dummyfileref
)
1201 free_fileref(fileref
->fcb
->Vcb
, me
->dummyfileref
);
1203 free_fileref(fileref
->fcb
->Vcb
, me
->fileref
);
1211 void insert_dir_child_into_hash_lists(fcb
* fcb
, dir_child
* dc
) {
1222 le
= fcb
->hash_ptrs
[d
];
1231 le
= fcb
->dir_children_hash
.Flink
;
1233 while (le
!= &fcb
->dir_children_hash
) {
1234 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
1236 if (dc2
->hash
> dc
->hash
) {
1237 InsertHeadList(le
->Blink
, &dc
->list_entry_hash
);
1246 InsertTailList(&fcb
->dir_children_hash
, &dc
->list_entry_hash
);
1248 if (!fcb
->hash_ptrs
[c
])
1249 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1251 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs
[c
], dir_child
, list_entry_hash
);
1253 if (dc2
->hash
> dc
->hash
)
1254 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1257 c
= dc
->hash_uc
>> 24;
1263 le
= fcb
->hash_ptrs_uc
[d
];
1272 le
= fcb
->dir_children_hash_uc
.Flink
;
1274 while (le
!= &fcb
->dir_children_hash_uc
) {
1275 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
1277 if (dc2
->hash_uc
> dc
->hash_uc
) {
1278 InsertHeadList(le
->Blink
, &dc
->list_entry_hash_uc
);
1287 InsertTailList(&fcb
->dir_children_hash_uc
, &dc
->list_entry_hash_uc
);
1289 if (!fcb
->hash_ptrs_uc
[c
])
1290 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1292 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs_uc
[c
], dir_child
, list_entry_hash_uc
);
1294 if (dc2
->hash_uc
> dc
->hash_uc
)
1295 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1299 static NTSTATUS
set_rename_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
1300 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1301 FILE_RENAME_INFORMATION
* fri
= Irp
->AssociatedIrp
.SystemBuffer
;
1302 fcb
*fcb
= FileObject
->FsContext
;
1303 ccb
* ccb
= FileObject
->FsContext2
;
1304 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
1306 ULONG fnlen
, utf8len
, origutf8len
;
1307 UNICODE_STRING fnus
;
1312 LIST_ENTRY rollback
, *le
;
1314 SECURITY_SUBJECT_CONTEXT subjcont
;
1317 InitializeListHead(&rollback
);
1319 TRACE("tfo = %p\n", tfo
);
1320 TRACE("ReplaceIfExists = %u\n", IrpSp
->Parameters
.SetFile
.ReplaceIfExists
);
1321 TRACE("RootDirectory = %p\n", fri
->RootDirectory
);
1322 TRACE("FileName = %.*S\n", fri
->FileNameLength
/ sizeof(WCHAR
), fri
->FileName
);
1325 fnlen
= fri
->FileNameLength
/ sizeof(WCHAR
);
1328 if (!fileref
|| !fileref
->parent
) {
1329 ERR("no fileref set and no directory given\n");
1330 return STATUS_INVALID_PARAMETER
;
1335 while (fnlen
> 0 && (fri
->FileName
[fnlen
- 1] == '/' || fri
->FileName
[fnlen
- 1] == '\\'))
1339 return STATUS_INVALID_PARAMETER
;
1341 for (i
= fnlen
- 1; i
>= 0; i
--) {
1342 if (fri
->FileName
[i
] == '\\' || fri
->FileName
[i
] == '/') {
1343 fn
= &fri
->FileName
[i
+1];
1344 fnlen
= (fri
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
1350 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1351 acquire_fcb_lock_exclusive(Vcb
);
1352 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1355 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1356 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1357 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1358 Status
= STATUS_INVALID_PARAMETER
;
1363 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
1365 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1367 origutf8len
= fileref
->dc
->utf8
.Length
;
1369 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1370 if (!NT_SUCCESS(Status
))
1373 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
1374 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
1376 ERR("out of memory\n");
1377 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1381 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1382 if (!NT_SUCCESS(Status
))
1385 if (tfo
&& tfo
->FsContext2
) {
1386 struct _ccb
* relatedccb
= tfo
->FsContext2
;
1388 related
= relatedccb
->fileref
;
1389 increase_fileref_refcount(related
);
1390 } else if (fnus
.Length
>= sizeof(WCHAR
) && fnus
.Buffer
[0] != '\\') {
1391 related
= fileref
->parent
;
1392 increase_fileref_refcount(related
);
1395 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1397 if (NT_SUCCESS(Status
)) {
1398 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref
));
1400 if (fileref
!= oldfileref
&& !oldfileref
->deleted
) {
1401 if (!IrpSp
->Parameters
.SetFile
.ReplaceIfExists
) {
1402 Status
= STATUS_OBJECT_NAME_COLLISION
;
1404 } else if ((oldfileref
->open_count
>= 1 || has_open_children(oldfileref
)) && !oldfileref
->deleted
) {
1405 WARN("trying to overwrite open file\n");
1406 Status
= STATUS_ACCESS_DENIED
;
1410 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1411 WARN("trying to overwrite directory\n");
1412 Status
= STATUS_ACCESS_DENIED
;
1417 if (fileref
== oldfileref
|| oldfileref
->deleted
) {
1418 free_fileref(Vcb
, oldfileref
);
1424 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1426 if (!NT_SUCCESS(Status
)) {
1427 ERR("open_fileref returned %08x\n", Status
);
1432 if (related
->fcb
== Vcb
->dummy_fcb
) {
1433 Status
= STATUS_ACCESS_DENIED
;
1437 SeCaptureSubjectContext(&subjcont
);
1439 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
1440 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1441 SeReleaseSubjectContext(&subjcont
);
1442 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1446 SeReleaseSubjectContext(&subjcont
);
1448 if (has_open_children(fileref
)) {
1449 WARN("trying to rename file with open children\n");
1450 Status
= STATUS_ACCESS_DENIED
;
1455 SeCaptureSubjectContext(&subjcont
);
1457 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
1458 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1459 SeReleaseSubjectContext(&subjcont
);
1460 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1464 SeReleaseSubjectContext(&subjcont
);
1466 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
1467 if (!NT_SUCCESS(Status
)) {
1468 ERR("delete_fileref returned %08x\n", Status
);
1473 if (fileref
->parent
->fcb
->subvol
!= related
->fcb
->subvol
&& (fileref
->fcb
->subvol
== fileref
->parent
->fcb
->subvol
|| fileref
->fcb
== Vcb
->dummy_fcb
)) {
1474 Status
= move_across_subvols(fileref
, ccb
, related
, &utf8
, &fnus
, Irp
, &rollback
);
1475 if (!NT_SUCCESS(Status
)) {
1476 ERR("move_across_subvols returned %08x\n", Status
);
1481 if (related
== fileref
->parent
) { // keeping file in same directory
1482 UNICODE_STRING oldfn
, newfn
;
1484 ULONG reqlen
, oldutf8len
;
1486 oldfn
.Length
= oldfn
.MaximumLength
= 0;
1488 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1489 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1490 ERR("fileref_get_filename returned %08x\n", Status
);
1494 oldfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1495 if (!oldfn
.Buffer
) {
1496 ERR("out of memory\n");
1497 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1501 oldfn
.MaximumLength
= (UINT16
)reqlen
;
1503 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1504 if (!NT_SUCCESS(Status
)) {
1505 ERR("fileref_get_filename returned %08x\n", Status
);
1506 ExFreePool(oldfn
.Buffer
);
1510 oldutf8len
= fileref
->dc
->utf8
.Length
;
1512 if (!fileref
->created
&& !fileref
->oldutf8
.Buffer
) {
1513 fileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1514 if (!fileref
->oldutf8
.Buffer
) {
1515 ERR("out of memory\n");
1516 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1520 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1521 RtlCopyMemory(fileref
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1524 TRACE("renaming %.*S to %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
, fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1526 mark_fileref_dirty(fileref
);
1529 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1531 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1532 ExFreePool(fileref
->dc
->name
.Buffer
);
1533 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1535 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1536 if (!fileref
->dc
->utf8
.Buffer
) {
1537 ERR("out of memory\n");
1538 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1539 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1540 ExFreePool(oldfn
.Buffer
);
1544 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1545 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1547 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1548 if (!fileref
->dc
->name
.Buffer
) {
1549 ERR("out of memory\n");
1550 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1551 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1552 ExFreePool(oldfn
.Buffer
);
1556 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1557 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1559 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1560 if (!NT_SUCCESS(Status
)) {
1561 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1562 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1563 ExFreePool(oldfn
.Buffer
);
1567 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1569 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1570 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1572 insert_dir_child_into_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1574 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1577 newfn
.Length
= newfn
.MaximumLength
= 0;
1579 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1580 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1581 ERR("fileref_get_filename returned %08x\n", Status
);
1582 ExFreePool(oldfn
.Buffer
);
1586 newfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1587 if (!newfn
.Buffer
) {
1588 ERR("out of memory\n");
1589 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1590 ExFreePool(oldfn
.Buffer
);
1594 newfn
.MaximumLength
= (UINT16
)reqlen
;
1596 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1597 if (!NT_SUCCESS(Status
)) {
1598 ERR("fileref_get_filename returned %08x\n", Status
);
1599 ExFreePool(oldfn
.Buffer
);
1600 ExFreePool(newfn
.Buffer
);
1604 KeQuerySystemTime(&time
);
1605 win_time_to_unix(time
, &now
);
1607 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1608 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1609 fcb
->inode_item
.sequence
++;
1611 if (!ccb
->user_set_change_time
)
1612 fcb
->inode_item
.st_ctime
= now
;
1614 fcb
->inode_item_changed
= TRUE
;
1615 mark_fcb_dirty(fcb
);
1618 // update parent's INODE_ITEM
1620 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1621 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1622 related
->fcb
->inode_item
.st_size
= related
->fcb
->inode_item
.st_size
+ (2 * utf8
.Length
) - (2* oldutf8len
);
1623 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1624 related
->fcb
->inode_item
.sequence
++;
1625 related
->fcb
->inode_item
.st_ctime
= now
;
1626 related
->fcb
->inode_item
.st_mtime
= now
;
1628 related
->fcb
->inode_item_changed
= TRUE
;
1629 mark_fcb_dirty(related
->fcb
);
1630 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1632 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&oldfn
, name_offset
, NULL
, NULL
,
1633 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_OLD_NAME
, NULL
, NULL
);
1634 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&newfn
, name_offset
, NULL
, NULL
,
1635 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_NEW_NAME
, NULL
, NULL
);
1637 ExFreePool(oldfn
.Buffer
);
1638 ExFreePool(newfn
.Buffer
);
1640 Status
= STATUS_SUCCESS
;
1644 // We move files by moving the existing fileref to the new directory, and
1645 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1647 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
1649 fr2
= create_fileref(Vcb
);
1651 fr2
->fcb
= fileref
->fcb
;
1652 fr2
->fcb
->refcount
++;
1654 fr2
->oldutf8
= fileref
->oldutf8
;
1655 fr2
->oldindex
= fileref
->dc
->index
;
1656 fr2
->delete_on_close
= fileref
->delete_on_close
;
1657 fr2
->deleted
= TRUE
;
1658 fr2
->created
= fileref
->created
;
1659 fr2
->parent
= fileref
->parent
;
1662 if (!fr2
->oldutf8
.Buffer
) {
1663 fr2
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1664 if (!fr2
->oldutf8
.Buffer
) {
1665 ERR("out of memory\n");
1666 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1670 RtlCopyMemory(fr2
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1672 fr2
->oldutf8
.Length
= fr2
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1675 if (fr2
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1676 fr2
->fcb
->fileref
= fr2
;
1678 if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
)
1679 fileref
->fcb
->subvol
->parent
= related
->fcb
->subvol
->id
;
1681 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= 0;
1682 fileref
->oldutf8
.Buffer
= NULL
;
1683 fileref
->deleted
= FALSE
;
1684 fileref
->created
= TRUE
;
1685 fileref
->parent
= related
;
1687 ExAcquireResourceExclusiveLite(&fileref
->parent
->nonpaged
->children_lock
, TRUE
);
1688 InsertHeadList(&fileref
->list_entry
, &fr2
->list_entry
);
1689 RemoveEntryList(&fileref
->list_entry
);
1690 ExReleaseResourceLite(&fileref
->parent
->nonpaged
->children_lock
);
1692 mark_fileref_dirty(fr2
);
1693 mark_fileref_dirty(fileref
);
1696 // remove from old parent
1697 ExAcquireResourceExclusiveLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1698 RemoveEntryList(&fileref
->dc
->list_entry_index
);
1699 remove_dir_child_from_hash_lists(fr2
->parent
->fcb
, fileref
->dc
);
1700 ExReleaseResourceLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
);
1702 if (fileref
->dc
->utf8
.Length
!= utf8
.Length
|| RtlCompareMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
) != utf8
.Length
) {
1703 // handle changed name
1705 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1706 ExFreePool(fileref
->dc
->name
.Buffer
);
1707 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1709 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1710 if (!fileref
->dc
->utf8
.Buffer
) {
1711 ERR("out of memory\n");
1712 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1716 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1717 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1719 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1720 if (!fileref
->dc
->name
.Buffer
) {
1721 ERR("out of memory\n");
1722 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1726 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1727 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1729 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1730 if (!NT_SUCCESS(Status
)) {
1731 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1735 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1736 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1739 // add to new parent
1740 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1742 if (IsListEmpty(&related
->fcb
->dir_children_index
))
1743 fileref
->dc
->index
= 2;
1745 dir_child
* dc2
= CONTAINING_RECORD(related
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1747 fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1750 InsertTailList(&related
->fcb
->dir_children_index
, &fileref
->dc
->list_entry_index
);
1751 insert_dir_child_into_hash_lists(related
->fcb
, fileref
->dc
);
1752 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
1755 ExAcquireResourceExclusiveLite(&related
->nonpaged
->children_lock
, TRUE
);
1756 InsertTailList(&related
->children
, &fileref
->list_entry
);
1757 ExReleaseResourceLite(&related
->nonpaged
->children_lock
);
1759 if (fcb
->inode_item
.st_nlink
> 1) {
1760 // add new hardlink entry to fcb
1762 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1764 ERR("out of memory\n");
1765 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1769 hl
->parent
= related
->fcb
->inode
;
1770 hl
->index
= fileref
->dc
->index
;
1772 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
1773 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1775 if (!hl
->name
.Buffer
) {
1776 ERR("out of memory\n");
1778 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1782 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1784 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1785 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1787 if (!hl
->utf8
.Buffer
) {
1788 ERR("out of memory\n");
1789 ExFreePool(hl
->name
.Buffer
);
1791 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1795 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1797 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
1800 // delete old hardlink entry from fcb
1802 le
= fcb
->hardlinks
.Flink
;
1803 while (le
!= &fcb
->hardlinks
) {
1804 hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1806 if (hl
->parent
== fr2
->parent
->fcb
->inode
&& hl
->index
== fr2
->oldindex
) {
1807 RemoveEntryList(&hl
->list_entry
);
1809 if (hl
->utf8
.Buffer
)
1810 ExFreePool(hl
->utf8
.Buffer
);
1812 if (hl
->name
.Buffer
)
1813 ExFreePool(hl
->name
.Buffer
);
1822 // update inode's INODE_ITEM
1824 KeQuerySystemTime(&time
);
1825 win_time_to_unix(time
, &now
);
1827 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1828 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1829 fcb
->inode_item
.sequence
++;
1831 if (!ccb
->user_set_change_time
)
1832 fcb
->inode_item
.st_ctime
= now
;
1834 fcb
->inode_item_changed
= TRUE
;
1835 mark_fcb_dirty(fcb
);
1838 // update new parent's INODE_ITEM
1840 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1841 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1842 related
->fcb
->inode_item
.st_size
+= 2 * utf8len
;
1843 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1844 related
->fcb
->inode_item
.sequence
++;
1845 related
->fcb
->inode_item
.st_ctime
= now
;
1846 related
->fcb
->inode_item
.st_mtime
= now
;
1848 related
->fcb
->inode_item_changed
= TRUE
;
1849 mark_fcb_dirty(related
->fcb
);
1851 // update old parent's INODE_ITEM
1853 fr2
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1854 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1855 fr2
->parent
->fcb
->inode_item
.st_size
-= 2 * origutf8len
;
1856 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1857 fr2
->parent
->fcb
->inode_item
.sequence
++;
1858 fr2
->parent
->fcb
->inode_item
.st_ctime
= now
;
1859 fr2
->parent
->fcb
->inode_item
.st_mtime
= now
;
1861 free_fileref(Vcb
, fr2
);
1863 fr2
->parent
->fcb
->inode_item_changed
= TRUE
;
1864 mark_fcb_dirty(fr2
->parent
->fcb
);
1866 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1867 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1868 send_notification_fileref(fr2
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1870 Status
= STATUS_SUCCESS
;
1874 free_fileref(Vcb
, oldfileref
);
1876 if (!NT_SUCCESS(Status
) && related
)
1877 free_fileref(Vcb
, related
);
1879 if (!NT_SUCCESS(Status
) && fr2
)
1880 free_fileref(Vcb
, fr2
);
1882 if (NT_SUCCESS(Status
))
1883 clear_rollback(&rollback
);
1885 do_rollback(Vcb
, &rollback
);
1887 ExReleaseResourceLite(fcb
->Header
.Resource
);
1888 release_fcb_lock(Vcb
);
1889 ExReleaseResourceLite(&Vcb
->tree_lock
);
1894 NTSTATUS
stream_set_end_of_file_information(device_extension
* Vcb
, UINT16 end
, fcb
* fcb
, file_ref
* fileref
, BOOL advance_only
) {
1898 TRACE("setting new end to %llx bytes (currently %x)\n", end
, fcb
->adsdata
.Length
);
1900 if (!fileref
|| !fileref
->parent
) {
1901 ERR("no fileref for stream\n");
1902 return STATUS_INTERNAL_ERROR
;
1905 if (end
< fcb
->adsdata
.Length
) {
1907 return STATUS_SUCCESS
;
1909 TRACE("truncating stream to %llx bytes\n", end
);
1911 fcb
->adsdata
.Length
= end
;
1912 } else if (end
> fcb
->adsdata
.Length
) {
1913 TRACE("extending stream to %llx bytes\n", end
);
1915 if (end
> fcb
->adsmaxlen
) {
1916 ERR("error - xattr too long (%u > %u)\n", end
, fcb
->adsmaxlen
);
1917 return STATUS_DISK_FULL
;
1920 if (end
> fcb
->adsdata
.MaximumLength
) {
1921 char* data
= ExAllocatePoolWithTag(PagedPool
, end
, ALLOC_TAG
);
1923 ERR("out of memory\n");
1925 return STATUS_INSUFFICIENT_RESOURCES
;
1928 if (fcb
->adsdata
.Buffer
) {
1929 RtlCopyMemory(data
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
1930 ExFreePool(fcb
->adsdata
.Buffer
);
1933 fcb
->adsdata
.Buffer
= data
;
1934 fcb
->adsdata
.MaximumLength
= end
;
1937 RtlZeroMemory(&fcb
->adsdata
.Buffer
[fcb
->adsdata
.Length
], end
- fcb
->adsdata
.Length
);
1939 fcb
->adsdata
.Length
= end
;
1942 mark_fcb_dirty(fcb
);
1944 fcb
->Header
.AllocationSize
.QuadPart
= end
;
1945 fcb
->Header
.FileSize
.QuadPart
= end
;
1946 fcb
->Header
.ValidDataLength
.QuadPart
= end
;
1948 KeQuerySystemTime(&time
);
1949 win_time_to_unix(time
, &now
);
1951 fileref
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1952 fileref
->parent
->fcb
->inode_item
.sequence
++;
1953 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1955 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1956 mark_fcb_dirty(fileref
->parent
->fcb
);
1958 fileref
->parent
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1959 fileref
->parent
->fcb
->subvol
->root_item
.ctime
= now
;
1961 return STATUS_SUCCESS
;
1964 static NTSTATUS
set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
, BOOL prealloc
) {
1965 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
1966 fcb
* fcb
= FileObject
->FsContext
;
1967 ccb
* ccb
= FileObject
->FsContext2
;
1968 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1972 LIST_ENTRY rollback
;
1973 BOOL set_size
= FALSE
;
1977 ERR("fileref is NULL\n");
1978 return STATUS_INVALID_PARAMETER
;
1981 InitializeListHead(&rollback
);
1983 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1985 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1987 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
1988 Status
= STATUS_FILE_CLOSED
;
1993 if (feofi
->EndOfFile
.QuadPart
> 0xffff) {
1994 Status
= STATUS_DISK_FULL
;
1998 if (feofi
->EndOfFile
.QuadPart
< 0) {
1999 Status
= STATUS_INVALID_PARAMETER
;
2003 Status
= stream_set_end_of_file_information(Vcb
, (UINT16
)feofi
->EndOfFile
.QuadPart
, fcb
, fileref
, advance_only
);
2005 if (NT_SUCCESS(Status
)) {
2006 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2007 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2008 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2012 filter
= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
2014 if (!ccb
->user_set_write_time
) {
2015 KeQuerySystemTime(&time
);
2016 win_time_to_unix(time
, &fileref
->parent
->fcb
->inode_item
.st_mtime
);
2017 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2019 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2020 mark_fcb_dirty(fileref
->parent
->fcb
);
2023 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED_STREAM
, &fileref
->dc
->name
);
2028 TRACE("file: %S\n", file_desc(FileObject
));
2029 TRACE("paging IO: %s\n", Irp
->Flags
& IRP_PAGING_IO
? "TRUE" : "FALSE");
2030 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2031 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2033 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi
->EndOfFile
.QuadPart
, fcb
->inode_item
.st_size
);
2035 if ((UINT64
)feofi
->EndOfFile
.QuadPart
< fcb
->inode_item
.st_size
) {
2037 Status
= STATUS_SUCCESS
;
2041 TRACE("truncating file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2043 if (!MmCanFileBeTruncated(&fcb
->nonpaged
->segment_object
, &feofi
->EndOfFile
)) {
2044 Status
= STATUS_USER_MAPPED_FILE
;
2048 Status
= truncate_file(fcb
, feofi
->EndOfFile
.QuadPart
, Irp
, &rollback
);
2049 if (!NT_SUCCESS(Status
)) {
2050 ERR("error - truncate_file failed\n");
2053 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
> fcb
->inode_item
.st_size
) {
2054 if (Irp
->Flags
& IRP_PAGING_IO
) {
2055 TRACE("paging IO tried to extend file size\n");
2056 Status
= STATUS_SUCCESS
;
2060 TRACE("extending file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2062 Status
= extend_file(fcb
, fileref
, feofi
->EndOfFile
.QuadPart
, prealloc
, NULL
, &rollback
);
2063 if (!NT_SUCCESS(Status
)) {
2064 ERR("error - extend_file failed\n");
2067 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
== fcb
->inode_item
.st_size
&& advance_only
) {
2068 Status
= STATUS_SUCCESS
;
2072 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2073 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2074 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2077 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2079 if (!ccb
->user_set_write_time
) {
2080 KeQuerySystemTime(&time
);
2081 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2082 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2085 fcb
->inode_item_changed
= TRUE
;
2086 mark_fcb_dirty(fcb
);
2087 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2089 Status
= STATUS_SUCCESS
;
2092 if (NT_SUCCESS(Status
))
2093 clear_rollback(&rollback
);
2095 do_rollback(Vcb
, &rollback
);
2097 ExReleaseResourceLite(fcb
->Header
.Resource
);
2101 CcSetFileSizes(FileObject
, &ccfs
);
2102 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2103 Status
= _SEH2_GetExceptionCode();
2106 if (!NT_SUCCESS(Status
))
2107 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2110 ExReleaseResourceLite(&Vcb
->tree_lock
);
2115 static NTSTATUS
set_position_information(PFILE_OBJECT FileObject
, PIRP Irp
) {
2116 FILE_POSITION_INFORMATION
* fpi
= (FILE_POSITION_INFORMATION
*)Irp
->AssociatedIrp
.SystemBuffer
;
2118 TRACE("setting the position on %S to %llx\n", file_desc(FileObject
), fpi
->CurrentByteOffset
.QuadPart
);
2120 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2122 FileObject
->CurrentByteOffset
= fpi
->CurrentByteOffset
;
2124 return STATUS_SUCCESS
;
2127 static NTSTATUS
set_link_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
2128 FILE_LINK_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
2129 fcb
*fcb
= FileObject
->FsContext
, *tfofcb
, *parfcb
;
2130 ccb
* ccb
= FileObject
->FsContext2
;
2131 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
2133 ULONG fnlen
, utf8len
;
2134 UNICODE_STRING fnus
;
2139 LIST_ENTRY rollback
;
2142 SECURITY_SUBJECT_CONTEXT subjcont
;
2143 dir_child
* dc
= NULL
;
2145 InitializeListHead(&rollback
);
2147 // FIXME - check fli length
2148 // FIXME - don't ignore fli->RootDirectory
2150 TRACE("ReplaceIfExists = %x\n", fli
->ReplaceIfExists
);
2151 TRACE("RootDirectory = %p\n", fli
->RootDirectory
);
2152 TRACE("FileNameLength = %x\n", fli
->FileNameLength
);
2153 TRACE("FileName = %.*S\n", fli
->FileNameLength
/ sizeof(WCHAR
), fli
->FileName
);
2156 fnlen
= fli
->FileNameLength
/ sizeof(WCHAR
);
2159 if (!fileref
|| !fileref
->parent
) {
2160 ERR("no fileref set and no directory given\n");
2161 return STATUS_INVALID_PARAMETER
;
2164 parfcb
= fileref
->parent
->fcb
;
2169 tfofcb
= tfo
->FsContext
;
2172 while (fnlen
> 0 && (fli
->FileName
[fnlen
- 1] == '/' || fli
->FileName
[fnlen
- 1] == '\\'))
2176 return STATUS_INVALID_PARAMETER
;
2178 for (i
= fnlen
- 1; i
>= 0; i
--) {
2179 if (fli
->FileName
[i
] == '\\' || fli
->FileName
[i
] == '/') {
2180 fn
= &fli
->FileName
[i
+1];
2181 fnlen
= (fli
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
2187 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2188 acquire_fcb_lock_exclusive(Vcb
);
2189 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2191 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2192 WARN("tried to create hard link on directory\n");
2193 Status
= STATUS_FILE_IS_A_DIRECTORY
;
2198 WARN("tried to create hard link on stream\n");
2199 Status
= STATUS_INVALID_PARAMETER
;
2203 if (fcb
->inode_item
.st_nlink
>= 65535) {
2204 Status
= STATUS_TOO_MANY_LINKS
;
2209 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
2211 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
2213 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2214 if (!NT_SUCCESS(Status
))
2217 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
2218 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
2220 ERR("out of memory\n");
2221 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2225 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2226 if (!NT_SUCCESS(Status
))
2229 if (tfo
&& tfo
->FsContext2
) {
2230 struct _ccb
* relatedccb
= tfo
->FsContext2
;
2232 related
= relatedccb
->fileref
;
2233 increase_fileref_refcount(related
);
2236 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2238 if (NT_SUCCESS(Status
)) {
2239 if (!oldfileref
->deleted
) {
2240 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref
));
2242 if (!fli
->ReplaceIfExists
) {
2243 Status
= STATUS_OBJECT_NAME_COLLISION
;
2245 } else if (oldfileref
->open_count
>= 1 && !oldfileref
->deleted
) {
2246 WARN("trying to overwrite open file\n");
2247 Status
= STATUS_ACCESS_DENIED
;
2249 } else if (fileref
== oldfileref
) {
2250 Status
= STATUS_ACCESS_DENIED
;
2254 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2255 WARN("trying to overwrite directory\n");
2256 Status
= STATUS_ACCESS_DENIED
;
2260 free_fileref(Vcb
, oldfileref
);
2266 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2268 if (!NT_SUCCESS(Status
)) {
2269 ERR("open_fileref returned %08x\n", Status
);
2274 SeCaptureSubjectContext(&subjcont
);
2276 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, FILE_ADD_FILE
, 0, NULL
,
2277 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2278 SeReleaseSubjectContext(&subjcont
);
2279 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2283 SeReleaseSubjectContext(&subjcont
);
2285 if (fcb
->subvol
!= parfcb
->subvol
) {
2286 WARN("can't create hard link over subvolume boundary\n");
2287 Status
= STATUS_INVALID_PARAMETER
;
2292 SeCaptureSubjectContext(&subjcont
);
2294 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
2295 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2296 SeReleaseSubjectContext(&subjcont
);
2297 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2301 SeReleaseSubjectContext(&subjcont
);
2303 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
2304 if (!NT_SUCCESS(Status
)) {
2305 ERR("delete_fileref returned %08x\n", Status
);
2310 fr2
= create_fileref(Vcb
);
2315 fr2
->created
= TRUE
;
2316 fr2
->parent
= related
;
2318 Status
= add_dir_child(related
->fcb
, fcb
->inode
, FALSE
, &utf8
, &fnus
, fcb
->type
, &dc
);
2319 if (!NT_SUCCESS(Status
))
2320 WARN("add_dir_child returned %08x\n", Status
);
2325 ExAcquireResourceExclusiveLite(&related
->nonpaged
->children_lock
, TRUE
);
2326 InsertTailList(&related
->children
, &fr2
->list_entry
);
2327 ExReleaseResourceLite(&related
->nonpaged
->children_lock
);
2329 // add hardlink for existing fileref, if it's not there already
2330 if (IsListEmpty(&fcb
->hardlinks
)) {
2331 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2333 ERR("out of memory\n");
2334 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2338 hl
->parent
= fileref
->parent
->fcb
->inode
;
2339 hl
->index
= fileref
->dc
->index
;
2341 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2342 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
2344 if (!hl
->name
.Buffer
) {
2345 ERR("out of memory\n");
2347 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2351 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2353 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
2354 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2356 if (!hl
->utf8
.Buffer
) {
2357 ERR("out of memory\n");
2358 ExFreePool(hl
->name
.Buffer
);
2360 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2364 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
2366 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2369 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2371 ERR("out of memory\n");
2372 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2376 hl
->parent
= related
->fcb
->inode
;
2377 hl
->index
= dc
->index
;
2379 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2380 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
2382 if (!hl
->name
.Buffer
) {
2383 ERR("out of memory\n");
2385 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2389 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2391 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= utf8
.Length
;
2392 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2394 if (!hl
->utf8
.Buffer
) {
2395 ERR("out of memory\n");
2396 ExFreePool(hl
->name
.Buffer
);
2398 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2402 RtlCopyMemory(hl
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
2403 ExFreePool(utf8
.Buffer
);
2405 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2407 mark_fileref_dirty(fr2
);
2408 free_fileref(Vcb
, fr2
);
2410 // update inode's INODE_ITEM
2412 KeQuerySystemTime(&time
);
2413 win_time_to_unix(time
, &now
);
2415 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2416 fcb
->inode_item
.sequence
++;
2417 fcb
->inode_item
.st_nlink
++;
2419 if (!ccb
->user_set_change_time
)
2420 fcb
->inode_item
.st_ctime
= now
;
2422 fcb
->inode_item_changed
= TRUE
;
2423 mark_fcb_dirty(fcb
);
2425 // update parent's INODE_ITEM
2427 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2428 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2429 parfcb
->inode_item
.st_size
+= 2 * utf8len
;
2430 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2431 parfcb
->inode_item
.sequence
++;
2432 parfcb
->inode_item
.st_ctime
= now
;
2434 parfcb
->inode_item_changed
= TRUE
;
2435 mark_fcb_dirty(parfcb
);
2437 send_notification_fileref(fr2
, FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2439 Status
= STATUS_SUCCESS
;
2443 free_fileref(Vcb
, oldfileref
);
2445 if (!NT_SUCCESS(Status
) && related
)
2446 free_fileref(Vcb
, related
);
2448 if (!NT_SUCCESS(Status
) && fr2
)
2449 free_fileref(Vcb
, fr2
);
2451 if (NT_SUCCESS(Status
))
2452 clear_rollback(&rollback
);
2454 do_rollback(Vcb
, &rollback
);
2456 ExReleaseResourceLite(fcb
->Header
.Resource
);
2457 release_fcb_lock(Vcb
);
2458 ExReleaseResourceLite(&Vcb
->tree_lock
);
2463 static NTSTATUS
set_valid_data_length_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
2464 FILE_VALID_DATA_LENGTH_INFORMATION
* fvdli
= Irp
->AssociatedIrp
.SystemBuffer
;
2465 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2466 fcb
* fcb
= FileObject
->FsContext
;
2467 ccb
* ccb
= FileObject
->FsContext2
;
2468 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2472 LIST_ENTRY rollback
;
2473 BOOL set_size
= FALSE
;
2476 if (IrpSp
->Parameters
.SetFile
.Length
< sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
)) {
2477 ERR("input buffer length was %u, expected %u\n", IrpSp
->Parameters
.SetFile
.Length
, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
));
2478 return STATUS_INVALID_PARAMETER
;
2482 ERR("fileref is NULL\n");
2483 return STATUS_INVALID_PARAMETER
;
2486 InitializeListHead(&rollback
);
2488 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2490 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2492 if (fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
) {
2493 Status
= STATUS_INVALID_PARAMETER
;
2497 if (fvdli
->ValidDataLength
.QuadPart
<= fcb
->Header
.ValidDataLength
.QuadPart
|| fvdli
->ValidDataLength
.QuadPart
> fcb
->Header
.FileSize
.QuadPart
) {
2498 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli
->ValidDataLength
.QuadPart
,
2499 fcb
->Header
.ValidDataLength
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
);
2500 Status
= STATUS_INVALID_PARAMETER
;
2504 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2505 Status
= STATUS_FILE_CLOSED
;
2509 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2510 // and we set it to the max anyway.
2512 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2513 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2514 ccfs
.ValidDataLength
= fvdli
->ValidDataLength
;
2517 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2519 if (!ccb
->user_set_write_time
) {
2520 KeQuerySystemTime(&time
);
2521 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2522 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2525 fcb
->inode_item_changed
= TRUE
;
2526 mark_fcb_dirty(fcb
);
2528 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2530 Status
= STATUS_SUCCESS
;
2533 if (NT_SUCCESS(Status
))
2534 clear_rollback(&rollback
);
2536 do_rollback(Vcb
, &rollback
);
2538 ExReleaseResourceLite(fcb
->Header
.Resource
);
2542 CcSetFileSizes(FileObject
, &ccfs
);
2543 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2544 Status
= _SEH2_GetExceptionCode();
2547 if (!NT_SUCCESS(Status
))
2548 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2550 fcb
->Header
.AllocationSize
= ccfs
.AllocationSize
;
2553 ExReleaseResourceLite(&Vcb
->tree_lock
);
2558 _Dispatch_type_(IRP_MJ_SET_INFORMATION
)
2559 _Function_class_(DRIVER_DISPATCH
)
2560 NTSTATUS
drv_set_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2562 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2563 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2564 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
2565 ccb
* ccb
= IrpSp
->FileObject
->FsContext2
;
2568 FsRtlEnterFileSystem();
2570 top_level
= is_top_level(Irp
);
2572 Irp
->IoStatus
.Information
= 0;
2574 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2575 Status
= vol_set_information(DeviceObject
, Irp
);
2577 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2578 Status
= STATUS_INVALID_PARAMETER
;
2582 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
2583 Status
= STATUS_ACCESS_DENIED
;
2587 if (Vcb
->readonly
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
) {
2588 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2594 Status
= STATUS_INVALID_PARAMETER
;
2600 Status
= STATUS_INVALID_PARAMETER
;
2604 if (fcb
!= Vcb
->dummy_fcb
&& is_subvol_readonly(fcb
->subvol
, Irp
) && IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
&&
2605 (fcb
->inode
!= SUBVOL_ROOT_INODE
|| (IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileBasicInformation
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileRenameInformation
))) {
2606 Status
= STATUS_ACCESS_DENIED
;
2610 Status
= STATUS_NOT_IMPLEMENTED
;
2612 TRACE("set information\n");
2614 switch (IrpSp
->Parameters
.SetFile
.FileInformationClass
) {
2615 case FileAllocationInformation
:
2617 TRACE("FileAllocationInformation\n");
2619 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
2620 WARN("insufficient privileges\n");
2621 Status
= STATUS_ACCESS_DENIED
;
2625 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, FALSE
, TRUE
);
2629 case FileBasicInformation
:
2631 TRACE("FileBasicInformation\n");
2633 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
2634 WARN("insufficient privileges\n");
2635 Status
= STATUS_ACCESS_DENIED
;
2639 Status
= set_basic_information(Vcb
, Irp
, IrpSp
->FileObject
);
2644 case FileDispositionInformation
:
2646 TRACE("FileDispositionInformation\n");
2648 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& DELETE
)) {
2649 WARN("insufficient privileges\n");
2650 Status
= STATUS_ACCESS_DENIED
;
2654 Status
= set_disposition_information(Vcb
, Irp
, IrpSp
->FileObject
);
2659 case FileEndOfFileInformation
:
2661 TRACE("FileEndOfFileInformation\n");
2663 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2664 WARN("insufficient privileges\n");
2665 Status
= STATUS_ACCESS_DENIED
;
2669 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.AdvanceOnly
, FALSE
);
2674 case FileLinkInformation
:
2675 TRACE("FileLinkInformation\n");
2676 Status
= set_link_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2679 case FilePositionInformation
:
2680 TRACE("FilePositionInformation\n");
2681 Status
= set_position_information(IrpSp
->FileObject
, Irp
);
2684 case FileRenameInformation
:
2685 TRACE("FileRenameInformation\n");
2686 // FIXME - make this work with streams
2687 Status
= set_rename_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2690 case FileValidDataLengthInformation
:
2692 TRACE("FileValidDataLengthInformation\n");
2694 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2695 WARN("insufficient privileges\n");
2696 Status
= STATUS_ACCESS_DENIED
;
2700 Status
= set_valid_data_length_information(Vcb
, Irp
, IrpSp
->FileObject
);
2706 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.SetFile
.FileInformationClass
);
2710 Irp
->IoStatus
.Status
= Status
;
2712 TRACE("returning %08x\n", Status
);
2714 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2717 IoSetTopLevelIrp(NULL
);
2719 FsRtlExitFileSystem();
2724 static NTSTATUS
fill_in_file_basic_information(FILE_BASIC_INFORMATION
* fbi
, INODE_ITEM
* ii
, LONG
* length
, fcb
* fcb
, file_ref
* fileref
) {
2725 RtlZeroMemory(fbi
, sizeof(FILE_BASIC_INFORMATION
));
2727 *length
-= sizeof(FILE_BASIC_INFORMATION
);
2729 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2732 KeQuerySystemTime(&time
);
2733 fbi
->CreationTime
= fbi
->LastAccessTime
= fbi
->LastWriteTime
= fbi
->ChangeTime
= time
;
2735 fbi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2736 fbi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2737 fbi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2738 fbi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2742 if (!fileref
|| !fileref
->parent
) {
2743 ERR("no fileref for stream\n");
2744 return STATUS_INTERNAL_ERROR
;
2746 fbi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2748 fbi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2750 return STATUS_SUCCESS
;
2753 static NTSTATUS
fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION
* fnoi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2756 if (*length
< (LONG
)sizeof(FILE_NETWORK_OPEN_INFORMATION
)) {
2758 return STATUS_BUFFER_OVERFLOW
;
2761 RtlZeroMemory(fnoi
, sizeof(FILE_NETWORK_OPEN_INFORMATION
));
2763 *length
-= sizeof(FILE_NETWORK_OPEN_INFORMATION
);
2766 if (!fileref
|| !fileref
->parent
) {
2767 ERR("no fileref for stream\n");
2768 return STATUS_INTERNAL_ERROR
;
2771 ii
= &fileref
->parent
->fcb
->inode_item
;
2773 ii
= &fcb
->inode_item
;
2775 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2778 KeQuerySystemTime(&time
);
2779 fnoi
->CreationTime
= fnoi
->LastAccessTime
= fnoi
->LastWriteTime
= fnoi
->ChangeTime
= time
;
2781 fnoi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2782 fnoi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2783 fnoi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2784 fnoi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2788 fnoi
->AllocationSize
.QuadPart
= fnoi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2789 fnoi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2791 fnoi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2792 fnoi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2793 fnoi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2796 return STATUS_SUCCESS
;
2799 static NTSTATUS
fill_in_file_standard_information(FILE_STANDARD_INFORMATION
* fsi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2800 RtlZeroMemory(fsi
, sizeof(FILE_STANDARD_INFORMATION
));
2802 *length
-= sizeof(FILE_STANDARD_INFORMATION
);
2805 if (!fileref
|| !fileref
->parent
) {
2806 ERR("no fileref for stream\n");
2807 return STATUS_INTERNAL_ERROR
;
2810 fsi
->AllocationSize
.QuadPart
= fsi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2811 fsi
->NumberOfLinks
= fileref
->parent
->fcb
->inode_item
.st_nlink
;
2812 fsi
->Directory
= FALSE
;
2814 fsi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2815 fsi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2816 fsi
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
2817 fsi
->Directory
= S_ISDIR(fcb
->inode_item
.st_mode
);
2820 TRACE("length = %llu\n", fsi
->EndOfFile
.QuadPart
);
2822 fsi
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
2824 return STATUS_SUCCESS
;
2827 static NTSTATUS
fill_in_file_internal_information(FILE_INTERNAL_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
2828 *length
-= sizeof(FILE_INTERNAL_INFORMATION
);
2830 fii
->IndexNumber
.QuadPart
= make_file_id(fcb
->subvol
, fcb
->inode
);
2832 return STATUS_SUCCESS
;
2835 static NTSTATUS
fill_in_file_ea_information(FILE_EA_INFORMATION
* eai
, fcb
* fcb
, LONG
* length
) {
2836 *length
-= sizeof(FILE_EA_INFORMATION
);
2838 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2839 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2840 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2843 eai
->EaSize
= fcb
->ealen
;
2845 return STATUS_SUCCESS
;
2848 static NTSTATUS
fill_in_file_position_information(FILE_POSITION_INFORMATION
* fpi
, PFILE_OBJECT FileObject
, LONG
* length
) {
2849 RtlZeroMemory(fpi
, sizeof(FILE_POSITION_INFORMATION
));
2851 *length
-= sizeof(FILE_POSITION_INFORMATION
);
2853 fpi
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
2855 return STATUS_SUCCESS
;
2858 NTSTATUS
fileref_get_filename(file_ref
* fileref
, PUNICODE_STRING fn
, USHORT
* name_offset
, ULONG
* preqlen
) {
2863 BOOL overflow
= FALSE
;
2865 // FIXME - we need a lock on filerefs' filepart
2867 if (fileref
== fileref
->fcb
->Vcb
->root_fileref
) {
2868 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2869 fn
->Buffer
[0] = '\\';
2870 fn
->Length
= sizeof(WCHAR
);
2875 return STATUS_SUCCESS
;
2878 *preqlen
= sizeof(WCHAR
);
2881 return STATUS_BUFFER_OVERFLOW
;
2888 while (fr
->parent
) {
2892 return STATUS_INTERNAL_ERROR
;
2895 if (fr
->dc
->name
.Length
+ sizeof(WCHAR
) + fn
->Length
> fn
->MaximumLength
)
2900 movelen
= fn
->MaximumLength
- fr
->dc
->name
.Length
- sizeof(WCHAR
);
2902 movelen
= fn
->Length
;
2904 if ((!overflow
|| fn
->MaximumLength
> fr
->dc
->name
.Length
+ sizeof(WCHAR
)) && movelen
> 0) {
2905 RtlMoveMemory(&fn
->Buffer
[(fr
->dc
->name
.Length
/ sizeof(WCHAR
)) + 1], fn
->Buffer
, movelen
);
2906 offset
+= fr
->dc
->name
.Length
+ sizeof(WCHAR
);
2909 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2910 fn
->Buffer
[0] = fr
->fcb
->ads
? ':' : '\\';
2911 fn
->Length
+= sizeof(WCHAR
);
2913 if (fn
->MaximumLength
> sizeof(WCHAR
)) {
2914 RtlCopyMemory(&fn
->Buffer
[1], fr
->dc
->name
.Buffer
, min(fr
->dc
->name
.Length
, fn
->MaximumLength
- sizeof(WCHAR
)));
2915 fn
->Length
+= fr
->dc
->name
.Length
;
2918 if (fn
->Length
> fn
->MaximumLength
) {
2919 fn
->Length
= fn
->MaximumLength
;
2924 reqlen
+= sizeof(WCHAR
) + fr
->dc
->name
.Length
;
2929 offset
+= sizeof(WCHAR
);
2934 Status
= STATUS_BUFFER_OVERFLOW
;
2937 *name_offset
= offset
;
2939 Status
= STATUS_SUCCESS
;
2945 static NTSTATUS
fill_in_file_name_information(FILE_NAME_INFORMATION
* fni
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2949 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2950 UINT16 datasuflen
= (UINT16
)wcslen(datasuf
) * sizeof(WCHAR
);
2953 ERR("called without fileref\n");
2954 return STATUS_INVALID_PARAMETER
;
2957 *length
-= (LONG
)offsetof(FILE_NAME_INFORMATION
, FileName
[0]);
2959 TRACE("maximum length is %u\n", *length
);
2960 fni
->FileNameLength
= 0;
2962 fni
->FileName
[0] = 0;
2964 fn
.Buffer
= fni
->FileName
;
2966 fn
.MaximumLength
= (UINT16
)*length
;
2968 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
2969 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
2970 ERR("fileref_get_filename returned %08x\n", Status
);
2975 if (Status
== STATUS_BUFFER_OVERFLOW
)
2976 reqlen
+= datasuflen
;
2978 if (fn
.Length
+ datasuflen
> fn
.MaximumLength
) {
2979 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, fn
.MaximumLength
- fn
.Length
);
2980 reqlen
+= datasuflen
;
2981 Status
= STATUS_BUFFER_OVERFLOW
;
2983 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, datasuflen
);
2984 fn
.Length
+= datasuflen
;
2989 if (Status
== STATUS_BUFFER_OVERFLOW
) {
2991 fni
->FileNameLength
= reqlen
;
2992 TRACE("%.*S (truncated)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
2994 *length
-= fn
.Length
;
2995 fni
->FileNameLength
= fn
.Length
;
2996 TRACE("%.*S\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3002 static NTSTATUS
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION
* ati
, fcb
* fcb
, ccb
* ccb
, PIRP Irp
, LONG
* length
) {
3003 *length
-= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
);
3006 if (!ccb
->fileref
|| !ccb
->fileref
->parent
) {
3007 ERR("no fileref for stream\n");
3008 return STATUS_INTERNAL_ERROR
;
3011 ati
->FileAttributes
= ccb
->fileref
->parent
->fcb
->atts
;
3013 ati
->FileAttributes
= fcb
->atts
;
3015 if (!(ati
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3016 ati
->ReparseTag
= 0;
3018 ati
->ReparseTag
= get_reparse_tag(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fcb
->atts
, ccb
->lxss
, Irp
);
3020 return STATUS_SUCCESS
;
3023 static NTSTATUS
fill_in_file_stream_information(FILE_STREAM_INFORMATION
* fsi
, file_ref
* fileref
, LONG
* length
) {
3026 FILE_STREAM_INFORMATION
*entry
, *lastentry
;
3029 static WCHAR datasuf
[] = L
":$DATA";
3033 ERR("fileref was NULL\n");
3034 return STATUS_INVALID_PARAMETER
;
3037 suf
.Buffer
= datasuf
;
3038 suf
.Length
= suf
.MaximumLength
= (UINT16
)wcslen(datasuf
) * sizeof(WCHAR
);
3040 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
3041 reqsize
= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
);
3045 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
3047 le
= fileref
->fcb
->dir_children_index
.Flink
;
3048 while (le
!= &fileref
->fcb
->dir_children_index
) {
3049 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3051 if (dc
->index
== 0) {
3052 reqsize
= (ULONG
)sector_align(reqsize
, sizeof(LONGLONG
));
3053 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
;
3060 TRACE("length = %i, reqsize = %u\n", *length
, reqsize
);
3062 if (reqsize
> *length
) {
3063 Status
= STATUS_BUFFER_OVERFLOW
;
3070 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3073 entry
->NextEntryOffset
= 0;
3074 entry
->StreamNameLength
= suf
.Length
+ sizeof(WCHAR
);
3075 entry
->StreamSize
.QuadPart
= fileref
->fcb
->inode_item
.st_size
;
3076 entry
->StreamAllocationSize
.QuadPart
= fcb_alloc_size(fileref
->fcb
);
3078 entry
->StreamName
[0] = ':';
3079 RtlCopyMemory(&entry
->StreamName
[1], suf
.Buffer
, suf
.Length
);
3081 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
), sizeof(LONGLONG
));
3084 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3087 le
= fileref
->fcb
->dir_children_index
.Flink
;
3088 while (le
!= &fileref
->fcb
->dir_children_index
) {
3089 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3091 if (dc
->index
== 0) {
3094 entry
->NextEntryOffset
= 0;
3095 entry
->StreamNameLength
= dc
->name
.Length
+ suf
.Length
+ sizeof(WCHAR
);
3098 entry
->StreamSize
.QuadPart
= dc
->fileref
->fcb
->adsdata
.Length
;
3100 entry
->StreamSize
.QuadPart
= dc
->size
;
3102 entry
->StreamAllocationSize
.QuadPart
= entry
->StreamSize
.QuadPart
;
3104 entry
->StreamName
[0] = ':';
3106 RtlCopyMemory(&entry
->StreamName
[1], dc
->name
.Buffer
, dc
->name
.Length
);
3107 RtlCopyMemory(&entry
->StreamName
[1 + (dc
->name
.Length
/ sizeof(WCHAR
))], suf
.Buffer
, suf
.Length
);
3110 lastentry
->NextEntryOffset
= (UINT32
)((UINT8
*)entry
- (UINT8
*)lastentry
);
3112 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
, sizeof(LONGLONG
));
3115 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3124 Status
= STATUS_SUCCESS
;
3127 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
3133 static NTSTATUS
fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION
* fsli
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3134 TRACE("FileStandardLinkInformation\n");
3136 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3138 fsli
->NumberOfAccessibleLinks
= fcb
->inode_item
.st_nlink
;
3139 fsli
->TotalNumberOfLinks
= fcb
->inode_item
.st_nlink
;
3140 fsli
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
3141 fsli
->Directory
= (!fcb
->ads
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) ? TRUE
: FALSE
;
3143 *length
-= sizeof(FILE_STANDARD_LINK_INFORMATION
);
3145 return STATUS_SUCCESS
;
3147 #endif /* __REACTOS__ */
3149 NTSTATUS
open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
3150 root
* subvol
, UINT64 inode
, file_ref
** pfr
, PIRP Irp
) {
3154 UNICODE_STRING name
;
3155 BOOL hl_alloc
= FALSE
;
3156 file_ref
*parfr
, *fr
;
3158 Status
= open_fcb(Vcb
, subvol
, inode
, 0, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
3159 if (!NT_SUCCESS(Status
)) {
3160 ERR("open_fcb returned %08x\n", Status
);
3165 *pfr
= fcb
->fileref
;
3166 increase_fileref_refcount(fcb
->fileref
);
3167 return STATUS_SUCCESS
;
3170 // find hardlink if fcb doesn't have any loaded
3171 if (IsListEmpty(&fcb
->hardlinks
)) {
3175 searchkey
.obj_id
= fcb
->inode
;
3176 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
3177 searchkey
.offset
= 0xffffffffffffffff;
3179 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3180 if (!NT_SUCCESS(Status
)) {
3181 ERR("find_item returned %08x\n", Status
);
3186 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
3187 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3191 ir
= (INODE_REF
*)tp
.item
->data
;
3193 parent
= tp
.item
->key
.offset
;
3195 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
3196 if (!NT_SUCCESS(Status
)) {
3197 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3202 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3207 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3210 ERR("out of memory\n");
3212 return STATUS_INSUFFICIENT_RESOURCES
;
3215 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
3216 if (!NT_SUCCESS(Status
)) {
3217 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3218 ExFreePool(name
.Buffer
);
3225 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3229 ier
= (INODE_EXTREF
*)tp
.item
->data
;
3233 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
3234 if (!NT_SUCCESS(Status
)) {
3235 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3240 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3245 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3248 ERR("out of memory\n");
3250 return STATUS_INSUFFICIENT_RESOURCES
;
3253 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
3254 if (!NT_SUCCESS(Status
)) {
3255 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3256 ExFreePool(name
.Buffer
);
3267 hardlink
* hl
= CONTAINING_RECORD(fcb
->hardlinks
.Flink
, hardlink
, list_entry
);
3270 parent
= hl
->parent
;
3274 ERR("subvol %llx, inode %llx has no hardlinks\n", subvol
->id
, inode
);
3276 if (hl_alloc
) ExFreePool(name
.Buffer
);
3277 return STATUS_INVALID_PARAMETER
;
3280 if (parent
== inode
) { // subvolume root
3284 searchkey
.obj_id
= subvol
->id
;
3285 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
3286 searchkey
.offset
= 0xffffffffffffffff;
3288 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3289 if (!NT_SUCCESS(Status
)) {
3290 ERR("find_item returned %08x\n", Status
);
3292 if (hl_alloc
) ExFreePool(name
.Buffer
);
3296 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3297 ROOT_REF
* rr
= (ROOT_REF
*)tp
.item
->data
;
3302 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
3303 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
));
3305 if (hl_alloc
) ExFreePool(name
.Buffer
);
3306 return STATUS_INTERNAL_ERROR
;
3309 if (tp
.item
->size
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
3310 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
, offsetof(ROOT_REF
, name
[0]) + rr
->n
);
3312 if (hl_alloc
) ExFreePool(name
.Buffer
);
3313 return STATUS_INTERNAL_ERROR
;
3316 le
= Vcb
->roots
.Flink
;
3317 while (le
!= &Vcb
->roots
) {
3318 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
3320 if (r2
->id
== tp
.item
->key
.offset
) {
3329 ERR("couldn't find subvol %llx\n", tp
.item
->key
.offset
);
3331 if (hl_alloc
) ExFreePool(name
.Buffer
);
3332 return STATUS_INTERNAL_ERROR
;
3335 Status
= open_fileref_by_inode(Vcb
, r
, rr
->dir
, &parfr
, Irp
);
3336 if (!NT_SUCCESS(Status
)) {
3337 ERR("open_fileref_by_inode returned %08x\n", Status
);
3339 if (hl_alloc
) ExFreePool(name
.Buffer
);
3344 ExFreePool(name
.Buffer
);
3348 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, rr
->name
, rr
->n
);
3349 if (!NT_SUCCESS(Status
)) {
3350 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3355 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3360 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3363 ERR("out of memory\n");
3365 return STATUS_INSUFFICIENT_RESOURCES
;
3368 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, rr
->name
, rr
->n
);
3369 if (!NT_SUCCESS(Status
)) {
3370 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3371 ExFreePool(name
.Buffer
);
3379 ERR("couldn't find parent for subvol %llx\n", subvol
->id
);
3381 if (hl_alloc
) ExFreePool(name
.Buffer
);
3382 return STATUS_INTERNAL_ERROR
;
3385 Status
= open_fileref_by_inode(Vcb
, subvol
, parent
, &parfr
, Irp
);
3386 if (!NT_SUCCESS(Status
)) {
3387 ERR("open_fileref_by_inode returned %08x\n", Status
);
3391 ExFreePool(name
.Buffer
);
3397 Status
= open_fileref_child(Vcb
, parfr
, &name
, TRUE
, TRUE
, FALSE
, PagedPool
, &fr
, Irp
);
3399 if (!NT_SUCCESS(Status
)) {
3400 ERR("open_fileref_child returned %08x\n", Status
);
3403 ExFreePool(name
.Buffer
);
3406 free_fileref(Vcb
, parfr
);
3414 ExFreePool(name
.Buffer
);
3417 free_fileref(Vcb
, parfr
);
3419 return STATUS_SUCCESS
;
3423 static NTSTATUS
fill_in_hard_link_information(FILE_LINKS_INFORMATION
* fli
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3427 FILE_LINK_ENTRY_INFORMATION
* feli
;
3428 BOOL overflow
= FALSE
;
3429 fcb
* fcb
= fileref
->fcb
;
3433 return STATUS_INVALID_PARAMETER
;
3435 if (*length
< (LONG
)offsetof(FILE_LINKS_INFORMATION
, Entry
))
3436 return STATUS_INVALID_PARAMETER
;
3438 RtlZeroMemory(fli
, *length
);
3440 bytes_needed
= offsetof(FILE_LINKS_INFORMATION
, Entry
);
3444 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3446 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
3449 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
)
3450 namelen
= sizeof(WCHAR
);
3452 namelen
= fileref
->dc
->name
.Length
;
3454 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) - sizeof(WCHAR
) + namelen
;
3456 if (bytes_needed
> *length
)
3462 feli
->NextEntryOffset
= 0;
3463 feli
->ParentFileId
= 0; // we use an inode of 0 to mean the parent of a subvolume
3465 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
) {
3466 feli
->FileNameLength
= 1;
3467 feli
->FileName
[0] = '.';
3469 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3470 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3473 fli
->EntriesReturned
++;
3478 acquire_fcb_lock_exclusive(fcb
->Vcb
);
3480 if (IsListEmpty(&fcb
->hardlinks
)) {
3481 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fileref
->dc
->name
.Length
- sizeof(WCHAR
);
3483 if (bytes_needed
> *length
)
3489 feli
->NextEntryOffset
= 0;
3490 feli
->ParentFileId
= fileref
->parent
->fcb
->inode
;
3491 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3492 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3494 fli
->EntriesReturned
++;
3499 le
= fcb
->hardlinks
.Flink
;
3500 while (le
!= &fcb
->hardlinks
) {
3501 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
3504 TRACE("parent %llx, index %llx, name %.*S\n", hl
->parent
, hl
->index
, hl
->name
.Length
/ sizeof(WCHAR
), hl
->name
.Buffer
);
3506 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, Irp
);
3508 if (!NT_SUCCESS(Status
)) {
3509 ERR("open_fileref_by_inode returned %08x\n", Status
);
3510 } else if (!parfr
->deleted
) {
3512 BOOL found
= FALSE
, deleted
= FALSE
;
3513 UNICODE_STRING
* fn
= NULL
;
3515 le2
= parfr
->children
.Flink
;
3516 while (le2
!= &parfr
->children
) {
3517 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
3519 if (fr2
->dc
->index
== hl
->index
) {
3521 deleted
= fr2
->deleted
;
3524 fn
= &fr2
->dc
->name
;
3536 TRACE("fn = %.*S (found = %u)\n", fn
->Length
/ sizeof(WCHAR
), fn
->Buffer
, found
);
3539 bytes_needed
= (LONG
)sector_align(bytes_needed
, 8);
3541 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fn
->Length
- sizeof(WCHAR
);
3543 if (bytes_needed
> *length
)
3548 feli
->NextEntryOffset
= (ULONG
)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION
) + ((feli
->FileNameLength
- 1) * sizeof(WCHAR
)), 8);
3549 feli
= (FILE_LINK_ENTRY_INFORMATION
*)((UINT8
*)feli
+ feli
->NextEntryOffset
);
3553 feli
->NextEntryOffset
= 0;
3554 feli
->ParentFileId
= parfr
->fcb
->inode
;
3555 feli
->FileNameLength
= fn
->Length
/ sizeof(WCHAR
);
3556 RtlCopyMemory(feli
->FileName
, fn
->Buffer
, fn
->Length
);
3558 fli
->EntriesReturned
++;
3564 free_fileref(fcb
->Vcb
, parfr
);
3571 release_fcb_lock(fcb
->Vcb
);
3574 fli
->BytesNeeded
= bytes_needed
;
3578 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
3580 ExReleaseResourceLite(fcb
->Header
.Resource
);
3584 #endif /* __REACTOS__ */
3586 #if (NTDDI_VERSION >= NTDDI_WIN10)
3588 typedef struct _FILE_ID_128
{
3589 UCHAR Identifier
[16];
3590 } FILE_ID_128
, *PFILE_ID_128
;
3592 typedef struct _FILE_ID_INFORMATION
{
3593 ULONGLONG VolumeSerialNumber
;
3595 } FILE_ID_INFORMATION
, *PFILE_ID_INFORMATION
;
3598 static NTSTATUS
fill_in_file_id_information(FILE_ID_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
3599 RtlCopyMemory(&fii
->VolumeSerialNumber
, &fcb
->Vcb
->superblock
.uuid
.uuid
[8], sizeof(UINT64
));
3600 RtlCopyMemory(&fii
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
3601 RtlCopyMemory(&fii
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
3603 *length
-= sizeof(FILE_ID_INFORMATION
);
3605 return STATUS_SUCCESS
;
3609 static NTSTATUS
query_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
3610 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3611 LONG length
= IrpSp
->Parameters
.QueryFile
.Length
;
3612 fcb
* fcb
= FileObject
->FsContext
;
3613 ccb
* ccb
= FileObject
->FsContext2
;
3614 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
3617 TRACE("(%p, %p, %p)\n", Vcb
, FileObject
, Irp
);
3618 TRACE("fcb = %p\n", fcb
);
3620 if (fcb
== Vcb
->volume_fcb
)
3621 return STATUS_INVALID_PARAMETER
;
3624 ERR("ccb is NULL\n");
3625 return STATUS_INVALID_PARAMETER
;
3628 switch (IrpSp
->Parameters
.QueryFile
.FileInformationClass
) {
3629 case FileAllInformation
:
3631 FILE_ALL_INFORMATION
* fai
= Irp
->AssociatedIrp
.SystemBuffer
;
3634 TRACE("FileAllInformation\n");
3636 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3637 WARN("insufficient privileges\n");
3638 Status
= STATUS_ACCESS_DENIED
;
3643 if (!fileref
|| !fileref
->parent
) {
3644 ERR("no fileref for stream\n");
3645 Status
= STATUS_INTERNAL_ERROR
;
3649 ii
= &fileref
->parent
->fcb
->inode_item
;
3651 ii
= &fcb
->inode_item
;
3653 // Access, mode, and alignment are all filled in by the kernel
3656 fill_in_file_basic_information(&fai
->BasicInformation
, ii
, &length
, fcb
, fileref
);
3659 fill_in_file_standard_information(&fai
->StandardInformation
, fcb
, fileref
, &length
);
3662 fill_in_file_internal_information(&fai
->InternalInformation
, fcb
, &length
);
3665 fill_in_file_ea_information(&fai
->EaInformation
, fcb
, &length
);
3667 length
-= sizeof(FILE_ACCESS_INFORMATION
);
3670 fill_in_file_position_information(&fai
->PositionInformation
, FileObject
, &length
);
3672 length
-= sizeof(FILE_MODE_INFORMATION
);
3674 length
-= sizeof(FILE_ALIGNMENT_INFORMATION
);
3677 fill_in_file_name_information(&fai
->NameInformation
, fcb
, fileref
, &length
);
3679 Status
= STATUS_SUCCESS
;
3684 case FileAttributeTagInformation
:
3686 FILE_ATTRIBUTE_TAG_INFORMATION
* ati
= Irp
->AssociatedIrp
.SystemBuffer
;
3688 TRACE("FileAttributeTagInformation\n");
3690 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3691 WARN("insufficient privileges\n");
3692 Status
= STATUS_ACCESS_DENIED
;
3696 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3697 Status
= fill_in_file_attribute_information(ati
, fcb
, ccb
, Irp
, &length
);
3698 ExReleaseResourceLite(&Vcb
->tree_lock
);
3703 case FileBasicInformation
:
3705 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
3708 TRACE("FileBasicInformation\n");
3710 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3711 WARN("insufficient privileges\n");
3712 Status
= STATUS_ACCESS_DENIED
;
3716 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_BASIC_INFORMATION
)) {
3718 Status
= STATUS_BUFFER_OVERFLOW
;
3723 if (!fileref
|| !fileref
->parent
) {
3724 ERR("no fileref for stream\n");
3725 Status
= STATUS_INTERNAL_ERROR
;
3729 ii
= &fileref
->parent
->fcb
->inode_item
;
3731 ii
= &fcb
->inode_item
;
3733 Status
= fill_in_file_basic_information(fbi
, ii
, &length
, fcb
, fileref
);
3737 case FileCompressionInformation
:
3738 FIXME("STUB: FileCompressionInformation\n");
3739 Status
= STATUS_INVALID_PARAMETER
;
3742 case FileEaInformation
:
3744 FILE_EA_INFORMATION
* eai
= Irp
->AssociatedIrp
.SystemBuffer
;
3746 TRACE("FileEaInformation\n");
3748 Status
= fill_in_file_ea_information(eai
, fcb
, &length
);
3753 case FileInternalInformation
:
3755 FILE_INTERNAL_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
3757 TRACE("FileInternalInformation\n");
3759 Status
= fill_in_file_internal_information(fii
, fcb
, &length
);
3764 case FileNameInformation
:
3766 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3768 TRACE("FileNameInformation\n");
3770 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3775 case FileNetworkOpenInformation
:
3777 FILE_NETWORK_OPEN_INFORMATION
* fnoi
= Irp
->AssociatedIrp
.SystemBuffer
;
3779 TRACE("FileNetworkOpenInformation\n");
3781 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3782 WARN("insufficient privileges\n");
3783 Status
= STATUS_ACCESS_DENIED
;
3787 Status
= fill_in_file_network_open_information(fnoi
, fcb
, fileref
, &length
);
3792 case FilePositionInformation
:
3794 FILE_POSITION_INFORMATION
* fpi
= Irp
->AssociatedIrp
.SystemBuffer
;
3796 TRACE("FilePositionInformation\n");
3798 Status
= fill_in_file_position_information(fpi
, FileObject
, &length
);
3803 case FileStandardInformation
:
3805 FILE_STANDARD_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3807 TRACE("FileStandardInformation\n");
3809 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STANDARD_INFORMATION
)) {
3811 Status
= STATUS_BUFFER_OVERFLOW
;
3815 Status
= fill_in_file_standard_information(fsi
, fcb
, ccb
->fileref
, &length
);
3820 case FileStreamInformation
:
3822 FILE_STREAM_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3824 TRACE("FileStreamInformation\n");
3826 Status
= fill_in_file_stream_information(fsi
, fileref
, &length
);
3831 #if (NTDDI_VERSION >= NTDDI_VISTA)
3832 case FileHardLinkInformation
:
3834 FILE_LINKS_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
3836 TRACE("FileHardLinkInformation\n");
3838 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3839 Status
= fill_in_hard_link_information(fli
, fileref
, Irp
, &length
);
3840 ExReleaseResourceLite(&Vcb
->tree_lock
);
3845 case FileNormalizedNameInformation
:
3847 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3849 TRACE("FileNormalizedNameInformation\n");
3851 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3857 #if (NTDDI_VERSION >= NTDDI_WIN7)
3858 case FileStandardLinkInformation
:
3860 FILE_STANDARD_LINK_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
3862 TRACE("FileStandardLinkInformation\n");
3864 Status
= fill_in_file_standard_link_information(fsli
, fcb
, ccb
->fileref
, &length
);
3869 case FileRemoteProtocolInformation
:
3870 TRACE("FileRemoteProtocolInformation\n");
3871 Status
= STATUS_INVALID_PARAMETER
;
3875 #if (NTDDI_VERSION >= NTDDI_WIN10)
3877 #pragma GCC diagnostic push
3878 #pragma GCC diagnostic ignored "-Wswitch"
3880 case FileIdInformation
:
3882 FILE_ID_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
3884 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_ID_INFORMATION
)) {
3886 Status
= STATUS_BUFFER_OVERFLOW
;
3890 TRACE("FileIdInformation\n");
3892 Status
= fill_in_file_id_information(fii
, fcb
, &length
);
3897 #pragma GCC diagnostic pop
3902 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryFile
.FileInformationClass
);
3903 Status
= STATUS_INVALID_PARAMETER
;
3909 Status
= STATUS_BUFFER_OVERFLOW
;
3912 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- length
;
3915 TRACE("query_info returning %08x\n", Status
);
3920 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION
)
3921 _Function_class_(DRIVER_DISPATCH
)
3922 NTSTATUS
drv_query_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3923 PIO_STACK_LOCATION IrpSp
;
3926 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3929 FsRtlEnterFileSystem();
3931 top_level
= is_top_level(Irp
);
3933 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3934 Status
= vol_query_information(DeviceObject
, Irp
);
3936 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3937 Status
= STATUS_INVALID_PARAMETER
;
3941 Irp
->IoStatus
.Information
= 0;
3943 TRACE("query information\n");
3945 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3947 fcb
= IrpSp
->FileObject
->FsContext
;
3948 TRACE("fcb = %p\n", fcb
);
3949 TRACE("fcb->subvol = %p\n", fcb
->subvol
);
3951 Status
= query_info(fcb
->Vcb
, IrpSp
->FileObject
, Irp
);
3954 TRACE("returning %08x\n", Status
);
3956 Irp
->IoStatus
.Status
= Status
;
3958 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
3961 IoSetTopLevelIrp(NULL
);
3963 FsRtlExitFileSystem();
3968 _Dispatch_type_(IRP_MJ_QUERY_EA
)
3969 _Function_class_(DRIVER_DISPATCH
)
3970 NTSTATUS
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3973 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3974 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3975 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
3978 FILE_FULL_EA_INFORMATION
* ffei
;
3981 FsRtlEnterFileSystem();
3983 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
3985 top_level
= is_top_level(Irp
);
3987 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3988 Status
= vol_query_ea(DeviceObject
, Irp
);
3990 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3991 Status
= STATUS_INVALID_PARAMETER
;
3995 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
3997 ERR("could not get output buffer\n");
3998 Status
= STATUS_INVALID_PARAMETER
;
4003 ERR("no file object\n");
4004 Status
= STATUS_INVALID_PARAMETER
;
4008 fcb
= FileObject
->FsContext
;
4012 Status
= STATUS_INVALID_PARAMETER
;
4016 ccb
= FileObject
->FsContext2
;
4020 Status
= STATUS_INVALID_PARAMETER
;
4024 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_READ_EA
| FILE_WRITE_EA
))) {
4025 WARN("insufficient privileges\n");
4026 Status
= STATUS_ACCESS_DENIED
;
4031 fcb
= ccb
->fileref
->parent
->fcb
;
4033 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4035 Status
= STATUS_SUCCESS
;
4037 if (fcb
->ea_xattr
.Length
== 0)
4040 if (IrpSp
->Parameters
.QueryEa
.EaList
) {
4041 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4042 FILE_GET_EA_INFORMATION
* in
;
4044 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4048 s
.Length
= s
.MaximumLength
= in
->EaNameLength
;
4049 s
.Buffer
= in
->EaName
;
4051 RtlUpperString(&s
, &s
);
4053 if (in
->NextEntryOffset
== 0)
4056 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4059 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4065 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4067 if (in
->EaNameLength
== ea
->EaNameLength
&&
4068 RtlCompareMemory(in
->EaName
, ea
->EaName
, in
->EaNameLength
) == in
->EaNameLength
) {
4073 if (in
->NextEntryOffset
== 0)
4076 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4080 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4082 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4083 Status
= STATUS_BUFFER_OVERFLOW
;
4091 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4092 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4096 out
->NextEntryOffset
= 0;
4097 out
->Flags
= ea
->Flags
;
4098 out
->EaNameLength
= ea
->EaNameLength
;
4099 out
->EaValueLength
= ea
->EaValueLength
;
4100 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4102 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4104 if (IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4108 if (ea
->NextEntryOffset
== 0)
4111 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4114 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4117 if (IrpSp
->Flags
& SL_INDEX_SPECIFIED
) {
4118 // The index is 1-based
4119 if (IrpSp
->Parameters
.QueryEa
.EaIndex
== 0) {
4120 Status
= STATUS_NONEXISTENT_EA_ENTRY
;
4123 index
= IrpSp
->Parameters
.QueryEa
.EaIndex
- 1;
4124 } else if (IrpSp
->Flags
& SL_RESTART_SCAN
)
4125 index
= ccb
->ea_index
= 0;
4127 index
= ccb
->ea_index
;
4129 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4134 for (i
= 0; i
< index
; i
++) {
4135 if (ea
->NextEntryOffset
== 0) // last item
4138 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4145 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4147 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4148 Status
= retlen
== 0 ? STATUS_BUFFER_TOO_SMALL
: STATUS_BUFFER_OVERFLOW
;
4155 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4156 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4160 out
->NextEntryOffset
= 0;
4161 out
->Flags
= ea
->Flags
;
4162 out
->EaNameLength
= ea
->EaNameLength
;
4163 out
->EaValueLength
= ea
->EaValueLength
;
4164 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4166 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4168 if (!(IrpSp
->Flags
& SL_INDEX_SPECIFIED
))
4171 if (ea
->NextEntryOffset
== 0 || IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4174 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4179 ExReleaseResourceLite(fcb
->Header
.Resource
);
4182 TRACE("returning %08x\n", Status
);
4184 Irp
->IoStatus
.Status
= Status
;
4185 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
? retlen
: 0;
4187 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4190 IoSetTopLevelIrp(NULL
);
4192 FsRtlExitFileSystem();
4201 LIST_ENTRY list_entry
;
4204 _Dispatch_type_(IRP_MJ_SET_EA
)
4205 _Function_class_(DRIVER_DISPATCH
)
4206 NTSTATUS
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4207 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4210 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4211 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4215 FILE_FULL_EA_INFORMATION
* ffei
;
4219 FILE_FULL_EA_INFORMATION
* ea
;
4224 FsRtlEnterFileSystem();
4226 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4228 top_level
= is_top_level(Irp
);
4230 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4231 Status
= vol_set_ea(DeviceObject
, Irp
);
4233 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4234 Status
= STATUS_INVALID_PARAMETER
;
4238 if (Vcb
->readonly
) {
4239 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4243 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
4245 ERR("could not get output buffer\n");
4246 Status
= STATUS_INVALID_PARAMETER
;
4250 Status
= IoCheckEaBufferValidity(ffei
, IrpSp
->Parameters
.SetEa
.Length
, &offset
);
4251 if (!NT_SUCCESS(Status
)) {
4252 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4257 ERR("no file object\n");
4258 Status
= STATUS_INVALID_PARAMETER
;
4262 fcb
= FileObject
->FsContext
;
4266 Status
= STATUS_INVALID_PARAMETER
;
4270 ccb
= FileObject
->FsContext2
;
4274 Status
= STATUS_INVALID_PARAMETER
;
4278 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_EA
)) {
4279 WARN("insufficient privileges\n");
4280 Status
= STATUS_ACCESS_DENIED
;
4285 fileref
= ccb
->fileref
->parent
;
4288 fileref
= ccb
->fileref
;
4290 InitializeListHead(&ealist
);
4292 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4294 if (fcb
->ea_xattr
.Length
> 0) {
4295 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4298 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4300 ERR("out of memory\n");
4301 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4305 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4306 item
->name
.Buffer
= ea
->EaName
;
4308 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4309 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4311 item
->flags
= ea
->Flags
;
4313 InsertTailList(&ealist
, &item
->list_entry
);
4315 if (ea
->NextEntryOffset
== 0)
4318 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4328 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
4329 s
.Buffer
= ea
->EaName
;
4331 RtlUpperString(&s
, &s
);
4334 while (le
!= &ealist
) {
4335 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4337 if (item
->name
.Length
== s
.Length
&&
4338 RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
4339 item
->flags
= ea
->Flags
;
4340 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4341 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4350 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4352 ERR("out of memory\n");
4353 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4357 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4358 item
->name
.Buffer
= ea
->EaName
;
4360 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4361 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4363 item
->flags
= ea
->Flags
;
4365 InsertTailList(&ealist
, &item
->list_entry
);
4368 if (ea
->NextEntryOffset
== 0)
4371 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4374 // remove entries with zero-length value
4376 while (le
!= &ealist
) {
4377 LIST_ENTRY
* le2
= le
->Flink
;
4379 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4381 if (item
->value
.Length
== 0) {
4382 RemoveEntryList(&item
->list_entry
);
4389 if (IsListEmpty(&ealist
)) {
4392 if (fcb
->ea_xattr
.Buffer
)
4393 ExFreePool(fcb
->ea_xattr
.Buffer
);
4395 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4396 fcb
->ea_xattr
.Buffer
= NULL
;
4402 while (le
!= &ealist
) {
4403 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4406 size
+= 4 - (size
% 4);
4408 size
+= (UINT16
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
4413 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
4415 ERR("out of memory\n");
4416 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4420 oldbuf
= fcb
->ea_xattr
.Buffer
;
4422 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
4423 fcb
->ea_xattr
.Buffer
= buf
;
4429 while (le
!= &ealist
) {
4430 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4433 ea
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
4435 if (ea
->NextEntryOffset
% 4 > 0)
4436 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
4438 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4440 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4442 ea
->NextEntryOffset
= 0;
4443 ea
->Flags
= item
->flags
;
4444 ea
->EaNameLength
= (UCHAR
)item
->name
.Length
;
4445 ea
->EaValueLength
= item
->value
.Length
;
4447 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
4448 ea
->EaName
[item
->name
.Length
] = 0;
4449 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
4451 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
4460 fcb
->ea_changed
= TRUE
;
4462 KeQuerySystemTime(&time
);
4463 win_time_to_unix(time
, &now
);
4465 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
4466 fcb
->inode_item
.sequence
++;
4468 if (!ccb
->user_set_change_time
)
4469 fcb
->inode_item
.st_ctime
= now
;
4471 fcb
->inode_item_changed
= TRUE
;
4472 mark_fcb_dirty(fcb
);
4474 send_notification_fileref(fileref
, FILE_NOTIFY_CHANGE_EA
, FILE_ACTION_MODIFIED
, NULL
);
4476 Status
= STATUS_SUCCESS
;
4479 ExReleaseResourceLite(fcb
->Header
.Resource
);
4481 while (!IsListEmpty(&ealist
)) {
4482 le
= RemoveHeadList(&ealist
);
4484 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4490 TRACE("returning %08x\n", Status
);
4492 Irp
->IoStatus
.Status
= Status
;
4493 Irp
->IoStatus
.Information
= 0;
4495 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4498 IoSetTopLevelIrp(NULL
);
4500 FsRtlExitFileSystem();