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
24 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
26 typedef struct _FILE_STAT_LX_INFORMATION
{
28 LARGE_INTEGER CreationTime
;
29 LARGE_INTEGER LastAccessTime
;
30 LARGE_INTEGER LastWriteTime
;
31 LARGE_INTEGER ChangeTime
;
32 LARGE_INTEGER AllocationSize
;
33 LARGE_INTEGER EndOfFile
;
37 ACCESS_MASK EffectiveAccess
;
42 ULONG LxDeviceIdMajor
;
43 ULONG LxDeviceIdMinor
;
44 } FILE_STAT_LX_INFORMATION
, *PFILE_STAT_LX_INFORMATION
;
46 #define LX_FILE_METADATA_HAS_UID 0x01
47 #define LX_FILE_METADATA_HAS_GID 0x02
48 #define LX_FILE_METADATA_HAS_MODE 0x04
49 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08
50 #define LX_FILE_CASE_SENSITIVE_DIR 0x10
55 static NTSTATUS
set_basic_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
56 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
57 fcb
* fcb
= FileObject
->FsContext
;
58 ccb
* ccb
= FileObject
->FsContext2
;
59 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
60 ULONG defda
, filter
= 0;
61 BOOL inode_item_changed
= FALSE
;
65 if (fileref
&& fileref
->parent
)
66 fcb
= fileref
->parent
->fcb
;
68 ERR("stream did not have fileref\n");
69 return STATUS_INTERNAL_ERROR
;
74 ERR("ccb was NULL\n");
75 return STATUS_INVALID_PARAMETER
;
78 TRACE("file = %S, attributes = %x\n", file_desc(FileObject
), fbi
->FileAttributes
);
80 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
82 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
83 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
84 Status
= STATUS_INVALID_PARAMETER
;
88 if (fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fcb
->subvol
, Irp
) &&
89 (fbi
->FileAttributes
== 0 || fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)) {
90 Status
= STATUS_ACCESS_DENIED
;
94 // don't allow readonly subvol to be made r/w if send operation running on it
95 if (fcb
->inode
== SUBVOL_ROOT_INODE
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
&&
96 fcb
->subvol
->send_ops
> 0) {
97 Status
= STATUS_DEVICE_NOT_READY
;
101 // times of -2 are some sort of undocumented behaviour to do with LXSS
103 if (fbi
->CreationTime
.QuadPart
== -2)
104 fbi
->CreationTime
.QuadPart
= 0;
106 if (fbi
->LastAccessTime
.QuadPart
== -2)
107 fbi
->LastAccessTime
.QuadPart
= 0;
109 if (fbi
->LastWriteTime
.QuadPart
== -2)
110 fbi
->LastWriteTime
.QuadPart
= 0;
112 if (fbi
->ChangeTime
.QuadPart
== -2)
113 fbi
->ChangeTime
.QuadPart
= 0;
115 if (fbi
->CreationTime
.QuadPart
== -1)
116 ccb
->user_set_creation_time
= TRUE
;
117 else if (fbi
->CreationTime
.QuadPart
!= 0) {
118 win_time_to_unix(fbi
->CreationTime
, &fcb
->inode_item
.otime
);
119 inode_item_changed
= TRUE
;
120 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
122 ccb
->user_set_creation_time
= TRUE
;
125 if (fbi
->LastAccessTime
.QuadPart
== -1)
126 ccb
->user_set_access_time
= TRUE
;
127 else if (fbi
->LastAccessTime
.QuadPart
!= 0) {
128 win_time_to_unix(fbi
->LastAccessTime
, &fcb
->inode_item
.st_atime
);
129 inode_item_changed
= TRUE
;
130 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
132 ccb
->user_set_access_time
= TRUE
;
135 if (fbi
->LastWriteTime
.QuadPart
== -1)
136 ccb
->user_set_write_time
= TRUE
;
137 else if (fbi
->LastWriteTime
.QuadPart
!= 0) {
138 win_time_to_unix(fbi
->LastWriteTime
, &fcb
->inode_item
.st_mtime
);
139 inode_item_changed
= TRUE
;
140 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
142 ccb
->user_set_write_time
= TRUE
;
145 if (fbi
->ChangeTime
.QuadPart
== -1)
146 ccb
->user_set_change_time
= TRUE
;
147 else if (fbi
->ChangeTime
.QuadPart
!= 0) {
148 win_time_to_unix(fbi
->ChangeTime
, &fcb
->inode_item
.st_ctime
);
149 inode_item_changed
= TRUE
;
150 // no filter for this
152 ccb
->user_set_change_time
= TRUE
;
155 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
156 if (fbi
->FileAttributes
!= 0) {
160 fbi
->FileAttributes
&= ~FILE_ATTRIBUTE_NORMAL
;
162 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] == '.',
165 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
166 fbi
->FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
167 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
168 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
170 fcb
->atts_changed
= TRUE
;
172 if (fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)
173 fbi
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
175 if (defda
== fbi
->FileAttributes
)
176 fcb
->atts_deleted
= TRUE
;
177 else if (fcb
->inode
== SUBVOL_ROOT_INODE
&& (defda
| FILE_ATTRIBUTE_READONLY
) == (fbi
->FileAttributes
| FILE_ATTRIBUTE_READONLY
))
178 fcb
->atts_deleted
= TRUE
;
180 fcb
->atts
= fbi
->FileAttributes
;
182 KeQuerySystemTime(&time
);
183 win_time_to_unix(time
, &now
);
185 if (!ccb
->user_set_change_time
)
186 fcb
->inode_item
.st_ctime
= now
;
188 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
189 fcb
->subvol
->root_item
.ctime
= now
;
191 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
192 if (fbi
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
193 fcb
->subvol
->root_item
.flags
|= BTRFS_SUBVOL_READONLY
;
195 fcb
->subvol
->root_item
.flags
&= ~BTRFS_SUBVOL_READONLY
;
198 inode_item_changed
= TRUE
;
200 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
203 if (inode_item_changed
) {
204 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
205 fcb
->inode_item
.sequence
++;
206 fcb
->inode_item_changed
= TRUE
;
212 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
214 Status
= STATUS_SUCCESS
;
217 ExReleaseResourceLite(fcb
->Header
.Resource
);
222 static NTSTATUS
set_disposition_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
223 FILE_DISPOSITION_INFORMATION
* fdi
= Irp
->AssociatedIrp
.SystemBuffer
;
224 fcb
* fcb
= FileObject
->FsContext
;
225 ccb
* ccb
= FileObject
->FsContext2
;
226 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
231 return STATUS_INVALID_PARAMETER
;
233 acquire_fcb_lock_exclusive(Vcb
);
235 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
237 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi
->DeleteFile
? "TRUE" : "FALSE", file_desc(FileObject
), fcb
);
241 atts
= fileref
->parent
->fcb
->atts
;
243 ERR("no fileref for stream\n");
244 Status
= STATUS_INTERNAL_ERROR
;
250 TRACE("atts = %x\n", atts
);
252 if (atts
& FILE_ATTRIBUTE_READONLY
) {
253 TRACE("not allowing readonly file to be deleted\n");
254 Status
= STATUS_CANNOT_DELETE
;
258 // FIXME - can we skip this bit for subvols?
259 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && (!fileref
|| fileref
->fcb
!= Vcb
->dummy_fcb
)) {
260 TRACE("directory not empty\n");
261 Status
= STATUS_DIRECTORY_NOT_EMPTY
;
265 if (!MmFlushImageSection(&fcb
->nonpaged
->segment_object
, MmFlushForDelete
)) {
266 TRACE("trying to delete file which is being mapped as an image\n");
267 Status
= STATUS_CANNOT_DELETE
;
271 ccb
->fileref
->delete_on_close
= fdi
->DeleteFile
;
273 FileObject
->DeletePending
= fdi
->DeleteFile
;
275 Status
= STATUS_SUCCESS
;
278 ExReleaseResourceLite(fcb
->Header
.Resource
);
280 release_fcb_lock(Vcb
);
282 // send notification that directory is about to be deleted
283 if (NT_SUCCESS(Status
) && fdi
->DeleteFile
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
284 FsRtlNotifyFullChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext
,
285 NULL
, FALSE
, FALSE
, 0, NULL
, NULL
, NULL
);
291 BOOL
has_open_children(file_ref
* fileref
) {
292 LIST_ENTRY
* le
= fileref
->children
.Flink
;
294 if (IsListEmpty(&fileref
->children
))
297 while (le
!= &fileref
->children
) {
298 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
300 if (c
->open_count
> 0)
303 if (has_open_children(c
))
312 static NTSTATUS
duplicate_fcb(fcb
* oldfcb
, fcb
** pfcb
) {
313 device_extension
* Vcb
= oldfcb
->Vcb
;
317 // FIXME - we can skip a lot of this if the inode is about to be deleted
319 fcb
= create_fcb(Vcb
, PagedPool
); // FIXME - what if we duplicate the paging file?
321 ERR("out of memory\n");
322 return STATUS_INSUFFICIENT_RESOURCES
;
327 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
328 fcb
->Header
.AllocationSize
= oldfcb
->Header
.AllocationSize
;
329 fcb
->Header
.FileSize
= oldfcb
->Header
.FileSize
;
330 fcb
->Header
.ValidDataLength
= oldfcb
->Header
.ValidDataLength
;
332 fcb
->type
= oldfcb
->type
;
336 fcb
->adshash
= oldfcb
->adshash
;
337 fcb
->adsmaxlen
= oldfcb
->adsmaxlen
;
339 if (oldfcb
->adsxattr
.Buffer
&& oldfcb
->adsxattr
.Length
> 0) {
340 fcb
->adsxattr
.Length
= oldfcb
->adsxattr
.Length
;
341 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
342 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
344 if (!fcb
->adsxattr
.Buffer
) {
345 ERR("out of memory\n");
347 return STATUS_INSUFFICIENT_RESOURCES
;
350 RtlCopyMemory(fcb
->adsxattr
.Buffer
, oldfcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
351 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
354 if (oldfcb
->adsdata
.Buffer
&& oldfcb
->adsdata
.Length
> 0) {
355 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= oldfcb
->adsdata
.Length
;
356 fcb
->adsdata
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsdata
.MaximumLength
, ALLOC_TAG
);
358 if (!fcb
->adsdata
.Buffer
) {
359 ERR("out of memory\n");
361 return STATUS_INSUFFICIENT_RESOURCES
;
364 RtlCopyMemory(fcb
->adsdata
.Buffer
, oldfcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
370 RtlCopyMemory(&fcb
->inode_item
, &oldfcb
->inode_item
, sizeof(INODE_ITEM
));
371 fcb
->inode_item_changed
= TRUE
;
373 if (oldfcb
->sd
&& RtlLengthSecurityDescriptor(oldfcb
->sd
) > 0) {
374 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, RtlLengthSecurityDescriptor(oldfcb
->sd
), ALLOC_TAG
);
376 ERR("out of memory\n");
378 return STATUS_INSUFFICIENT_RESOURCES
;
381 RtlCopyMemory(fcb
->sd
, oldfcb
->sd
, RtlLengthSecurityDescriptor(oldfcb
->sd
));
384 fcb
->atts
= oldfcb
->atts
;
386 le
= oldfcb
->extents
.Flink
;
387 while (le
!= &oldfcb
->extents
) {
388 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
391 extent
* ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
394 ERR("out of memory\n");
396 return STATUS_INSUFFICIENT_RESOURCES
;
399 ext2
->offset
= ext
->offset
;
400 ext2
->datalen
= ext
->datalen
;
402 if (ext2
->datalen
> 0)
403 RtlCopyMemory(&ext2
->extent_data
, &ext
->extent_data
, ext2
->datalen
);
405 ext2
->unique
= FALSE
;
406 ext2
->ignore
= FALSE
;
407 ext2
->inserted
= TRUE
;
411 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
413 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
)
414 len
= (ULONG
)ed2
->num_bytes
;
416 len
= (ULONG
)ed2
->size
;
418 len
= len
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
;
420 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
422 ERR("out of memory\n");
424 return STATUS_INSUFFICIENT_RESOURCES
;
427 RtlCopyMemory(ext2
->csum
, ext
->csum
, len
);
431 InsertTailList(&fcb
->extents
, &ext2
->list_entry
);
437 le
= oldfcb
->hardlinks
.Flink
;
438 while (le
!= &oldfcb
->hardlinks
) {
439 hardlink
*hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
), *hl2
;
441 hl2
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
444 ERR("out of memory\n");
446 return STATUS_INSUFFICIENT_RESOURCES
;
449 hl2
->parent
= hl
->parent
;
450 hl2
->index
= hl
->index
;
452 hl2
->name
.Length
= hl2
->name
.MaximumLength
= hl
->name
.Length
;
453 hl2
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->name
.MaximumLength
, ALLOC_TAG
);
455 if (!hl2
->name
.Buffer
) {
456 ERR("out of memory\n");
459 return STATUS_INSUFFICIENT_RESOURCES
;
462 RtlCopyMemory(hl2
->name
.Buffer
, hl
->name
.Buffer
, hl
->name
.Length
);
464 hl2
->utf8
.Length
= hl2
->utf8
.MaximumLength
= hl
->utf8
.Length
;
465 hl2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->utf8
.MaximumLength
, ALLOC_TAG
);
467 if (!hl2
->utf8
.Buffer
) {
468 ERR("out of memory\n");
469 ExFreePool(hl2
->name
.Buffer
);
472 return STATUS_INSUFFICIENT_RESOURCES
;
475 RtlCopyMemory(hl2
->utf8
.Buffer
, hl
->utf8
.Buffer
, hl
->utf8
.Length
);
477 InsertTailList(&fcb
->hardlinks
, &hl2
->list_entry
);
482 if (oldfcb
->reparse_xattr
.Buffer
&& oldfcb
->reparse_xattr
.Length
> 0) {
483 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= oldfcb
->reparse_xattr
.Length
;
485 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.MaximumLength
, ALLOC_TAG
);
486 if (!fcb
->reparse_xattr
.Buffer
) {
487 ERR("out of memory\n");
489 return STATUS_INSUFFICIENT_RESOURCES
;
492 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, oldfcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
495 if (oldfcb
->ea_xattr
.Buffer
&& oldfcb
->ea_xattr
.Length
> 0) {
496 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= oldfcb
->ea_xattr
.Length
;
498 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->ea_xattr
.MaximumLength
, ALLOC_TAG
);
499 if (!fcb
->ea_xattr
.Buffer
) {
500 ERR("out of memory\n");
502 return STATUS_INSUFFICIENT_RESOURCES
;
505 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, oldfcb
->ea_xattr
.Buffer
, fcb
->ea_xattr
.Length
);
508 fcb
->prop_compression
= oldfcb
->prop_compression
;
510 le
= oldfcb
->xattrs
.Flink
;
511 while (le
!= &oldfcb
->xattrs
) {
512 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
514 if (xa
->valuelen
> 0) {
517 xa2
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
, ALLOC_TAG
);
520 ERR("out of memory\n");
522 return STATUS_INSUFFICIENT_RESOURCES
;
525 xa2
->namelen
= xa
->namelen
;
526 xa2
->valuelen
= xa
->valuelen
;
527 xa2
->dirty
= xa
->dirty
;
528 memcpy(xa2
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
530 InsertTailList(&fcb
->xattrs
, &xa2
->list_entry
);
539 return STATUS_SUCCESS
;
542 typedef struct _move_entry
{
545 file_ref
* dummyfileref
;
546 struct _move_entry
* parent
;
547 LIST_ENTRY list_entry
;
550 static NTSTATUS
add_children_to_move_list(device_extension
* Vcb
, move_entry
* me
, PIRP Irp
) {
554 ExAcquireResourceSharedLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
556 le
= me
->fileref
->fcb
->dir_children_index
.Flink
;
558 while (le
!= &me
->fileref
->fcb
->dir_children_index
) {
559 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
563 Status
= open_fileref_child(Vcb
, me
->fileref
, &dc
->name
, TRUE
, TRUE
, dc
->index
== 0 ? TRUE
: FALSE
, PagedPool
, &fr
, Irp
);
565 if (!NT_SUCCESS(Status
)) {
566 ERR("open_fileref_child returned %08x\n", Status
);
567 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
571 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
573 ERR("out of memory\n");
574 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
575 return STATUS_INSUFFICIENT_RESOURCES
;
579 me2
->dummyfcb
= NULL
;
580 me2
->dummyfileref
= NULL
;
583 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
588 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
590 return STATUS_SUCCESS
;
593 void remove_dir_child_from_hash_lists(fcb
* fcb
, dir_child
* dc
) {
598 if (fcb
->hash_ptrs
[c
] == &dc
->list_entry_hash
) {
599 if (dc
->list_entry_hash
.Flink
== &fcb
->dir_children_hash
)
600 fcb
->hash_ptrs
[c
] = NULL
;
602 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash
.Flink
, dir_child
, list_entry_hash
);
604 if (dc2
->hash
>> 24 == c
)
605 fcb
->hash_ptrs
[c
] = &dc2
->list_entry_hash
;
607 fcb
->hash_ptrs
[c
] = NULL
;
611 RemoveEntryList(&dc
->list_entry_hash
);
613 c
= dc
->hash_uc
>> 24;
615 if (fcb
->hash_ptrs_uc
[c
] == &dc
->list_entry_hash_uc
) {
616 if (dc
->list_entry_hash_uc
.Flink
== &fcb
->dir_children_hash_uc
)
617 fcb
->hash_ptrs_uc
[c
] = NULL
;
619 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash_uc
.Flink
, dir_child
, list_entry_hash_uc
);
621 if (dc2
->hash_uc
>> 24 == c
)
622 fcb
->hash_ptrs_uc
[c
] = &dc2
->list_entry_hash_uc
;
624 fcb
->hash_ptrs_uc
[c
] = NULL
;
628 RemoveEntryList(&dc
->list_entry_hash_uc
);
631 static NTSTATUS
create_directory_fcb(device_extension
* Vcb
, root
* r
, fcb
* parfcb
, fcb
** pfcb
) {
634 SECURITY_SUBJECT_CONTEXT subjcont
;
640 fcb
= create_fcb(Vcb
, PagedPool
);
642 ERR("out of memory\n");
643 return STATUS_INSUFFICIENT_RESOURCES
;
646 KeQuerySystemTime(&time
);
647 win_time_to_unix(time
, &now
);
652 fcb
->inode
= InterlockedIncrement64(&r
->lastinode
);
653 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
655 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
656 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
657 fcb
->inode_item
.st_nlink
= 1;
658 fcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(parfcb
, TRUE
);
659 fcb
->inode_item
.st_atime
= fcb
->inode_item
.st_ctime
= fcb
->inode_item
.st_mtime
= fcb
->inode_item
.otime
= now
;
660 fcb
->inode_item
.st_gid
= GID_NOBODY
;
662 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, FALSE
, TRUE
, NULL
);
664 SeCaptureSubjectContext(&subjcont
);
666 Status
= SeAssignSecurity(parfcb
->sd
, NULL
, (void**)&fcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
668 if (!NT_SUCCESS(Status
)) {
669 ERR("SeAssignSecurity returned %08x\n", Status
);
674 ERR("SeAssignSecurity returned NULL security descriptor\n");
675 return STATUS_INTERNAL_ERROR
;
678 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
679 if (!NT_SUCCESS(Status
)) {
680 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
681 fcb
->inode_item
.st_uid
= UID_NOBODY
;
682 fcb
->sd_dirty
= TRUE
;
684 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
685 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
688 find_gid(fcb
, parfcb
, &subjcont
);
690 fcb
->inode_item_changed
= TRUE
;
692 InsertTailList(&r
->fcbs
, &fcb
->list_entry
);
693 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
695 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
696 fcb
->Header
.AllocationSize
.QuadPart
= 0;
697 fcb
->Header
.FileSize
.QuadPart
= 0;
698 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
703 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
704 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
706 fcb
->prop_compression
= parfcb
->prop_compression
;
707 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
709 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
710 if (!fcb
->hash_ptrs
) {
711 ERR("out of memory\n");
712 return STATUS_INSUFFICIENT_RESOURCES
;
715 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
717 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
718 if (!fcb
->hash_ptrs_uc
) {
719 ERR("out of memory\n");
720 return STATUS_INSUFFICIENT_RESOURCES
;
723 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
727 return STATUS_SUCCESS
;
730 static NTSTATUS
move_across_subvols(file_ref
* fileref
, ccb
* ccb
, file_ref
* destdir
, PANSI_STRING utf8
, PUNICODE_STRING fnus
, PIRP Irp
, LIST_ENTRY
* rollback
) {
732 LIST_ENTRY move_list
, *le
;
736 file_ref
* origparent
;
738 InitializeListHead(&move_list
);
740 KeQuerySystemTime(&time
);
741 win_time_to_unix(time
, &now
);
743 me
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
746 ERR("out of memory\n");
747 Status
= STATUS_INSUFFICIENT_RESOURCES
;
751 origparent
= fileref
->parent
;
753 me
->fileref
= fileref
;
754 increase_fileref_refcount(me
->fileref
);
756 me
->dummyfileref
= NULL
;
759 InsertTailList(&move_list
, &me
->list_entry
);
761 le
= move_list
.Flink
;
762 while (le
!= &move_list
) {
763 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
765 ExAcquireResourceSharedLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
767 if (!me
->fileref
->fcb
->ads
&& me
->fileref
->fcb
->subvol
== origparent
->fcb
->subvol
) {
768 Status
= add_children_to_move_list(fileref
->fcb
->Vcb
, me
, Irp
);
770 if (!NT_SUCCESS(Status
)) {
771 ERR("add_children_to_move_list returned %08x\n", Status
);
776 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
781 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
783 // loop through list and create new inodes
785 le
= move_list
.Flink
;
786 while (le
!= &move_list
) {
787 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
789 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
&& me
->fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) {
792 BOOL inserted
= FALSE
;
795 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
797 Status
= duplicate_fcb(me
->fileref
->fcb
, &me
->dummyfcb
);
798 if (!NT_SUCCESS(Status
)) {
799 ERR("duplicate_fcb returned %08x\n", Status
);
800 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
804 me
->dummyfcb
->subvol
= me
->fileref
->fcb
->subvol
;
805 me
->dummyfcb
->inode
= me
->fileref
->fcb
->inode
;
807 if (!me
->dummyfcb
->ads
) {
808 me
->dummyfcb
->sd_dirty
= me
->fileref
->fcb
->sd_dirty
;
809 me
->dummyfcb
->atts_changed
= me
->fileref
->fcb
->atts_changed
;
810 me
->dummyfcb
->atts_deleted
= me
->fileref
->fcb
->atts_deleted
;
811 me
->dummyfcb
->extents_changed
= me
->fileref
->fcb
->extents_changed
;
812 me
->dummyfcb
->reparse_xattr_changed
= me
->fileref
->fcb
->reparse_xattr_changed
;
813 me
->dummyfcb
->ea_changed
= me
->fileref
->fcb
->ea_changed
;
816 me
->dummyfcb
->created
= me
->fileref
->fcb
->created
;
817 me
->dummyfcb
->deleted
= me
->fileref
->fcb
->deleted
;
818 mark_fcb_dirty(me
->dummyfcb
);
820 if (!me
->fileref
->fcb
->ads
) {
823 me
->fileref
->fcb
->subvol
= destdir
->fcb
->subvol
;
824 me
->fileref
->fcb
->inode
= InterlockedIncrement64(&destdir
->fcb
->subvol
->lastinode
);
825 me
->fileref
->fcb
->inode_item
.st_nlink
= 1;
827 defda
= get_file_attributes(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, me
->fileref
->fcb
->inode
,
828 me
->fileref
->fcb
->type
, me
->fileref
->dc
&& me
->fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && me
->fileref
->dc
->name
.Buffer
[0] == '.',
831 me
->fileref
->fcb
->sd_dirty
= !!me
->fileref
->fcb
->sd
;
832 me
->fileref
->fcb
->atts_changed
= defda
!= me
->fileref
->fcb
->atts
;
833 me
->fileref
->fcb
->extents_changed
= !IsListEmpty(&me
->fileref
->fcb
->extents
);
834 me
->fileref
->fcb
->reparse_xattr_changed
= !!me
->fileref
->fcb
->reparse_xattr
.Buffer
;
835 me
->fileref
->fcb
->ea_changed
= !!me
->fileref
->fcb
->ea_xattr
.Buffer
;
836 me
->fileref
->fcb
->xattrs_changed
= !IsListEmpty(&me
->fileref
->fcb
->xattrs
);
837 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
839 le2
= me
->fileref
->fcb
->xattrs
.Flink
;
840 while (le2
!= &me
->fileref
->fcb
->xattrs
) {
841 xattr
* xa
= CONTAINING_RECORD(le2
, xattr
, list_entry
);
848 if (le
== move_list
.Flink
) { // first entry
849 me
->fileref
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
850 me
->fileref
->fcb
->inode_item
.sequence
++;
852 if (!ccb
->user_set_change_time
)
853 me
->fileref
->fcb
->inode_item
.st_ctime
= now
;
856 le2
= me
->fileref
->fcb
->extents
.Flink
;
857 while (le2
!= &me
->fileref
->fcb
->extents
) {
858 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
860 if (!ext
->ignore
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
861 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
863 if (ed2
->size
!= 0) {
864 chunk
* c
= get_chunk_from_address(me
->fileref
->fcb
->Vcb
, ed2
->address
);
867 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
869 Status
= update_changed_extent_ref(me
->fileref
->fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, me
->fileref
->fcb
->subvol
->id
, me
->fileref
->fcb
->inode
,
870 ext
->offset
- ed2
->offset
, 1, me
->fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
872 if (!NT_SUCCESS(Status
)) {
873 ERR("update_changed_extent_ref returned %08x\n", Status
);
874 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
885 me
->fileref
->fcb
->subvol
= me
->parent
->fileref
->fcb
->subvol
;
886 me
->fileref
->fcb
->inode
= me
->parent
->fileref
->fcb
->inode
;
889 me
->fileref
->fcb
->created
= TRUE
;
891 InsertHeadList(&me
->fileref
->fcb
->list_entry
, &me
->dummyfcb
->list_entry
);
892 RemoveEntryList(&me
->fileref
->fcb
->list_entry
);
894 le3
= destdir
->fcb
->subvol
->fcbs
.Flink
;
895 while (le3
!= &destdir
->fcb
->subvol
->fcbs
) {
896 fcb
* fcb
= CONTAINING_RECORD(le3
, struct _fcb
, list_entry
);
898 if (fcb
->inode
> me
->fileref
->fcb
->inode
) {
899 InsertHeadList(le3
->Blink
, &me
->fileref
->fcb
->list_entry
);
908 InsertTailList(&destdir
->fcb
->subvol
->fcbs
, &me
->fileref
->fcb
->list_entry
);
910 InsertTailList(&me
->fileref
->fcb
->Vcb
->all_fcbs
, &me
->dummyfcb
->list_entry_all
);
912 while (!IsListEmpty(&me
->fileref
->fcb
->hardlinks
)) {
913 hardlink
* hl
= CONTAINING_RECORD(RemoveHeadList(&me
->fileref
->fcb
->hardlinks
), hardlink
, list_entry
);
916 ExFreePool(hl
->name
.Buffer
);
919 ExFreePool(hl
->utf8
.Buffer
);
924 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
925 mark_fcb_dirty(me
->fileref
->fcb
);
927 if ((!me
->dummyfcb
->ads
&& me
->dummyfcb
->inode_item
.st_nlink
> 1) || (me
->dummyfcb
->ads
&& me
->parent
->dummyfcb
->inode_item
.st_nlink
> 1)) {
928 LIST_ENTRY
* le2
= le
->Flink
;
930 while (le2
!= &move_list
) {
931 move_entry
* me2
= CONTAINING_RECORD(le2
, move_entry
, list_entry
);
933 if (me2
->fileref
->fcb
== me
->fileref
->fcb
&& !me2
->fileref
->fcb
->ads
) {
934 me2
->dummyfcb
= me
->dummyfcb
;
935 InterlockedIncrement(&me
->dummyfcb
->refcount
);
942 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
944 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
945 me
->fileref
->fcb
->inode_item
.st_nlink
++;
946 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
947 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
954 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
955 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
957 // loop through list and create new filerefs
959 le
= move_list
.Flink
;
960 while (le
!= &move_list
) {
962 BOOL name_changed
= FALSE
;
964 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
966 me
->dummyfileref
= create_fileref(fileref
->fcb
->Vcb
);
967 if (!me
->dummyfileref
) {
968 ERR("out of memory\n");
969 Status
= STATUS_INSUFFICIENT_RESOURCES
;
973 if (me
->fileref
->fcb
== me
->fileref
->fcb
->Vcb
->dummy_fcb
) {
974 root
* r
= me
->parent
? me
->parent
->fileref
->fcb
->subvol
: destdir
->fcb
->subvol
;
976 Status
= create_directory_fcb(me
->fileref
->fcb
->Vcb
, r
, me
->fileref
->parent
->fcb
, &me
->fileref
->fcb
);
977 if (!NT_SUCCESS(Status
)) {
978 ERR("create_directory_fcb returnd %08x\n", Status
);
982 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
983 me
->fileref
->dc
->key
.obj_type
= TYPE_INODE_ITEM
;
985 me
->dummyfileref
->fcb
= me
->fileref
->fcb
->Vcb
->dummy_fcb
;
986 } else if (me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) {
987 me
->dummyfileref
->fcb
= me
->fileref
->fcb
;
989 me
->fileref
->fcb
->subvol
->parent
= le
== move_list
.Flink
? destdir
->fcb
->subvol
->id
: me
->parent
->fileref
->fcb
->subvol
->id
;
991 me
->dummyfileref
->fcb
= me
->dummyfcb
;
993 InterlockedIncrement(&me
->dummyfileref
->fcb
->refcount
);
995 me
->dummyfileref
->oldutf8
= me
->fileref
->oldutf8
;
996 me
->dummyfileref
->oldindex
= me
->fileref
->dc
->index
;
998 if (le
== move_list
.Flink
&& (me
->fileref
->dc
->utf8
.Length
!= utf8
->Length
|| RtlCompareMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
) != utf8
->Length
))
1001 if ((le
== move_list
.Flink
|| me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) && !me
->dummyfileref
->oldutf8
.Buffer
) {
1002 me
->dummyfileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1003 if (!me
->dummyfileref
->oldutf8
.Buffer
) {
1004 ERR("out of memory\n");
1005 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1009 RtlCopyMemory(me
->dummyfileref
->oldutf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
1011 me
->dummyfileref
->oldutf8
.Length
= me
->dummyfileref
->oldutf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
1014 me
->dummyfileref
->delete_on_close
= me
->fileref
->delete_on_close
;
1015 me
->dummyfileref
->deleted
= me
->fileref
->deleted
;
1017 me
->dummyfileref
->created
= me
->fileref
->created
;
1018 me
->fileref
->created
= TRUE
;
1020 me
->dummyfileref
->parent
= me
->parent
? me
->parent
->dummyfileref
: origparent
;
1021 increase_fileref_refcount(me
->dummyfileref
->parent
);
1023 ExAcquireResourceExclusiveLite(&me
->dummyfileref
->parent
->nonpaged
->children_lock
, TRUE
);
1024 InsertTailList(&me
->dummyfileref
->parent
->children
, &me
->dummyfileref
->list_entry
);
1025 ExReleaseResourceLite(&me
->dummyfileref
->parent
->nonpaged
->children_lock
);
1027 me
->dummyfileref
->debug_desc
= me
->fileref
->debug_desc
;
1029 if (me
->dummyfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1030 me
->dummyfileref
->fcb
->fileref
= me
->dummyfileref
;
1033 RemoveEntryList(&me
->fileref
->list_entry
);
1035 increase_fileref_refcount(destdir
);
1037 if (me
->fileref
->dc
) {
1038 // remove from old parent
1039 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1040 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1041 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1042 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1044 me
->fileref
->parent
->fcb
->inode_item
.st_size
-= me
->fileref
->dc
->utf8
.Length
* 2;
1045 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1046 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1047 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1048 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1049 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1050 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1053 ExFreePool(me
->fileref
->dc
->utf8
.Buffer
);
1054 ExFreePool(me
->fileref
->dc
->name
.Buffer
);
1055 ExFreePool(me
->fileref
->dc
->name_uc
.Buffer
);
1057 me
->fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1058 if (!me
->fileref
->dc
->utf8
.Buffer
) {
1059 ERR("out of memory\n");
1060 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1064 me
->fileref
->dc
->utf8
.Length
= me
->fileref
->dc
->utf8
.MaximumLength
= utf8
->Length
;
1065 RtlCopyMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1067 me
->fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
->Length
, ALLOC_TAG
);
1068 if (!me
->fileref
->dc
->name
.Buffer
) {
1069 ERR("out of memory\n");
1070 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1074 me
->fileref
->dc
->name
.Length
= me
->fileref
->dc
->name
.MaximumLength
= fnus
->Length
;
1075 RtlCopyMemory(me
->fileref
->dc
->name
.Buffer
, fnus
->Buffer
, fnus
->Length
);
1077 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1078 if (!NT_SUCCESS(Status
)) {
1079 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1083 me
->fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1084 me
->fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name_uc
.Buffer
, me
->fileref
->dc
->name_uc
.Length
);
1087 if (me
->fileref
->dc
->key
.obj_type
== TYPE_INODE_ITEM
)
1088 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1090 // add to new parent
1092 ExAcquireResourceExclusiveLite(&destdir
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1094 if (IsListEmpty(&destdir
->fcb
->dir_children_index
))
1095 me
->fileref
->dc
->index
= 2;
1097 dir_child
* dc2
= CONTAINING_RECORD(destdir
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1099 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1102 InsertTailList(&destdir
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1103 insert_dir_child_into_hash_lists(destdir
->fcb
, me
->fileref
->dc
);
1104 ExReleaseResourceLite(&destdir
->fcb
->nonpaged
->dir_children_lock
);
1107 free_fileref(fileref
->fcb
->Vcb
, me
->fileref
->parent
);
1108 me
->fileref
->parent
= destdir
;
1110 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->nonpaged
->children_lock
, TRUE
);
1111 InsertTailList(&me
->fileref
->parent
->children
, &me
->fileref
->list_entry
);
1112 ExReleaseResourceLite(&me
->fileref
->parent
->nonpaged
->children_lock
);
1114 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
);
1115 me
->fileref
->parent
->fcb
->inode_item
.st_size
+= me
->fileref
->dc
->utf8
.Length
* 2;
1116 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
);
1117 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1118 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1119 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1120 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1121 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1122 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1124 if (me
->fileref
->dc
) {
1125 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1126 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1128 if (!me
->fileref
->fcb
->ads
)
1129 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1131 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1133 ExAcquireResourceExclusiveLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1135 if (me
->fileref
->fcb
->ads
)
1136 InsertHeadList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1138 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
)
1139 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1141 if (IsListEmpty(&me
->parent
->fileref
->fcb
->dir_children_index
))
1142 me
->fileref
->dc
->index
= 2;
1144 dir_child
* dc2
= CONTAINING_RECORD(me
->parent
->fileref
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1146 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1149 InsertTailList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1150 insert_dir_child_into_hash_lists(me
->parent
->fileref
->fcb
, me
->fileref
->dc
);
1153 ExReleaseResourceLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
);
1157 if (!me
->dummyfileref
->fcb
->ads
) {
1158 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1159 if (!NT_SUCCESS(Status
)) {
1160 ERR("delete_fileref returned %08x\n", Status
);
1165 if (me
->fileref
->fcb
->inode_item
.st_nlink
> 1) {
1166 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1168 ERR("out of memory\n");
1169 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1173 hl
->parent
= me
->fileref
->parent
->fcb
->inode
;
1174 hl
->index
= me
->fileref
->dc
->index
;
1176 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
1177 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1178 if (!hl
->utf8
.Buffer
) {
1179 ERR("out of memory\n");
1180 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1185 RtlCopyMemory(hl
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
1187 hl
->name
.Length
= hl
->name
.MaximumLength
= me
->fileref
->dc
->name
.Length
;
1188 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1189 if (!hl
->name
.Buffer
) {
1190 ERR("out of memory\n");
1191 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1192 ExFreePool(hl
->utf8
.Buffer
);
1197 RtlCopyMemory(hl
->name
.Buffer
, me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1199 InsertTailList(&me
->fileref
->fcb
->hardlinks
, &hl
->list_entry
);
1202 mark_fileref_dirty(me
->fileref
);
1207 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1209 le
= move_list
.Flink
;
1210 while (le
!= &move_list
) {
1211 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1213 if (me
->dummyfileref
->fcb
->ads
&& me
->parent
->dummyfileref
->fcb
->deleted
) {
1214 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1215 if (!NT_SUCCESS(Status
)) {
1216 ERR("delete_fileref returned %08x\n", Status
);
1224 destdir
->fcb
->subvol
->root_item
.ctransid
= destdir
->fcb
->Vcb
->superblock
.generation
;
1225 destdir
->fcb
->subvol
->root_item
.ctime
= now
;
1227 me
= CONTAINING_RECORD(move_list
.Flink
, move_entry
, list_entry
);
1228 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1229 send_notification_fileref(me
->dummyfileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1230 send_notification_fileref(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1232 Status
= STATUS_SUCCESS
;
1235 while (!IsListEmpty(&move_list
)) {
1236 le
= RemoveHeadList(&move_list
);
1237 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1240 free_fcb(fileref
->fcb
->Vcb
, me
->dummyfcb
);
1242 if (me
->dummyfileref
)
1243 free_fileref(fileref
->fcb
->Vcb
, me
->dummyfileref
);
1245 free_fileref(fileref
->fcb
->Vcb
, me
->fileref
);
1253 void insert_dir_child_into_hash_lists(fcb
* fcb
, dir_child
* dc
) {
1264 le
= fcb
->hash_ptrs
[d
];
1273 le
= fcb
->dir_children_hash
.Flink
;
1275 while (le
!= &fcb
->dir_children_hash
) {
1276 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
1278 if (dc2
->hash
> dc
->hash
) {
1279 InsertHeadList(le
->Blink
, &dc
->list_entry_hash
);
1288 InsertTailList(&fcb
->dir_children_hash
, &dc
->list_entry_hash
);
1290 if (!fcb
->hash_ptrs
[c
])
1291 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1293 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs
[c
], dir_child
, list_entry_hash
);
1295 if (dc2
->hash
> dc
->hash
)
1296 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1299 c
= dc
->hash_uc
>> 24;
1305 le
= fcb
->hash_ptrs_uc
[d
];
1314 le
= fcb
->dir_children_hash_uc
.Flink
;
1316 while (le
!= &fcb
->dir_children_hash_uc
) {
1317 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
1319 if (dc2
->hash_uc
> dc
->hash_uc
) {
1320 InsertHeadList(le
->Blink
, &dc
->list_entry_hash_uc
);
1329 InsertTailList(&fcb
->dir_children_hash_uc
, &dc
->list_entry_hash_uc
);
1331 if (!fcb
->hash_ptrs_uc
[c
])
1332 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1334 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs_uc
[c
], dir_child
, list_entry_hash_uc
);
1336 if (dc2
->hash_uc
> dc
->hash_uc
)
1337 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1341 static NTSTATUS
set_rename_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
1342 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1343 FILE_RENAME_INFORMATION
* fri
= Irp
->AssociatedIrp
.SystemBuffer
;
1344 fcb
*fcb
= FileObject
->FsContext
;
1345 ccb
* ccb
= FileObject
->FsContext2
;
1346 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
1348 ULONG fnlen
, utf8len
, origutf8len
;
1349 UNICODE_STRING fnus
;
1354 LIST_ENTRY rollback
, *le
;
1356 SECURITY_SUBJECT_CONTEXT subjcont
;
1359 InitializeListHead(&rollback
);
1361 TRACE("tfo = %p\n", tfo
);
1362 TRACE("ReplaceIfExists = %u\n", IrpSp
->Parameters
.SetFile
.ReplaceIfExists
);
1363 TRACE("RootDirectory = %p\n", fri
->RootDirectory
);
1364 TRACE("FileName = %.*S\n", fri
->FileNameLength
/ sizeof(WCHAR
), fri
->FileName
);
1367 fnlen
= fri
->FileNameLength
/ sizeof(WCHAR
);
1370 if (!fileref
|| !fileref
->parent
) {
1371 ERR("no fileref set and no directory given\n");
1372 return STATUS_INVALID_PARAMETER
;
1377 while (fnlen
> 0 && (fri
->FileName
[fnlen
- 1] == '/' || fri
->FileName
[fnlen
- 1] == '\\'))
1381 return STATUS_INVALID_PARAMETER
;
1383 for (i
= fnlen
- 1; i
>= 0; i
--) {
1384 if (fri
->FileName
[i
] == '\\' || fri
->FileName
[i
] == '/') {
1385 fn
= &fri
->FileName
[i
+1];
1386 fnlen
= (fri
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
1392 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1393 acquire_fcb_lock_exclusive(Vcb
);
1394 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1397 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1398 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1399 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1400 Status
= STATUS_INVALID_PARAMETER
;
1405 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
1407 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1409 origutf8len
= fileref
->dc
->utf8
.Length
;
1411 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1412 if (!NT_SUCCESS(Status
))
1415 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
1416 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
1418 ERR("out of memory\n");
1419 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1423 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1424 if (!NT_SUCCESS(Status
))
1427 if (tfo
&& tfo
->FsContext2
) {
1428 struct _ccb
* relatedccb
= tfo
->FsContext2
;
1430 related
= relatedccb
->fileref
;
1431 increase_fileref_refcount(related
);
1432 } else if (fnus
.Length
>= sizeof(WCHAR
) && fnus
.Buffer
[0] != '\\') {
1433 related
= fileref
->parent
;
1434 increase_fileref_refcount(related
);
1437 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1439 if (NT_SUCCESS(Status
)) {
1440 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref
));
1442 if (fileref
!= oldfileref
&& !oldfileref
->deleted
) {
1443 if (!IrpSp
->Parameters
.SetFile
.ReplaceIfExists
) {
1444 Status
= STATUS_OBJECT_NAME_COLLISION
;
1446 } else if ((oldfileref
->open_count
>= 1 || has_open_children(oldfileref
)) && !oldfileref
->deleted
) {
1447 WARN("trying to overwrite open file\n");
1448 Status
= STATUS_ACCESS_DENIED
;
1452 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1453 WARN("trying to overwrite directory\n");
1454 Status
= STATUS_ACCESS_DENIED
;
1459 if (fileref
== oldfileref
|| oldfileref
->deleted
) {
1460 free_fileref(Vcb
, oldfileref
);
1466 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1468 if (!NT_SUCCESS(Status
)) {
1469 ERR("open_fileref returned %08x\n", Status
);
1474 if (related
->fcb
== Vcb
->dummy_fcb
) {
1475 Status
= STATUS_ACCESS_DENIED
;
1479 SeCaptureSubjectContext(&subjcont
);
1481 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
1482 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1483 SeReleaseSubjectContext(&subjcont
);
1484 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1488 SeReleaseSubjectContext(&subjcont
);
1490 if (has_open_children(fileref
)) {
1491 WARN("trying to rename file with open children\n");
1492 Status
= STATUS_ACCESS_DENIED
;
1497 SeCaptureSubjectContext(&subjcont
);
1499 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
1500 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1501 SeReleaseSubjectContext(&subjcont
);
1502 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1506 SeReleaseSubjectContext(&subjcont
);
1508 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
1509 if (!NT_SUCCESS(Status
)) {
1510 ERR("delete_fileref returned %08x\n", Status
);
1515 if (fileref
->parent
->fcb
->subvol
!= related
->fcb
->subvol
&& (fileref
->fcb
->subvol
== fileref
->parent
->fcb
->subvol
|| fileref
->fcb
== Vcb
->dummy_fcb
)) {
1516 Status
= move_across_subvols(fileref
, ccb
, related
, &utf8
, &fnus
, Irp
, &rollback
);
1517 if (!NT_SUCCESS(Status
)) {
1518 ERR("move_across_subvols returned %08x\n", Status
);
1523 if (related
== fileref
->parent
) { // keeping file in same directory
1524 UNICODE_STRING oldfn
, newfn
;
1526 ULONG reqlen
, oldutf8len
;
1528 oldfn
.Length
= oldfn
.MaximumLength
= 0;
1530 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1531 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1532 ERR("fileref_get_filename returned %08x\n", Status
);
1536 oldfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1537 if (!oldfn
.Buffer
) {
1538 ERR("out of memory\n");
1539 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1543 oldfn
.MaximumLength
= (UINT16
)reqlen
;
1545 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1546 if (!NT_SUCCESS(Status
)) {
1547 ERR("fileref_get_filename returned %08x\n", Status
);
1548 ExFreePool(oldfn
.Buffer
);
1552 oldutf8len
= fileref
->dc
->utf8
.Length
;
1554 if (!fileref
->created
&& !fileref
->oldutf8
.Buffer
) {
1555 fileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1556 if (!fileref
->oldutf8
.Buffer
) {
1557 ERR("out of memory\n");
1558 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1562 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1563 RtlCopyMemory(fileref
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1566 TRACE("renaming %.*S to %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
, fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1568 mark_fileref_dirty(fileref
);
1571 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1573 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1574 ExFreePool(fileref
->dc
->name
.Buffer
);
1575 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1577 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1578 if (!fileref
->dc
->utf8
.Buffer
) {
1579 ERR("out of memory\n");
1580 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1581 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1582 ExFreePool(oldfn
.Buffer
);
1586 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1587 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1589 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1590 if (!fileref
->dc
->name
.Buffer
) {
1591 ERR("out of memory\n");
1592 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1593 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1594 ExFreePool(oldfn
.Buffer
);
1598 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1599 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1601 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1602 if (!NT_SUCCESS(Status
)) {
1603 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1604 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1605 ExFreePool(oldfn
.Buffer
);
1609 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1611 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1612 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1614 insert_dir_child_into_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1616 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1619 newfn
.Length
= newfn
.MaximumLength
= 0;
1621 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1622 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1623 ERR("fileref_get_filename returned %08x\n", Status
);
1624 ExFreePool(oldfn
.Buffer
);
1628 newfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1629 if (!newfn
.Buffer
) {
1630 ERR("out of memory\n");
1631 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1632 ExFreePool(oldfn
.Buffer
);
1636 newfn
.MaximumLength
= (UINT16
)reqlen
;
1638 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1639 if (!NT_SUCCESS(Status
)) {
1640 ERR("fileref_get_filename returned %08x\n", Status
);
1641 ExFreePool(oldfn
.Buffer
);
1642 ExFreePool(newfn
.Buffer
);
1646 KeQuerySystemTime(&time
);
1647 win_time_to_unix(time
, &now
);
1649 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1650 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1651 fcb
->inode_item
.sequence
++;
1653 if (!ccb
->user_set_change_time
)
1654 fcb
->inode_item
.st_ctime
= now
;
1656 fcb
->inode_item_changed
= TRUE
;
1657 mark_fcb_dirty(fcb
);
1660 // update parent's INODE_ITEM
1662 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1663 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1664 related
->fcb
->inode_item
.st_size
= related
->fcb
->inode_item
.st_size
+ (2 * utf8
.Length
) - (2* oldutf8len
);
1665 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1666 related
->fcb
->inode_item
.sequence
++;
1667 related
->fcb
->inode_item
.st_ctime
= now
;
1668 related
->fcb
->inode_item
.st_mtime
= now
;
1670 related
->fcb
->inode_item_changed
= TRUE
;
1671 mark_fcb_dirty(related
->fcb
);
1672 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1674 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&oldfn
, name_offset
, NULL
, NULL
,
1675 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_OLD_NAME
, NULL
, NULL
);
1676 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&newfn
, name_offset
, NULL
, NULL
,
1677 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_NEW_NAME
, NULL
, NULL
);
1679 ExFreePool(oldfn
.Buffer
);
1680 ExFreePool(newfn
.Buffer
);
1682 Status
= STATUS_SUCCESS
;
1686 // We move files by moving the existing fileref to the new directory, and
1687 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1689 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
1691 fr2
= create_fileref(Vcb
);
1693 fr2
->fcb
= fileref
->fcb
;
1694 fr2
->fcb
->refcount
++;
1696 fr2
->oldutf8
= fileref
->oldutf8
;
1697 fr2
->oldindex
= fileref
->dc
->index
;
1698 fr2
->delete_on_close
= fileref
->delete_on_close
;
1699 fr2
->deleted
= TRUE
;
1700 fr2
->created
= fileref
->created
;
1701 fr2
->parent
= fileref
->parent
;
1704 if (!fr2
->oldutf8
.Buffer
) {
1705 fr2
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1706 if (!fr2
->oldutf8
.Buffer
) {
1707 ERR("out of memory\n");
1708 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1712 RtlCopyMemory(fr2
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1714 fr2
->oldutf8
.Length
= fr2
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1717 if (fr2
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1718 fr2
->fcb
->fileref
= fr2
;
1720 if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
)
1721 fileref
->fcb
->subvol
->parent
= related
->fcb
->subvol
->id
;
1723 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= 0;
1724 fileref
->oldutf8
.Buffer
= NULL
;
1725 fileref
->deleted
= FALSE
;
1726 fileref
->created
= TRUE
;
1727 fileref
->parent
= related
;
1729 ExAcquireResourceExclusiveLite(&fileref
->parent
->nonpaged
->children_lock
, TRUE
);
1730 InsertHeadList(&fileref
->list_entry
, &fr2
->list_entry
);
1731 RemoveEntryList(&fileref
->list_entry
);
1732 ExReleaseResourceLite(&fileref
->parent
->nonpaged
->children_lock
);
1734 mark_fileref_dirty(fr2
);
1735 mark_fileref_dirty(fileref
);
1738 // remove from old parent
1739 ExAcquireResourceExclusiveLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1740 RemoveEntryList(&fileref
->dc
->list_entry_index
);
1741 remove_dir_child_from_hash_lists(fr2
->parent
->fcb
, fileref
->dc
);
1742 ExReleaseResourceLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
);
1744 if (fileref
->dc
->utf8
.Length
!= utf8
.Length
|| RtlCompareMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
) != utf8
.Length
) {
1745 // handle changed name
1747 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1748 ExFreePool(fileref
->dc
->name
.Buffer
);
1749 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1751 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1752 if (!fileref
->dc
->utf8
.Buffer
) {
1753 ERR("out of memory\n");
1754 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1758 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1759 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1761 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1762 if (!fileref
->dc
->name
.Buffer
) {
1763 ERR("out of memory\n");
1764 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1768 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1769 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1771 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1772 if (!NT_SUCCESS(Status
)) {
1773 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1777 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1778 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1781 // add to new parent
1782 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1784 if (IsListEmpty(&related
->fcb
->dir_children_index
))
1785 fileref
->dc
->index
= 2;
1787 dir_child
* dc2
= CONTAINING_RECORD(related
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1789 fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1792 InsertTailList(&related
->fcb
->dir_children_index
, &fileref
->dc
->list_entry_index
);
1793 insert_dir_child_into_hash_lists(related
->fcb
, fileref
->dc
);
1794 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
1797 ExAcquireResourceExclusiveLite(&related
->nonpaged
->children_lock
, TRUE
);
1798 InsertTailList(&related
->children
, &fileref
->list_entry
);
1799 ExReleaseResourceLite(&related
->nonpaged
->children_lock
);
1801 if (fcb
->inode_item
.st_nlink
> 1) {
1802 // add new hardlink entry to fcb
1804 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1806 ERR("out of memory\n");
1807 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1811 hl
->parent
= related
->fcb
->inode
;
1812 hl
->index
= fileref
->dc
->index
;
1814 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
1815 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1817 if (!hl
->name
.Buffer
) {
1818 ERR("out of memory\n");
1820 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1824 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1826 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1827 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1829 if (!hl
->utf8
.Buffer
) {
1830 ERR("out of memory\n");
1831 ExFreePool(hl
->name
.Buffer
);
1833 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1837 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1839 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
1842 // delete old hardlink entry from fcb
1844 le
= fcb
->hardlinks
.Flink
;
1845 while (le
!= &fcb
->hardlinks
) {
1846 hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1848 if (hl
->parent
== fr2
->parent
->fcb
->inode
&& hl
->index
== fr2
->oldindex
) {
1849 RemoveEntryList(&hl
->list_entry
);
1851 if (hl
->utf8
.Buffer
)
1852 ExFreePool(hl
->utf8
.Buffer
);
1854 if (hl
->name
.Buffer
)
1855 ExFreePool(hl
->name
.Buffer
);
1864 // update inode's INODE_ITEM
1866 KeQuerySystemTime(&time
);
1867 win_time_to_unix(time
, &now
);
1869 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1870 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1871 fcb
->inode_item
.sequence
++;
1873 if (!ccb
->user_set_change_time
)
1874 fcb
->inode_item
.st_ctime
= now
;
1876 fcb
->inode_item_changed
= TRUE
;
1877 mark_fcb_dirty(fcb
);
1880 // update new parent's INODE_ITEM
1882 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1883 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1884 related
->fcb
->inode_item
.st_size
+= 2 * utf8len
;
1885 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1886 related
->fcb
->inode_item
.sequence
++;
1887 related
->fcb
->inode_item
.st_ctime
= now
;
1888 related
->fcb
->inode_item
.st_mtime
= now
;
1890 related
->fcb
->inode_item_changed
= TRUE
;
1891 mark_fcb_dirty(related
->fcb
);
1893 // update old parent's INODE_ITEM
1895 fr2
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1896 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1897 fr2
->parent
->fcb
->inode_item
.st_size
-= 2 * origutf8len
;
1898 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1899 fr2
->parent
->fcb
->inode_item
.sequence
++;
1900 fr2
->parent
->fcb
->inode_item
.st_ctime
= now
;
1901 fr2
->parent
->fcb
->inode_item
.st_mtime
= now
;
1903 free_fileref(Vcb
, fr2
);
1905 fr2
->parent
->fcb
->inode_item_changed
= TRUE
;
1906 mark_fcb_dirty(fr2
->parent
->fcb
);
1908 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1909 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1910 send_notification_fileref(fr2
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1912 Status
= STATUS_SUCCESS
;
1916 free_fileref(Vcb
, oldfileref
);
1918 if (!NT_SUCCESS(Status
) && related
)
1919 free_fileref(Vcb
, related
);
1921 if (!NT_SUCCESS(Status
) && fr2
)
1922 free_fileref(Vcb
, fr2
);
1924 if (NT_SUCCESS(Status
))
1925 clear_rollback(&rollback
);
1927 do_rollback(Vcb
, &rollback
);
1929 ExReleaseResourceLite(fcb
->Header
.Resource
);
1930 release_fcb_lock(Vcb
);
1931 ExReleaseResourceLite(&Vcb
->tree_lock
);
1936 NTSTATUS
stream_set_end_of_file_information(device_extension
* Vcb
, UINT16 end
, fcb
* fcb
, file_ref
* fileref
, BOOL advance_only
) {
1940 TRACE("setting new end to %llx bytes (currently %x)\n", end
, fcb
->adsdata
.Length
);
1942 if (!fileref
|| !fileref
->parent
) {
1943 ERR("no fileref for stream\n");
1944 return STATUS_INTERNAL_ERROR
;
1947 if (end
< fcb
->adsdata
.Length
) {
1949 return STATUS_SUCCESS
;
1951 TRACE("truncating stream to %llx bytes\n", end
);
1953 fcb
->adsdata
.Length
= end
;
1954 } else if (end
> fcb
->adsdata
.Length
) {
1955 TRACE("extending stream to %llx bytes\n", end
);
1957 if (end
> fcb
->adsmaxlen
) {
1958 ERR("error - xattr too long (%u > %u)\n", end
, fcb
->adsmaxlen
);
1959 return STATUS_DISK_FULL
;
1962 if (end
> fcb
->adsdata
.MaximumLength
) {
1963 char* data
= ExAllocatePoolWithTag(PagedPool
, end
, ALLOC_TAG
);
1965 ERR("out of memory\n");
1967 return STATUS_INSUFFICIENT_RESOURCES
;
1970 if (fcb
->adsdata
.Buffer
) {
1971 RtlCopyMemory(data
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
1972 ExFreePool(fcb
->adsdata
.Buffer
);
1975 fcb
->adsdata
.Buffer
= data
;
1976 fcb
->adsdata
.MaximumLength
= end
;
1979 RtlZeroMemory(&fcb
->adsdata
.Buffer
[fcb
->adsdata
.Length
], end
- fcb
->adsdata
.Length
);
1981 fcb
->adsdata
.Length
= end
;
1984 mark_fcb_dirty(fcb
);
1986 fcb
->Header
.AllocationSize
.QuadPart
= end
;
1987 fcb
->Header
.FileSize
.QuadPart
= end
;
1988 fcb
->Header
.ValidDataLength
.QuadPart
= end
;
1990 KeQuerySystemTime(&time
);
1991 win_time_to_unix(time
, &now
);
1993 fileref
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1994 fileref
->parent
->fcb
->inode_item
.sequence
++;
1995 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1997 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1998 mark_fcb_dirty(fileref
->parent
->fcb
);
2000 fileref
->parent
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2001 fileref
->parent
->fcb
->subvol
->root_item
.ctime
= now
;
2003 return STATUS_SUCCESS
;
2006 static NTSTATUS
set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
, BOOL prealloc
) {
2007 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
2008 fcb
* fcb
= FileObject
->FsContext
;
2009 ccb
* ccb
= FileObject
->FsContext2
;
2010 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2014 LIST_ENTRY rollback
;
2015 BOOL set_size
= FALSE
;
2019 ERR("fileref is NULL\n");
2020 return STATUS_INVALID_PARAMETER
;
2023 InitializeListHead(&rollback
);
2025 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2027 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2029 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2030 Status
= STATUS_FILE_CLOSED
;
2035 if (feofi
->EndOfFile
.QuadPart
> 0xffff) {
2036 Status
= STATUS_DISK_FULL
;
2040 if (feofi
->EndOfFile
.QuadPart
< 0) {
2041 Status
= STATUS_INVALID_PARAMETER
;
2045 Status
= stream_set_end_of_file_information(Vcb
, (UINT16
)feofi
->EndOfFile
.QuadPart
, fcb
, fileref
, advance_only
);
2047 if (NT_SUCCESS(Status
)) {
2048 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2049 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2050 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2054 filter
= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
2056 if (!ccb
->user_set_write_time
) {
2057 KeQuerySystemTime(&time
);
2058 win_time_to_unix(time
, &fileref
->parent
->fcb
->inode_item
.st_mtime
);
2059 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2061 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2062 mark_fcb_dirty(fileref
->parent
->fcb
);
2065 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED_STREAM
, &fileref
->dc
->name
);
2070 TRACE("file: %S\n", file_desc(FileObject
));
2071 TRACE("paging IO: %s\n", Irp
->Flags
& IRP_PAGING_IO
? "TRUE" : "FALSE");
2072 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2073 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2075 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi
->EndOfFile
.QuadPart
, fcb
->inode_item
.st_size
);
2077 if ((UINT64
)feofi
->EndOfFile
.QuadPart
< fcb
->inode_item
.st_size
) {
2079 Status
= STATUS_SUCCESS
;
2083 TRACE("truncating file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2085 if (!MmCanFileBeTruncated(&fcb
->nonpaged
->segment_object
, &feofi
->EndOfFile
)) {
2086 Status
= STATUS_USER_MAPPED_FILE
;
2090 Status
= truncate_file(fcb
, feofi
->EndOfFile
.QuadPart
, Irp
, &rollback
);
2091 if (!NT_SUCCESS(Status
)) {
2092 ERR("error - truncate_file failed\n");
2095 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
> fcb
->inode_item
.st_size
) {
2096 if (Irp
->Flags
& IRP_PAGING_IO
) {
2097 TRACE("paging IO tried to extend file size\n");
2098 Status
= STATUS_SUCCESS
;
2102 TRACE("extending file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2104 Status
= extend_file(fcb
, fileref
, feofi
->EndOfFile
.QuadPart
, prealloc
, NULL
, &rollback
);
2105 if (!NT_SUCCESS(Status
)) {
2106 ERR("error - extend_file failed\n");
2109 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
== fcb
->inode_item
.st_size
&& advance_only
) {
2110 Status
= STATUS_SUCCESS
;
2114 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2115 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2116 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2119 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2121 if (!ccb
->user_set_write_time
) {
2122 KeQuerySystemTime(&time
);
2123 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2124 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2127 fcb
->inode_item_changed
= TRUE
;
2128 mark_fcb_dirty(fcb
);
2129 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2131 Status
= STATUS_SUCCESS
;
2134 if (NT_SUCCESS(Status
))
2135 clear_rollback(&rollback
);
2137 do_rollback(Vcb
, &rollback
);
2139 ExReleaseResourceLite(fcb
->Header
.Resource
);
2143 CcSetFileSizes(FileObject
, &ccfs
);
2144 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2145 Status
= _SEH2_GetExceptionCode();
2148 if (!NT_SUCCESS(Status
))
2149 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2152 ExReleaseResourceLite(&Vcb
->tree_lock
);
2157 static NTSTATUS
set_position_information(PFILE_OBJECT FileObject
, PIRP Irp
) {
2158 FILE_POSITION_INFORMATION
* fpi
= (FILE_POSITION_INFORMATION
*)Irp
->AssociatedIrp
.SystemBuffer
;
2160 TRACE("setting the position on %S to %llx\n", file_desc(FileObject
), fpi
->CurrentByteOffset
.QuadPart
);
2162 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2164 FileObject
->CurrentByteOffset
= fpi
->CurrentByteOffset
;
2166 return STATUS_SUCCESS
;
2169 static NTSTATUS
set_link_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
2170 FILE_LINK_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
2171 fcb
*fcb
= FileObject
->FsContext
, *tfofcb
, *parfcb
;
2172 ccb
* ccb
= FileObject
->FsContext2
;
2173 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
2175 ULONG fnlen
, utf8len
;
2176 UNICODE_STRING fnus
;
2181 LIST_ENTRY rollback
;
2184 SECURITY_SUBJECT_CONTEXT subjcont
;
2185 dir_child
* dc
= NULL
;
2187 InitializeListHead(&rollback
);
2189 // FIXME - check fli length
2190 // FIXME - don't ignore fli->RootDirectory
2192 TRACE("ReplaceIfExists = %x\n", fli
->ReplaceIfExists
);
2193 TRACE("RootDirectory = %p\n", fli
->RootDirectory
);
2194 TRACE("FileNameLength = %x\n", fli
->FileNameLength
);
2195 TRACE("FileName = %.*S\n", fli
->FileNameLength
/ sizeof(WCHAR
), fli
->FileName
);
2198 fnlen
= fli
->FileNameLength
/ sizeof(WCHAR
);
2201 if (!fileref
|| !fileref
->parent
) {
2202 ERR("no fileref set and no directory given\n");
2203 return STATUS_INVALID_PARAMETER
;
2206 parfcb
= fileref
->parent
->fcb
;
2211 tfofcb
= tfo
->FsContext
;
2214 while (fnlen
> 0 && (fli
->FileName
[fnlen
- 1] == '/' || fli
->FileName
[fnlen
- 1] == '\\'))
2218 return STATUS_INVALID_PARAMETER
;
2220 for (i
= fnlen
- 1; i
>= 0; i
--) {
2221 if (fli
->FileName
[i
] == '\\' || fli
->FileName
[i
] == '/') {
2222 fn
= &fli
->FileName
[i
+1];
2223 fnlen
= (fli
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
2229 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2230 acquire_fcb_lock_exclusive(Vcb
);
2231 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2233 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2234 WARN("tried to create hard link on directory\n");
2235 Status
= STATUS_FILE_IS_A_DIRECTORY
;
2240 WARN("tried to create hard link on stream\n");
2241 Status
= STATUS_INVALID_PARAMETER
;
2245 if (fcb
->inode_item
.st_nlink
>= 65535) {
2246 Status
= STATUS_TOO_MANY_LINKS
;
2251 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
2253 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
2255 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2256 if (!NT_SUCCESS(Status
))
2259 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
2260 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
2262 ERR("out of memory\n");
2263 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2267 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2268 if (!NT_SUCCESS(Status
))
2271 if (tfo
&& tfo
->FsContext2
) {
2272 struct _ccb
* relatedccb
= tfo
->FsContext2
;
2274 related
= relatedccb
->fileref
;
2275 increase_fileref_refcount(related
);
2278 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2280 if (NT_SUCCESS(Status
)) {
2281 if (!oldfileref
->deleted
) {
2282 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref
));
2284 if (!fli
->ReplaceIfExists
) {
2285 Status
= STATUS_OBJECT_NAME_COLLISION
;
2287 } else if (oldfileref
->open_count
>= 1 && !oldfileref
->deleted
) {
2288 WARN("trying to overwrite open file\n");
2289 Status
= STATUS_ACCESS_DENIED
;
2291 } else if (fileref
== oldfileref
) {
2292 Status
= STATUS_ACCESS_DENIED
;
2296 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2297 WARN("trying to overwrite directory\n");
2298 Status
= STATUS_ACCESS_DENIED
;
2302 free_fileref(Vcb
, oldfileref
);
2308 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2310 if (!NT_SUCCESS(Status
)) {
2311 ERR("open_fileref returned %08x\n", Status
);
2316 SeCaptureSubjectContext(&subjcont
);
2318 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, FILE_ADD_FILE
, 0, NULL
,
2319 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2320 SeReleaseSubjectContext(&subjcont
);
2321 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2325 SeReleaseSubjectContext(&subjcont
);
2327 if (fcb
->subvol
!= parfcb
->subvol
) {
2328 WARN("can't create hard link over subvolume boundary\n");
2329 Status
= STATUS_INVALID_PARAMETER
;
2334 SeCaptureSubjectContext(&subjcont
);
2336 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
2337 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2338 SeReleaseSubjectContext(&subjcont
);
2339 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2343 SeReleaseSubjectContext(&subjcont
);
2345 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
2346 if (!NT_SUCCESS(Status
)) {
2347 ERR("delete_fileref returned %08x\n", Status
);
2352 fr2
= create_fileref(Vcb
);
2357 fr2
->created
= TRUE
;
2358 fr2
->parent
= related
;
2360 Status
= add_dir_child(related
->fcb
, fcb
->inode
, FALSE
, &utf8
, &fnus
, fcb
->type
, &dc
);
2361 if (!NT_SUCCESS(Status
))
2362 WARN("add_dir_child returned %08x\n", Status
);
2367 ExAcquireResourceExclusiveLite(&related
->nonpaged
->children_lock
, TRUE
);
2368 InsertTailList(&related
->children
, &fr2
->list_entry
);
2369 ExReleaseResourceLite(&related
->nonpaged
->children_lock
);
2371 // add hardlink for existing fileref, if it's not there already
2372 if (IsListEmpty(&fcb
->hardlinks
)) {
2373 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2375 ERR("out of memory\n");
2376 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2380 hl
->parent
= fileref
->parent
->fcb
->inode
;
2381 hl
->index
= fileref
->dc
->index
;
2383 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2384 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
2386 if (!hl
->name
.Buffer
) {
2387 ERR("out of memory\n");
2389 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2393 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2395 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
2396 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2398 if (!hl
->utf8
.Buffer
) {
2399 ERR("out of memory\n");
2400 ExFreePool(hl
->name
.Buffer
);
2402 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2406 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
2408 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2411 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2413 ERR("out of memory\n");
2414 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2418 hl
->parent
= related
->fcb
->inode
;
2419 hl
->index
= dc
->index
;
2421 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2422 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
2424 if (!hl
->name
.Buffer
) {
2425 ERR("out of memory\n");
2427 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2431 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2433 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= utf8
.Length
;
2434 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2436 if (!hl
->utf8
.Buffer
) {
2437 ERR("out of memory\n");
2438 ExFreePool(hl
->name
.Buffer
);
2440 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2444 RtlCopyMemory(hl
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
2445 ExFreePool(utf8
.Buffer
);
2447 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2449 mark_fileref_dirty(fr2
);
2450 free_fileref(Vcb
, fr2
);
2452 // update inode's INODE_ITEM
2454 KeQuerySystemTime(&time
);
2455 win_time_to_unix(time
, &now
);
2457 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2458 fcb
->inode_item
.sequence
++;
2459 fcb
->inode_item
.st_nlink
++;
2461 if (!ccb
->user_set_change_time
)
2462 fcb
->inode_item
.st_ctime
= now
;
2464 fcb
->inode_item_changed
= TRUE
;
2465 mark_fcb_dirty(fcb
);
2467 // update parent's INODE_ITEM
2469 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2470 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2471 parfcb
->inode_item
.st_size
+= 2 * utf8len
;
2472 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2473 parfcb
->inode_item
.sequence
++;
2474 parfcb
->inode_item
.st_ctime
= now
;
2476 parfcb
->inode_item_changed
= TRUE
;
2477 mark_fcb_dirty(parfcb
);
2479 send_notification_fileref(fr2
, FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2481 Status
= STATUS_SUCCESS
;
2485 free_fileref(Vcb
, oldfileref
);
2487 if (!NT_SUCCESS(Status
) && related
)
2488 free_fileref(Vcb
, related
);
2490 if (!NT_SUCCESS(Status
) && fr2
)
2491 free_fileref(Vcb
, fr2
);
2493 if (NT_SUCCESS(Status
))
2494 clear_rollback(&rollback
);
2496 do_rollback(Vcb
, &rollback
);
2498 ExReleaseResourceLite(fcb
->Header
.Resource
);
2499 release_fcb_lock(Vcb
);
2500 ExReleaseResourceLite(&Vcb
->tree_lock
);
2505 static NTSTATUS
set_valid_data_length_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
2506 FILE_VALID_DATA_LENGTH_INFORMATION
* fvdli
= Irp
->AssociatedIrp
.SystemBuffer
;
2507 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2508 fcb
* fcb
= FileObject
->FsContext
;
2509 ccb
* ccb
= FileObject
->FsContext2
;
2510 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2514 LIST_ENTRY rollback
;
2515 BOOL set_size
= FALSE
;
2518 if (IrpSp
->Parameters
.SetFile
.Length
< sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
)) {
2519 ERR("input buffer length was %u, expected %u\n", IrpSp
->Parameters
.SetFile
.Length
, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
));
2520 return STATUS_INVALID_PARAMETER
;
2524 ERR("fileref is NULL\n");
2525 return STATUS_INVALID_PARAMETER
;
2528 InitializeListHead(&rollback
);
2530 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2532 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2534 if (fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
) {
2535 Status
= STATUS_INVALID_PARAMETER
;
2539 if (fvdli
->ValidDataLength
.QuadPart
<= fcb
->Header
.ValidDataLength
.QuadPart
|| fvdli
->ValidDataLength
.QuadPart
> fcb
->Header
.FileSize
.QuadPart
) {
2540 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli
->ValidDataLength
.QuadPart
,
2541 fcb
->Header
.ValidDataLength
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
);
2542 Status
= STATUS_INVALID_PARAMETER
;
2546 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2547 Status
= STATUS_FILE_CLOSED
;
2551 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2552 // and we set it to the max anyway.
2554 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2555 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2556 ccfs
.ValidDataLength
= fvdli
->ValidDataLength
;
2559 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2561 if (!ccb
->user_set_write_time
) {
2562 KeQuerySystemTime(&time
);
2563 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2564 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2567 fcb
->inode_item_changed
= TRUE
;
2568 mark_fcb_dirty(fcb
);
2570 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2572 Status
= STATUS_SUCCESS
;
2575 if (NT_SUCCESS(Status
))
2576 clear_rollback(&rollback
);
2578 do_rollback(Vcb
, &rollback
);
2580 ExReleaseResourceLite(fcb
->Header
.Resource
);
2584 CcSetFileSizes(FileObject
, &ccfs
);
2585 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2586 Status
= _SEH2_GetExceptionCode();
2589 if (!NT_SUCCESS(Status
))
2590 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2592 fcb
->Header
.AllocationSize
= ccfs
.AllocationSize
;
2595 ExReleaseResourceLite(&Vcb
->tree_lock
);
2600 _Dispatch_type_(IRP_MJ_SET_INFORMATION
)
2601 _Function_class_(DRIVER_DISPATCH
)
2602 NTSTATUS NTAPI
drv_set_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2604 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2605 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2606 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
2607 ccb
* ccb
= IrpSp
->FileObject
->FsContext2
;
2610 FsRtlEnterFileSystem();
2612 top_level
= is_top_level(Irp
);
2614 Irp
->IoStatus
.Information
= 0;
2616 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2617 Status
= vol_set_information(DeviceObject
, Irp
);
2619 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2620 Status
= STATUS_INVALID_PARAMETER
;
2624 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
2625 Status
= STATUS_ACCESS_DENIED
;
2629 if (Vcb
->readonly
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
) {
2630 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2636 Status
= STATUS_INVALID_PARAMETER
;
2642 Status
= STATUS_INVALID_PARAMETER
;
2646 if (fcb
!= Vcb
->dummy_fcb
&& is_subvol_readonly(fcb
->subvol
, Irp
) && IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
&&
2647 (fcb
->inode
!= SUBVOL_ROOT_INODE
|| (IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileBasicInformation
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileRenameInformation
))) {
2648 Status
= STATUS_ACCESS_DENIED
;
2652 Status
= STATUS_NOT_IMPLEMENTED
;
2654 TRACE("set information\n");
2656 switch (IrpSp
->Parameters
.SetFile
.FileInformationClass
) {
2657 case FileAllocationInformation
:
2659 TRACE("FileAllocationInformation\n");
2661 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
2662 WARN("insufficient privileges\n");
2663 Status
= STATUS_ACCESS_DENIED
;
2667 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, FALSE
, TRUE
);
2671 case FileBasicInformation
:
2673 TRACE("FileBasicInformation\n");
2675 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
2676 WARN("insufficient privileges\n");
2677 Status
= STATUS_ACCESS_DENIED
;
2681 Status
= set_basic_information(Vcb
, Irp
, IrpSp
->FileObject
);
2686 case FileDispositionInformation
:
2688 TRACE("FileDispositionInformation\n");
2690 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& DELETE
)) {
2691 WARN("insufficient privileges\n");
2692 Status
= STATUS_ACCESS_DENIED
;
2696 Status
= set_disposition_information(Vcb
, Irp
, IrpSp
->FileObject
);
2701 case FileEndOfFileInformation
:
2703 TRACE("FileEndOfFileInformation\n");
2705 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2706 WARN("insufficient privileges\n");
2707 Status
= STATUS_ACCESS_DENIED
;
2711 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.AdvanceOnly
, FALSE
);
2716 case FileLinkInformation
:
2717 TRACE("FileLinkInformation\n");
2718 Status
= set_link_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2721 case FilePositionInformation
:
2722 TRACE("FilePositionInformation\n");
2723 Status
= set_position_information(IrpSp
->FileObject
, Irp
);
2726 case FileRenameInformation
:
2727 TRACE("FileRenameInformation\n");
2728 // FIXME - make this work with streams
2729 Status
= set_rename_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2732 case FileValidDataLengthInformation
:
2734 TRACE("FileValidDataLengthInformation\n");
2736 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2737 WARN("insufficient privileges\n");
2738 Status
= STATUS_ACCESS_DENIED
;
2742 Status
= set_valid_data_length_information(Vcb
, Irp
, IrpSp
->FileObject
);
2748 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.SetFile
.FileInformationClass
);
2752 Irp
->IoStatus
.Status
= Status
;
2754 TRACE("returning %08x\n", Status
);
2756 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2759 IoSetTopLevelIrp(NULL
);
2761 FsRtlExitFileSystem();
2766 static NTSTATUS
fill_in_file_basic_information(FILE_BASIC_INFORMATION
* fbi
, INODE_ITEM
* ii
, LONG
* length
, fcb
* fcb
, file_ref
* fileref
) {
2767 RtlZeroMemory(fbi
, sizeof(FILE_BASIC_INFORMATION
));
2769 *length
-= sizeof(FILE_BASIC_INFORMATION
);
2771 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2774 KeQuerySystemTime(&time
);
2775 fbi
->CreationTime
= fbi
->LastAccessTime
= fbi
->LastWriteTime
= fbi
->ChangeTime
= time
;
2777 fbi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2778 fbi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2779 fbi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2780 fbi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2784 if (!fileref
|| !fileref
->parent
) {
2785 ERR("no fileref for stream\n");
2786 return STATUS_INTERNAL_ERROR
;
2788 fbi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2790 fbi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2792 return STATUS_SUCCESS
;
2795 static NTSTATUS
fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION
* fnoi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2798 if (*length
< (LONG
)sizeof(FILE_NETWORK_OPEN_INFORMATION
)) {
2800 return STATUS_BUFFER_OVERFLOW
;
2803 RtlZeroMemory(fnoi
, sizeof(FILE_NETWORK_OPEN_INFORMATION
));
2805 *length
-= sizeof(FILE_NETWORK_OPEN_INFORMATION
);
2808 if (!fileref
|| !fileref
->parent
) {
2809 ERR("no fileref for stream\n");
2810 return STATUS_INTERNAL_ERROR
;
2813 ii
= &fileref
->parent
->fcb
->inode_item
;
2815 ii
= &fcb
->inode_item
;
2817 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2820 KeQuerySystemTime(&time
);
2821 fnoi
->CreationTime
= fnoi
->LastAccessTime
= fnoi
->LastWriteTime
= fnoi
->ChangeTime
= time
;
2823 fnoi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2824 fnoi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2825 fnoi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2826 fnoi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2830 fnoi
->AllocationSize
.QuadPart
= fnoi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2831 fnoi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2833 fnoi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2834 fnoi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2835 fnoi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2838 return STATUS_SUCCESS
;
2841 static NTSTATUS
fill_in_file_standard_information(FILE_STANDARD_INFORMATION
* fsi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2842 RtlZeroMemory(fsi
, sizeof(FILE_STANDARD_INFORMATION
));
2844 *length
-= sizeof(FILE_STANDARD_INFORMATION
);
2847 if (!fileref
|| !fileref
->parent
) {
2848 ERR("no fileref for stream\n");
2849 return STATUS_INTERNAL_ERROR
;
2852 fsi
->AllocationSize
.QuadPart
= fsi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2853 fsi
->NumberOfLinks
= fileref
->parent
->fcb
->inode_item
.st_nlink
;
2854 fsi
->Directory
= FALSE
;
2856 fsi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2857 fsi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2858 fsi
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
2859 fsi
->Directory
= S_ISDIR(fcb
->inode_item
.st_mode
);
2862 TRACE("length = %llu\n", fsi
->EndOfFile
.QuadPart
);
2864 fsi
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
2866 return STATUS_SUCCESS
;
2869 static NTSTATUS
fill_in_file_internal_information(FILE_INTERNAL_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
2870 *length
-= sizeof(FILE_INTERNAL_INFORMATION
);
2872 fii
->IndexNumber
.QuadPart
= make_file_id(fcb
->subvol
, fcb
->inode
);
2874 return STATUS_SUCCESS
;
2877 static NTSTATUS
fill_in_file_ea_information(FILE_EA_INFORMATION
* eai
, fcb
* fcb
, LONG
* length
) {
2878 *length
-= sizeof(FILE_EA_INFORMATION
);
2880 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2881 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2882 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2885 eai
->EaSize
= fcb
->ealen
;
2887 return STATUS_SUCCESS
;
2890 static NTSTATUS
fill_in_file_position_information(FILE_POSITION_INFORMATION
* fpi
, PFILE_OBJECT FileObject
, LONG
* length
) {
2891 RtlZeroMemory(fpi
, sizeof(FILE_POSITION_INFORMATION
));
2893 *length
-= sizeof(FILE_POSITION_INFORMATION
);
2895 fpi
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
2897 return STATUS_SUCCESS
;
2900 NTSTATUS
fileref_get_filename(file_ref
* fileref
, PUNICODE_STRING fn
, USHORT
* name_offset
, ULONG
* preqlen
) {
2905 BOOL overflow
= FALSE
;
2907 // FIXME - we need a lock on filerefs' filepart
2909 if (fileref
== fileref
->fcb
->Vcb
->root_fileref
) {
2910 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2911 fn
->Buffer
[0] = '\\';
2912 fn
->Length
= sizeof(WCHAR
);
2917 return STATUS_SUCCESS
;
2920 *preqlen
= sizeof(WCHAR
);
2923 return STATUS_BUFFER_OVERFLOW
;
2930 while (fr
->parent
) {
2934 return STATUS_INTERNAL_ERROR
;
2937 if (fr
->dc
->name
.Length
+ sizeof(WCHAR
) + fn
->Length
> fn
->MaximumLength
)
2942 movelen
= fn
->MaximumLength
- fr
->dc
->name
.Length
- sizeof(WCHAR
);
2944 movelen
= fn
->Length
;
2946 if ((!overflow
|| fn
->MaximumLength
> fr
->dc
->name
.Length
+ sizeof(WCHAR
)) && movelen
> 0) {
2947 RtlMoveMemory(&fn
->Buffer
[(fr
->dc
->name
.Length
/ sizeof(WCHAR
)) + 1], fn
->Buffer
, movelen
);
2948 offset
+= fr
->dc
->name
.Length
+ sizeof(WCHAR
);
2951 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2952 fn
->Buffer
[0] = fr
->fcb
->ads
? ':' : '\\';
2953 fn
->Length
+= sizeof(WCHAR
);
2955 if (fn
->MaximumLength
> sizeof(WCHAR
)) {
2956 RtlCopyMemory(&fn
->Buffer
[1], fr
->dc
->name
.Buffer
, min(fr
->dc
->name
.Length
, fn
->MaximumLength
- sizeof(WCHAR
)));
2957 fn
->Length
+= fr
->dc
->name
.Length
;
2960 if (fn
->Length
> fn
->MaximumLength
) {
2961 fn
->Length
= fn
->MaximumLength
;
2966 reqlen
+= sizeof(WCHAR
) + fr
->dc
->name
.Length
;
2971 offset
+= sizeof(WCHAR
);
2976 Status
= STATUS_BUFFER_OVERFLOW
;
2979 *name_offset
= offset
;
2981 Status
= STATUS_SUCCESS
;
2987 static NTSTATUS
fill_in_file_name_information(FILE_NAME_INFORMATION
* fni
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2991 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2992 UINT16 datasuflen
= sizeof(datasuf
) - sizeof(WCHAR
);
2995 ERR("called without fileref\n");
2996 return STATUS_INVALID_PARAMETER
;
2999 *length
-= (LONG
)offsetof(FILE_NAME_INFORMATION
, FileName
[0]);
3001 TRACE("maximum length is %u\n", *length
);
3002 fni
->FileNameLength
= 0;
3004 fni
->FileName
[0] = 0;
3006 fn
.Buffer
= fni
->FileName
;
3008 fn
.MaximumLength
= (UINT16
)*length
;
3010 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
3011 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
3012 ERR("fileref_get_filename returned %08x\n", Status
);
3017 if (Status
== STATUS_BUFFER_OVERFLOW
)
3018 reqlen
+= datasuflen
;
3020 if (fn
.Length
+ datasuflen
> fn
.MaximumLength
) {
3021 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, fn
.MaximumLength
- fn
.Length
);
3022 reqlen
+= datasuflen
;
3023 Status
= STATUS_BUFFER_OVERFLOW
;
3025 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, datasuflen
);
3026 fn
.Length
+= datasuflen
;
3031 if (Status
== STATUS_BUFFER_OVERFLOW
) {
3033 fni
->FileNameLength
= reqlen
;
3034 TRACE("%.*S (truncated)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3036 *length
-= fn
.Length
;
3037 fni
->FileNameLength
= fn
.Length
;
3038 TRACE("%.*S\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3044 static NTSTATUS
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION
* ati
, fcb
* fcb
, ccb
* ccb
, LONG
* length
) {
3045 *length
-= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
);
3048 if (!ccb
->fileref
|| !ccb
->fileref
->parent
) {
3049 ERR("no fileref for stream\n");
3050 return STATUS_INTERNAL_ERROR
;
3053 ati
->FileAttributes
= ccb
->fileref
->parent
->fcb
->atts
;
3055 ati
->FileAttributes
= fcb
->atts
;
3057 if (!(ati
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3058 ati
->ReparseTag
= 0;
3060 ati
->ReparseTag
= get_reparse_tag_fcb(fcb
);
3062 return STATUS_SUCCESS
;
3065 static NTSTATUS
fill_in_file_stream_information(FILE_STREAM_INFORMATION
* fsi
, file_ref
* fileref
, LONG
* length
) {
3068 FILE_STREAM_INFORMATION
*entry
, *lastentry
;
3071 static const WCHAR datasuf
[] = L
":$DATA";
3075 ERR("fileref was NULL\n");
3076 return STATUS_INVALID_PARAMETER
;
3079 suf
.Buffer
= (WCHAR
*)datasuf
;
3080 suf
.Length
= suf
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
3082 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
3083 reqsize
= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
);
3087 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
3089 le
= fileref
->fcb
->dir_children_index
.Flink
;
3090 while (le
!= &fileref
->fcb
->dir_children_index
) {
3091 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3093 if (dc
->index
== 0) {
3094 reqsize
= (ULONG
)sector_align(reqsize
, sizeof(LONGLONG
));
3095 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
;
3102 TRACE("length = %i, reqsize = %u\n", *length
, reqsize
);
3104 if (reqsize
> *length
) {
3105 Status
= STATUS_BUFFER_OVERFLOW
;
3112 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3115 entry
->NextEntryOffset
= 0;
3116 entry
->StreamNameLength
= suf
.Length
+ sizeof(WCHAR
);
3117 entry
->StreamSize
.QuadPart
= fileref
->fcb
->inode_item
.st_size
;
3118 entry
->StreamAllocationSize
.QuadPart
= fcb_alloc_size(fileref
->fcb
);
3120 entry
->StreamName
[0] = ':';
3121 RtlCopyMemory(&entry
->StreamName
[1], suf
.Buffer
, suf
.Length
);
3123 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
), sizeof(LONGLONG
));
3126 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3129 le
= fileref
->fcb
->dir_children_index
.Flink
;
3130 while (le
!= &fileref
->fcb
->dir_children_index
) {
3131 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3133 if (dc
->index
== 0) {
3136 entry
->NextEntryOffset
= 0;
3137 entry
->StreamNameLength
= dc
->name
.Length
+ suf
.Length
+ sizeof(WCHAR
);
3140 entry
->StreamSize
.QuadPart
= dc
->fileref
->fcb
->adsdata
.Length
;
3142 entry
->StreamSize
.QuadPart
= dc
->size
;
3144 entry
->StreamAllocationSize
.QuadPart
= entry
->StreamSize
.QuadPart
;
3146 entry
->StreamName
[0] = ':';
3148 RtlCopyMemory(&entry
->StreamName
[1], dc
->name
.Buffer
, dc
->name
.Length
);
3149 RtlCopyMemory(&entry
->StreamName
[1 + (dc
->name
.Length
/ sizeof(WCHAR
))], suf
.Buffer
, suf
.Length
);
3152 lastentry
->NextEntryOffset
= (UINT32
)((UINT8
*)entry
- (UINT8
*)lastentry
);
3154 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
, sizeof(LONGLONG
));
3157 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3166 Status
= STATUS_SUCCESS
;
3169 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
3175 static NTSTATUS
fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION
* fsli
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3176 TRACE("FileStandardLinkInformation\n");
3178 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3180 fsli
->NumberOfAccessibleLinks
= fcb
->inode_item
.st_nlink
;
3181 fsli
->TotalNumberOfLinks
= fcb
->inode_item
.st_nlink
;
3182 fsli
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
3183 fsli
->Directory
= (!fcb
->ads
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) ? TRUE
: FALSE
;
3185 *length
-= sizeof(FILE_STANDARD_LINK_INFORMATION
);
3187 return STATUS_SUCCESS
;
3189 #endif /* __REACTOS__ */
3191 NTSTATUS
open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
3192 root
* subvol
, UINT64 inode
, file_ref
** pfr
, PIRP Irp
) {
3196 UNICODE_STRING name
;
3197 BOOL hl_alloc
= FALSE
;
3198 file_ref
*parfr
, *fr
;
3200 Status
= open_fcb(Vcb
, subvol
, inode
, 0, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
3201 if (!NT_SUCCESS(Status
)) {
3202 ERR("open_fcb returned %08x\n", Status
);
3207 *pfr
= fcb
->fileref
;
3208 increase_fileref_refcount(fcb
->fileref
);
3209 return STATUS_SUCCESS
;
3212 // find hardlink if fcb doesn't have any loaded
3213 if (IsListEmpty(&fcb
->hardlinks
)) {
3217 searchkey
.obj_id
= fcb
->inode
;
3218 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
3219 searchkey
.offset
= 0xffffffffffffffff;
3221 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3222 if (!NT_SUCCESS(Status
)) {
3223 ERR("find_item returned %08x\n", Status
);
3228 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
3229 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
3233 ir
= (INODE_REF
*)tp
.item
->data
;
3235 parent
= tp
.item
->key
.offset
;
3237 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
3238 if (!NT_SUCCESS(Status
)) {
3239 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3244 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3249 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3252 ERR("out of memory\n");
3254 return STATUS_INSUFFICIENT_RESOURCES
;
3257 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
3258 if (!NT_SUCCESS(Status
)) {
3259 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3260 ExFreePool(name
.Buffer
);
3267 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
3271 ier
= (INODE_EXTREF
*)tp
.item
->data
;
3275 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
3276 if (!NT_SUCCESS(Status
)) {
3277 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3282 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3287 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3290 ERR("out of memory\n");
3292 return STATUS_INSUFFICIENT_RESOURCES
;
3295 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
3296 if (!NT_SUCCESS(Status
)) {
3297 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3298 ExFreePool(name
.Buffer
);
3309 hardlink
* hl
= CONTAINING_RECORD(fcb
->hardlinks
.Flink
, hardlink
, list_entry
);
3312 parent
= hl
->parent
;
3316 ERR("subvol %llx, inode %llx has no hardlinks\n", subvol
->id
, inode
);
3318 if (hl_alloc
) ExFreePool(name
.Buffer
);
3319 return STATUS_INVALID_PARAMETER
;
3322 if (parent
== inode
) { // subvolume root
3326 searchkey
.obj_id
= subvol
->id
;
3327 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
3328 searchkey
.offset
= 0xffffffffffffffff;
3330 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3331 if (!NT_SUCCESS(Status
)) {
3332 ERR("find_item returned %08x\n", Status
);
3334 if (hl_alloc
) ExFreePool(name
.Buffer
);
3338 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3339 ROOT_REF
* rr
= (ROOT_REF
*)tp
.item
->data
;
3344 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
3345 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
));
3347 if (hl_alloc
) ExFreePool(name
.Buffer
);
3348 return STATUS_INTERNAL_ERROR
;
3351 if (tp
.item
->size
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
3352 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
);
3354 if (hl_alloc
) ExFreePool(name
.Buffer
);
3355 return STATUS_INTERNAL_ERROR
;
3358 le
= Vcb
->roots
.Flink
;
3359 while (le
!= &Vcb
->roots
) {
3360 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
3362 if (r2
->id
== tp
.item
->key
.offset
) {
3371 ERR("couldn't find subvol %llx\n", tp
.item
->key
.offset
);
3373 if (hl_alloc
) ExFreePool(name
.Buffer
);
3374 return STATUS_INTERNAL_ERROR
;
3377 Status
= open_fileref_by_inode(Vcb
, r
, rr
->dir
, &parfr
, Irp
);
3378 if (!NT_SUCCESS(Status
)) {
3379 ERR("open_fileref_by_inode returned %08x\n", Status
);
3381 if (hl_alloc
) ExFreePool(name
.Buffer
);
3386 ExFreePool(name
.Buffer
);
3390 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, rr
->name
, rr
->n
);
3391 if (!NT_SUCCESS(Status
)) {
3392 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3397 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
3402 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
3405 ERR("out of memory\n");
3407 return STATUS_INSUFFICIENT_RESOURCES
;
3410 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, rr
->name
, rr
->n
);
3411 if (!NT_SUCCESS(Status
)) {
3412 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3413 ExFreePool(name
.Buffer
);
3421 ERR("couldn't find parent for subvol %llx\n", subvol
->id
);
3423 if (hl_alloc
) ExFreePool(name
.Buffer
);
3424 return STATUS_INTERNAL_ERROR
;
3427 Status
= open_fileref_by_inode(Vcb
, subvol
, parent
, &parfr
, Irp
);
3428 if (!NT_SUCCESS(Status
)) {
3429 ERR("open_fileref_by_inode returned %08x\n", Status
);
3433 ExFreePool(name
.Buffer
);
3439 Status
= open_fileref_child(Vcb
, parfr
, &name
, TRUE
, TRUE
, FALSE
, PagedPool
, &fr
, Irp
);
3441 if (!NT_SUCCESS(Status
)) {
3442 ERR("open_fileref_child returned %08x\n", Status
);
3445 ExFreePool(name
.Buffer
);
3448 free_fileref(Vcb
, parfr
);
3456 ExFreePool(name
.Buffer
);
3459 free_fileref(Vcb
, parfr
);
3461 return STATUS_SUCCESS
;
3465 static NTSTATUS
fill_in_hard_link_information(FILE_LINKS_INFORMATION
* fli
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3469 FILE_LINK_ENTRY_INFORMATION
* feli
;
3470 BOOL overflow
= FALSE
;
3471 fcb
* fcb
= fileref
->fcb
;
3475 return STATUS_INVALID_PARAMETER
;
3477 if (*length
< (LONG
)offsetof(FILE_LINKS_INFORMATION
, Entry
))
3478 return STATUS_INVALID_PARAMETER
;
3480 RtlZeroMemory(fli
, *length
);
3482 bytes_needed
= offsetof(FILE_LINKS_INFORMATION
, Entry
);
3486 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3488 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
3491 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
)
3492 namelen
= sizeof(WCHAR
);
3494 namelen
= fileref
->dc
->name
.Length
;
3496 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) - sizeof(WCHAR
) + namelen
;
3498 if (bytes_needed
> *length
)
3504 feli
->NextEntryOffset
= 0;
3505 feli
->ParentFileId
= 0; // we use an inode of 0 to mean the parent of a subvolume
3507 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
) {
3508 feli
->FileNameLength
= 1;
3509 feli
->FileName
[0] = '.';
3511 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3512 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3515 fli
->EntriesReturned
++;
3520 acquire_fcb_lock_exclusive(fcb
->Vcb
);
3522 if (IsListEmpty(&fcb
->hardlinks
)) {
3523 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fileref
->dc
->name
.Length
- sizeof(WCHAR
);
3525 if (bytes_needed
> *length
)
3531 feli
->NextEntryOffset
= 0;
3532 feli
->ParentFileId
= fileref
->parent
->fcb
->inode
;
3533 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3534 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3536 fli
->EntriesReturned
++;
3541 le
= fcb
->hardlinks
.Flink
;
3542 while (le
!= &fcb
->hardlinks
) {
3543 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
3546 TRACE("parent %llx, index %llx, name %.*S\n", hl
->parent
, hl
->index
, hl
->name
.Length
/ sizeof(WCHAR
), hl
->name
.Buffer
);
3548 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, Irp
);
3550 if (!NT_SUCCESS(Status
)) {
3551 ERR("open_fileref_by_inode returned %08x\n", Status
);
3552 } else if (!parfr
->deleted
) {
3554 BOOL found
= FALSE
, deleted
= FALSE
;
3555 UNICODE_STRING
* fn
= NULL
;
3557 le2
= parfr
->children
.Flink
;
3558 while (le2
!= &parfr
->children
) {
3559 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
3561 if (fr2
->dc
->index
== hl
->index
) {
3563 deleted
= fr2
->deleted
;
3566 fn
= &fr2
->dc
->name
;
3578 TRACE("fn = %.*S (found = %u)\n", fn
->Length
/ sizeof(WCHAR
), fn
->Buffer
, found
);
3581 bytes_needed
= (LONG
)sector_align(bytes_needed
, 8);
3583 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fn
->Length
- sizeof(WCHAR
);
3585 if (bytes_needed
> *length
)
3590 feli
->NextEntryOffset
= (ULONG
)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION
) + ((feli
->FileNameLength
- 1) * sizeof(WCHAR
)), 8);
3591 feli
= (FILE_LINK_ENTRY_INFORMATION
*)((UINT8
*)feli
+ feli
->NextEntryOffset
);
3595 feli
->NextEntryOffset
= 0;
3596 feli
->ParentFileId
= parfr
->fcb
->inode
;
3597 feli
->FileNameLength
= fn
->Length
/ sizeof(WCHAR
);
3598 RtlCopyMemory(feli
->FileName
, fn
->Buffer
, fn
->Length
);
3600 fli
->EntriesReturned
++;
3606 free_fileref(fcb
->Vcb
, parfr
);
3613 release_fcb_lock(fcb
->Vcb
);
3616 fli
->BytesNeeded
= bytes_needed
;
3620 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
3622 ExReleaseResourceLite(fcb
->Header
.Resource
);
3626 #endif /* __REACTOS__ */
3628 #if (NTDDI_VERSION >= NTDDI_WIN10)
3630 typedef struct _FILE_ID_128
{
3631 UCHAR Identifier
[16];
3632 } FILE_ID_128
, *PFILE_ID_128
;
3634 typedef struct _FILE_ID_INFORMATION
{
3635 ULONGLONG VolumeSerialNumber
;
3637 } FILE_ID_INFORMATION
, *PFILE_ID_INFORMATION
;
3640 static NTSTATUS
fill_in_file_id_information(FILE_ID_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
3641 RtlCopyMemory(&fii
->VolumeSerialNumber
, &fcb
->Vcb
->superblock
.uuid
.uuid
[8], sizeof(UINT64
));
3642 RtlCopyMemory(&fii
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
3643 RtlCopyMemory(&fii
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
3645 *length
-= sizeof(FILE_ID_INFORMATION
);
3647 return STATUS_SUCCESS
;
3652 static NTSTATUS
fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION
* fsli
, fcb
* fcb
, ccb
* ccb
, LONG
* length
) {
3655 fsli
->FileId
.LowPart
= (UINT32
)fcb
->inode
;
3656 fsli
->FileId
.HighPart
= (UINT32
)fcb
->subvol
->id
;
3659 ii
= &ccb
->fileref
->parent
->fcb
->inode_item
;
3661 ii
= &fcb
->inode_item
;
3663 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
3666 KeQuerySystemTime(&time
);
3667 fsli
->CreationTime
= fsli
->LastAccessTime
= fsli
->LastWriteTime
= fsli
->ChangeTime
= time
;
3669 fsli
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
3670 fsli
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
3671 fsli
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
3672 fsli
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
3676 fsli
->AllocationSize
.QuadPart
= fsli
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
3677 fsli
->FileAttributes
= ccb
->fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: ccb
->fileref
->parent
->fcb
->atts
;
3679 fsli
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
3680 fsli
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
3681 fsli
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
3684 if (fcb
->type
== BTRFS_TYPE_SOCKET
)
3685 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_SOCKET
;
3686 else if (fcb
->type
== BTRFS_TYPE_FIFO
)
3687 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_FIFO
;
3688 else if (fcb
->type
== BTRFS_TYPE_CHARDEV
)
3689 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_CHARDEV
;
3690 else if (fcb
->type
== BTRFS_TYPE_BLOCKDEV
)
3691 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_BLOCKDEV
;
3692 else if (!(fsli
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3693 fsli
->ReparseTag
= 0;
3695 fsli
->ReparseTag
= get_reparse_tag_fcb(fcb
);
3697 if (fcb
->type
== BTRFS_TYPE_SOCKET
|| fcb
->type
== BTRFS_TYPE_FIFO
|| fcb
->type
== BTRFS_TYPE_CHARDEV
|| fcb
->type
== BTRFS_TYPE_BLOCKDEV
)
3698 fsli
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
3701 fsli
->NumberOfLinks
= ccb
->fileref
->parent
->fcb
->inode_item
.st_nlink
;
3703 fsli
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
3705 fsli
->EffectiveAccess
= ccb
->access
;
3706 fsli
->LxFlags
= LX_FILE_METADATA_HAS_UID
| LX_FILE_METADATA_HAS_GID
| LX_FILE_METADATA_HAS_MODE
| LX_FILE_METADATA_HAS_DEVICE_ID
; // FIXME - LX_FILE_CASE_SENSITIVE_DIR
3707 fsli
->LxUid
= ii
->st_uid
;
3708 fsli
->LxGid
= ii
->st_gid
;
3709 fsli
->LxMode
= ii
->st_mode
;
3711 if (ii
->st_mode
& __S_IFBLK
|| ii
->st_mode
& __S_IFCHR
) {
3712 fsli
->LxDeviceIdMajor
= (ii
->st_rdev
& 0xFFFFFFFFFFF00000) >> 20;
3713 fsli
->LxDeviceIdMinor
= (ii
->st_rdev
& 0xFFFFF);
3715 fsli
->LxDeviceIdMajor
= 0;
3716 fsli
->LxDeviceIdMinor
= 0;
3719 *length
-= sizeof(FILE_STAT_LX_INFORMATION
);
3721 return STATUS_SUCCESS
;
3725 static NTSTATUS
query_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
3726 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3727 LONG length
= IrpSp
->Parameters
.QueryFile
.Length
;
3728 fcb
* fcb
= FileObject
->FsContext
;
3729 ccb
* ccb
= FileObject
->FsContext2
;
3730 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
3733 TRACE("(%p, %p, %p)\n", Vcb
, FileObject
, Irp
);
3734 TRACE("fcb = %p\n", fcb
);
3736 if (fcb
== Vcb
->volume_fcb
)
3737 return STATUS_INVALID_PARAMETER
;
3740 ERR("ccb is NULL\n");
3741 return STATUS_INVALID_PARAMETER
;
3744 switch (IrpSp
->Parameters
.QueryFile
.FileInformationClass
) {
3745 case FileAllInformation
:
3747 FILE_ALL_INFORMATION
* fai
= Irp
->AssociatedIrp
.SystemBuffer
;
3750 TRACE("FileAllInformation\n");
3752 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3753 WARN("insufficient privileges\n");
3754 Status
= STATUS_ACCESS_DENIED
;
3759 if (!fileref
|| !fileref
->parent
) {
3760 ERR("no fileref for stream\n");
3761 Status
= STATUS_INTERNAL_ERROR
;
3765 ii
= &fileref
->parent
->fcb
->inode_item
;
3767 ii
= &fcb
->inode_item
;
3769 // Access, mode, and alignment are all filled in by the kernel
3772 fill_in_file_basic_information(&fai
->BasicInformation
, ii
, &length
, fcb
, fileref
);
3775 fill_in_file_standard_information(&fai
->StandardInformation
, fcb
, fileref
, &length
);
3778 fill_in_file_internal_information(&fai
->InternalInformation
, fcb
, &length
);
3781 fill_in_file_ea_information(&fai
->EaInformation
, fcb
, &length
);
3783 length
-= sizeof(FILE_ACCESS_INFORMATION
);
3786 fill_in_file_position_information(&fai
->PositionInformation
, FileObject
, &length
);
3788 length
-= sizeof(FILE_MODE_INFORMATION
);
3790 length
-= sizeof(FILE_ALIGNMENT_INFORMATION
);
3793 fill_in_file_name_information(&fai
->NameInformation
, fcb
, fileref
, &length
);
3795 Status
= STATUS_SUCCESS
;
3800 case FileAttributeTagInformation
:
3802 FILE_ATTRIBUTE_TAG_INFORMATION
* ati
= Irp
->AssociatedIrp
.SystemBuffer
;
3804 TRACE("FileAttributeTagInformation\n");
3806 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3807 WARN("insufficient privileges\n");
3808 Status
= STATUS_ACCESS_DENIED
;
3812 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3813 Status
= fill_in_file_attribute_information(ati
, fcb
, ccb
, &length
);
3814 ExReleaseResourceLite(&Vcb
->tree_lock
);
3819 case FileBasicInformation
:
3821 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
3824 TRACE("FileBasicInformation\n");
3826 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3827 WARN("insufficient privileges\n");
3828 Status
= STATUS_ACCESS_DENIED
;
3832 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_BASIC_INFORMATION
)) {
3834 Status
= STATUS_BUFFER_OVERFLOW
;
3839 if (!fileref
|| !fileref
->parent
) {
3840 ERR("no fileref for stream\n");
3841 Status
= STATUS_INTERNAL_ERROR
;
3845 ii
= &fileref
->parent
->fcb
->inode_item
;
3847 ii
= &fcb
->inode_item
;
3849 Status
= fill_in_file_basic_information(fbi
, ii
, &length
, fcb
, fileref
);
3853 case FileCompressionInformation
:
3854 FIXME("STUB: FileCompressionInformation\n");
3855 Status
= STATUS_INVALID_PARAMETER
;
3858 case FileEaInformation
:
3860 FILE_EA_INFORMATION
* eai
= Irp
->AssociatedIrp
.SystemBuffer
;
3862 TRACE("FileEaInformation\n");
3864 Status
= fill_in_file_ea_information(eai
, fcb
, &length
);
3869 case FileInternalInformation
:
3871 FILE_INTERNAL_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
3873 TRACE("FileInternalInformation\n");
3875 Status
= fill_in_file_internal_information(fii
, fcb
, &length
);
3880 case FileNameInformation
:
3882 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3884 TRACE("FileNameInformation\n");
3886 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3891 case FileNetworkOpenInformation
:
3893 FILE_NETWORK_OPEN_INFORMATION
* fnoi
= Irp
->AssociatedIrp
.SystemBuffer
;
3895 TRACE("FileNetworkOpenInformation\n");
3897 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3898 WARN("insufficient privileges\n");
3899 Status
= STATUS_ACCESS_DENIED
;
3903 Status
= fill_in_file_network_open_information(fnoi
, fcb
, fileref
, &length
);
3908 case FilePositionInformation
:
3910 FILE_POSITION_INFORMATION
* fpi
= Irp
->AssociatedIrp
.SystemBuffer
;
3912 TRACE("FilePositionInformation\n");
3914 Status
= fill_in_file_position_information(fpi
, FileObject
, &length
);
3919 case FileStandardInformation
:
3921 FILE_STANDARD_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3923 TRACE("FileStandardInformation\n");
3925 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STANDARD_INFORMATION
)) {
3927 Status
= STATUS_BUFFER_OVERFLOW
;
3931 Status
= fill_in_file_standard_information(fsi
, fcb
, ccb
->fileref
, &length
);
3936 case FileStreamInformation
:
3938 FILE_STREAM_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3940 TRACE("FileStreamInformation\n");
3942 Status
= fill_in_file_stream_information(fsi
, fileref
, &length
);
3947 #if (NTDDI_VERSION >= NTDDI_VISTA)
3948 case FileHardLinkInformation
:
3950 FILE_LINKS_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
3952 TRACE("FileHardLinkInformation\n");
3954 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3955 Status
= fill_in_hard_link_information(fli
, fileref
, Irp
, &length
);
3956 ExReleaseResourceLite(&Vcb
->tree_lock
);
3961 case FileNormalizedNameInformation
:
3963 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3965 TRACE("FileNormalizedNameInformation\n");
3967 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3973 #if (NTDDI_VERSION >= NTDDI_WIN7)
3974 case FileStandardLinkInformation
:
3976 FILE_STANDARD_LINK_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
3978 TRACE("FileStandardLinkInformation\n");
3980 Status
= fill_in_file_standard_link_information(fsli
, fcb
, ccb
->fileref
, &length
);
3985 case FileRemoteProtocolInformation
:
3986 TRACE("FileRemoteProtocolInformation\n");
3987 Status
= STATUS_INVALID_PARAMETER
;
3991 #if (NTDDI_VERSION >= NTDDI_WIN10)
3993 #pragma GCC diagnostic push
3994 #pragma GCC diagnostic ignored "-Wswitch"
3996 case FileIdInformation
:
3998 FILE_ID_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
4000 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_ID_INFORMATION
)) {
4002 Status
= STATUS_BUFFER_OVERFLOW
;
4006 TRACE("FileIdInformation\n");
4008 Status
= fill_in_file_id_information(fii
, fcb
, &length
);
4013 case FileStatLxInformation
:
4015 FILE_STAT_LX_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
4017 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STAT_LX_INFORMATION
)) {
4019 Status
= STATUS_BUFFER_OVERFLOW
;
4023 TRACE("FileStatLxInformation\n");
4025 Status
= fill_in_file_stat_lx_information(fsli
, fcb
, ccb
, &length
);
4030 #pragma GCC diagnostic pop
4035 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryFile
.FileInformationClass
);
4036 Status
= STATUS_INVALID_PARAMETER
;
4042 Status
= STATUS_BUFFER_OVERFLOW
;
4045 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- length
;
4048 TRACE("query_info returning %08x\n", Status
);
4053 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION
)
4054 _Function_class_(DRIVER_DISPATCH
)
4055 NTSTATUS NTAPI
drv_query_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4056 PIO_STACK_LOCATION IrpSp
;
4059 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4062 FsRtlEnterFileSystem();
4064 top_level
= is_top_level(Irp
);
4066 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4067 Status
= vol_query_information(DeviceObject
, Irp
);
4069 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4070 Status
= STATUS_INVALID_PARAMETER
;
4074 Irp
->IoStatus
.Information
= 0;
4076 TRACE("query information\n");
4078 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4080 fcb
= IrpSp
->FileObject
->FsContext
;
4081 TRACE("fcb = %p\n", fcb
);
4082 TRACE("fcb->subvol = %p\n", fcb
->subvol
);
4084 Status
= query_info(fcb
->Vcb
, IrpSp
->FileObject
, Irp
);
4087 TRACE("returning %08x\n", Status
);
4089 Irp
->IoStatus
.Status
= Status
;
4091 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4094 IoSetTopLevelIrp(NULL
);
4096 FsRtlExitFileSystem();
4101 _Dispatch_type_(IRP_MJ_QUERY_EA
)
4102 _Function_class_(DRIVER_DISPATCH
)
4103 NTSTATUS NTAPI
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4106 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4107 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4108 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4111 FILE_FULL_EA_INFORMATION
* ffei
;
4114 FsRtlEnterFileSystem();
4116 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4118 top_level
= is_top_level(Irp
);
4120 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4121 Status
= vol_query_ea(DeviceObject
, Irp
);
4123 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4124 Status
= STATUS_INVALID_PARAMETER
;
4128 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
4130 ERR("could not get output buffer\n");
4131 Status
= STATUS_INVALID_PARAMETER
;
4136 ERR("no file object\n");
4137 Status
= STATUS_INVALID_PARAMETER
;
4141 fcb
= FileObject
->FsContext
;
4145 Status
= STATUS_INVALID_PARAMETER
;
4149 ccb
= FileObject
->FsContext2
;
4153 Status
= STATUS_INVALID_PARAMETER
;
4157 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_READ_EA
| FILE_WRITE_EA
))) {
4158 WARN("insufficient privileges\n");
4159 Status
= STATUS_ACCESS_DENIED
;
4164 fcb
= ccb
->fileref
->parent
->fcb
;
4166 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
4168 Status
= STATUS_SUCCESS
;
4170 if (fcb
->ea_xattr
.Length
== 0)
4173 if (IrpSp
->Parameters
.QueryEa
.EaList
) {
4174 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4175 FILE_GET_EA_INFORMATION
* in
;
4177 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4181 s
.Length
= s
.MaximumLength
= in
->EaNameLength
;
4182 s
.Buffer
= in
->EaName
;
4184 RtlUpperString(&s
, &s
);
4186 if (in
->NextEntryOffset
== 0)
4189 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4192 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4198 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
4200 if (in
->EaNameLength
== ea
->EaNameLength
&&
4201 RtlCompareMemory(in
->EaName
, ea
->EaName
, in
->EaNameLength
) == in
->EaNameLength
) {
4206 if (in
->NextEntryOffset
== 0)
4209 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
4213 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4215 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4216 Status
= STATUS_BUFFER_OVERFLOW
;
4224 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4225 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4229 out
->NextEntryOffset
= 0;
4230 out
->Flags
= ea
->Flags
;
4231 out
->EaNameLength
= ea
->EaNameLength
;
4232 out
->EaValueLength
= ea
->EaValueLength
;
4233 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4235 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4237 if (IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4241 if (ea
->NextEntryOffset
== 0)
4244 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4247 FILE_FULL_EA_INFORMATION
*ea
, *out
;
4250 if (IrpSp
->Flags
& SL_INDEX_SPECIFIED
) {
4251 // The index is 1-based
4252 if (IrpSp
->Parameters
.QueryEa
.EaIndex
== 0) {
4253 Status
= STATUS_NONEXISTENT_EA_ENTRY
;
4256 index
= IrpSp
->Parameters
.QueryEa
.EaIndex
- 1;
4257 } else if (IrpSp
->Flags
& SL_RESTART_SCAN
)
4258 index
= ccb
->ea_index
= 0;
4260 index
= ccb
->ea_index
;
4262 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4267 for (i
= 0; i
< index
; i
++) {
4268 if (ea
->NextEntryOffset
== 0) // last item
4271 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4278 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4280 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4281 Status
= retlen
== 0 ? STATUS_BUFFER_TOO_SMALL
: STATUS_BUFFER_OVERFLOW
;
4288 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4289 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4293 out
->NextEntryOffset
= 0;
4294 out
->Flags
= ea
->Flags
;
4295 out
->EaNameLength
= ea
->EaNameLength
;
4296 out
->EaValueLength
= ea
->EaValueLength
;
4297 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4299 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4301 if (!(IrpSp
->Flags
& SL_INDEX_SPECIFIED
))
4304 if (ea
->NextEntryOffset
== 0 || IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4307 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4312 ExReleaseResourceLite(fcb
->Header
.Resource
);
4315 TRACE("returning %08x\n", Status
);
4317 Irp
->IoStatus
.Status
= Status
;
4318 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
? retlen
: 0;
4320 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4323 IoSetTopLevelIrp(NULL
);
4325 FsRtlExitFileSystem();
4330 _Dispatch_type_(IRP_MJ_SET_EA
)
4331 _Function_class_(DRIVER_DISPATCH
)
4332 NTSTATUS NTAPI
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4333 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4336 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4337 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4341 FILE_FULL_EA_INFORMATION
* ffei
;
4345 FILE_FULL_EA_INFORMATION
* ea
;
4350 FsRtlEnterFileSystem();
4352 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4354 top_level
= is_top_level(Irp
);
4356 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4357 Status
= vol_set_ea(DeviceObject
, Irp
);
4359 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4360 Status
= STATUS_INVALID_PARAMETER
;
4364 if (Vcb
->readonly
) {
4365 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4369 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
4371 ERR("could not get output buffer\n");
4372 Status
= STATUS_INVALID_PARAMETER
;
4376 Status
= IoCheckEaBufferValidity(ffei
, IrpSp
->Parameters
.SetEa
.Length
, &offset
);
4377 if (!NT_SUCCESS(Status
)) {
4378 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4383 ERR("no file object\n");
4384 Status
= STATUS_INVALID_PARAMETER
;
4388 fcb
= FileObject
->FsContext
;
4392 Status
= STATUS_INVALID_PARAMETER
;
4396 ccb
= FileObject
->FsContext2
;
4400 Status
= STATUS_INVALID_PARAMETER
;
4404 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_EA
)) {
4405 WARN("insufficient privileges\n");
4406 Status
= STATUS_ACCESS_DENIED
;
4411 fileref
= ccb
->fileref
->parent
;
4414 fileref
= ccb
->fileref
;
4416 InitializeListHead(&ealist
);
4418 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4420 if (fcb
->ea_xattr
.Length
> 0) {
4421 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4424 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4426 ERR("out of memory\n");
4427 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4431 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4432 item
->name
.Buffer
= ea
->EaName
;
4434 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4435 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4437 item
->flags
= ea
->Flags
;
4439 InsertTailList(&ealist
, &item
->list_entry
);
4441 if (ea
->NextEntryOffset
== 0)
4444 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4454 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
4455 s
.Buffer
= ea
->EaName
;
4457 RtlUpperString(&s
, &s
);
4460 while (le
!= &ealist
) {
4461 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4463 if (item
->name
.Length
== s
.Length
&&
4464 RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
4465 item
->flags
= ea
->Flags
;
4466 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4467 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4476 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4478 ERR("out of memory\n");
4479 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4483 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4484 item
->name
.Buffer
= ea
->EaName
;
4486 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4487 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4489 item
->flags
= ea
->Flags
;
4491 InsertTailList(&ealist
, &item
->list_entry
);
4494 if (ea
->NextEntryOffset
== 0)
4497 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4500 // remove entries with zero-length value
4502 while (le
!= &ealist
) {
4503 LIST_ENTRY
* le2
= le
->Flink
;
4505 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4507 if (item
->value
.Length
== 0) {
4508 RemoveEntryList(&item
->list_entry
);
4515 // handle LXSS values
4517 while (le
!= &ealist
) {
4518 LIST_ENTRY
* le2
= le
->Flink
;
4520 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4522 if (item
->name
.Length
== sizeof(lxuid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxuid
, item
->name
.Length
) == item
->name
.Length
) {
4523 if (item
->value
.Length
< sizeof(UINT32
)) {
4524 ERR("uid value was shorter than expected\n");
4525 Status
= STATUS_INVALID_PARAMETER
;
4529 if (Irp
->RequestorMode
== KernelMode
) {
4530 RtlCopyMemory(&fcb
->inode_item
.st_uid
, item
->value
.Buffer
, sizeof(UINT32
));
4531 fcb
->sd_dirty
= TRUE
;
4532 fcb
->sd_deleted
= FALSE
;
4535 RemoveEntryList(&item
->list_entry
);
4537 } else if (item
->name
.Length
== sizeof(lxgid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxgid
, item
->name
.Length
) == item
->name
.Length
) {
4538 if (item
->value
.Length
< sizeof(UINT32
)) {
4539 ERR("gid value was shorter than expected\n");
4540 Status
= STATUS_INVALID_PARAMETER
;
4544 if (Irp
->RequestorMode
== KernelMode
)
4545 RtlCopyMemory(&fcb
->inode_item
.st_gid
, item
->value
.Buffer
, sizeof(UINT32
));
4547 RemoveEntryList(&item
->list_entry
);
4549 } else if (item
->name
.Length
== sizeof(lxmod
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxmod
, item
->name
.Length
) == item
->name
.Length
) {
4550 if (item
->value
.Length
< sizeof(UINT32
)) {
4551 ERR("mode value was shorter than expected\n");
4552 Status
= STATUS_INVALID_PARAMETER
;
4556 if (Irp
->RequestorMode
== KernelMode
) {
4557 UINT32 allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
| S_ISGID
| S_ISVTX
| S_ISUID
;
4560 RtlCopyMemory(&val
, item
->value
.Buffer
, sizeof(UINT32
));
4562 fcb
->inode_item
.st_mode
&= ~allowed
;
4563 fcb
->inode_item
.st_mode
|= val
& allowed
;
4566 RemoveEntryList(&item
->list_entry
);
4573 if (IsListEmpty(&ealist
)) {
4576 if (fcb
->ea_xattr
.Buffer
)
4577 ExFreePool(fcb
->ea_xattr
.Buffer
);
4579 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4580 fcb
->ea_xattr
.Buffer
= NULL
;
4586 while (le
!= &ealist
) {
4587 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4590 size
+= 4 - (size
% 4);
4592 size
+= (UINT16
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
4597 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
4599 ERR("out of memory\n");
4600 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4604 oldbuf
= fcb
->ea_xattr
.Buffer
;
4606 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
4607 fcb
->ea_xattr
.Buffer
= buf
;
4613 while (le
!= &ealist
) {
4614 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4617 ea
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
4619 if (ea
->NextEntryOffset
% 4 > 0)
4620 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
4622 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4624 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4626 ea
->NextEntryOffset
= 0;
4627 ea
->Flags
= item
->flags
;
4628 ea
->EaNameLength
= (UCHAR
)item
->name
.Length
;
4629 ea
->EaValueLength
= item
->value
.Length
;
4631 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
4632 ea
->EaName
[item
->name
.Length
] = 0;
4633 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
4635 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
4644 fcb
->ea_changed
= TRUE
;
4646 KeQuerySystemTime(&time
);
4647 win_time_to_unix(time
, &now
);
4649 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
4650 fcb
->inode_item
.sequence
++;
4652 if (!ccb
->user_set_change_time
)
4653 fcb
->inode_item
.st_ctime
= now
;
4655 fcb
->inode_item_changed
= TRUE
;
4656 mark_fcb_dirty(fcb
);
4658 send_notification_fileref(fileref
, FILE_NOTIFY_CHANGE_EA
, FILE_ACTION_MODIFIED
, NULL
);
4660 Status
= STATUS_SUCCESS
;
4663 ExReleaseResourceLite(fcb
->Header
.Resource
);
4665 while (!IsListEmpty(&ealist
)) {
4666 le
= RemoveHeadList(&ealist
);
4668 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4674 TRACE("returning %08x\n", Status
);
4676 Irp
->IoStatus
.Status
= Status
;
4677 Irp
->IoStatus
.Information
= 0;
4679 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4682 IoSetTopLevelIrp(NULL
);
4684 FsRtlExitFileSystem();