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
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 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
235 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi
->DeleteFile
? "TRUE" : "FALSE", file_desc(FileObject
), fcb
);
239 atts
= fileref
->parent
->fcb
->atts
;
241 ERR("no fileref for stream\n");
242 Status
= STATUS_INTERNAL_ERROR
;
248 TRACE("atts = %x\n", atts
);
250 if (atts
& FILE_ATTRIBUTE_READONLY
) {
251 TRACE("not allowing readonly file to be deleted\n");
252 Status
= STATUS_CANNOT_DELETE
;
256 // FIXME - can we skip this bit for subvols?
257 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0 && (!fileref
|| fileref
->fcb
!= Vcb
->dummy_fcb
)) {
258 TRACE("directory not empty\n");
259 Status
= STATUS_DIRECTORY_NOT_EMPTY
;
263 if (!MmFlushImageSection(&fcb
->nonpaged
->segment_object
, MmFlushForDelete
)) {
264 TRACE("trying to delete file which is being mapped as an image\n");
265 Status
= STATUS_CANNOT_DELETE
;
269 ccb
->fileref
->delete_on_close
= fdi
->DeleteFile
;
271 FileObject
->DeletePending
= fdi
->DeleteFile
;
273 Status
= STATUS_SUCCESS
;
276 ExReleaseResourceLite(fcb
->Header
.Resource
);
278 // send notification that directory is about to be deleted
279 if (NT_SUCCESS(Status
) && fdi
->DeleteFile
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
280 FsRtlNotifyFullChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext
,
281 NULL
, FALSE
, FALSE
, 0, NULL
, NULL
, NULL
);
287 BOOL
has_open_children(file_ref
* fileref
) {
288 LIST_ENTRY
* le
= fileref
->children
.Flink
;
290 if (IsListEmpty(&fileref
->children
))
293 while (le
!= &fileref
->children
) {
294 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
296 if (c
->open_count
> 0)
299 if (has_open_children(c
))
308 static NTSTATUS
duplicate_fcb(fcb
* oldfcb
, fcb
** pfcb
) {
309 device_extension
* Vcb
= oldfcb
->Vcb
;
313 // FIXME - we can skip a lot of this if the inode is about to be deleted
315 fcb
= create_fcb(Vcb
, PagedPool
); // FIXME - what if we duplicate the paging file?
317 ERR("out of memory\n");
318 return STATUS_INSUFFICIENT_RESOURCES
;
323 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
324 fcb
->Header
.AllocationSize
= oldfcb
->Header
.AllocationSize
;
325 fcb
->Header
.FileSize
= oldfcb
->Header
.FileSize
;
326 fcb
->Header
.ValidDataLength
= oldfcb
->Header
.ValidDataLength
;
328 fcb
->type
= oldfcb
->type
;
332 fcb
->adshash
= oldfcb
->adshash
;
333 fcb
->adsmaxlen
= oldfcb
->adsmaxlen
;
335 if (oldfcb
->adsxattr
.Buffer
&& oldfcb
->adsxattr
.Length
> 0) {
336 fcb
->adsxattr
.Length
= oldfcb
->adsxattr
.Length
;
337 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
338 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
340 if (!fcb
->adsxattr
.Buffer
) {
341 ERR("out of memory\n");
343 return STATUS_INSUFFICIENT_RESOURCES
;
346 RtlCopyMemory(fcb
->adsxattr
.Buffer
, oldfcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
347 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
350 if (oldfcb
->adsdata
.Buffer
&& oldfcb
->adsdata
.Length
> 0) {
351 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= oldfcb
->adsdata
.Length
;
352 fcb
->adsdata
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsdata
.MaximumLength
, ALLOC_TAG
);
354 if (!fcb
->adsdata
.Buffer
) {
355 ERR("out of memory\n");
357 return STATUS_INSUFFICIENT_RESOURCES
;
360 RtlCopyMemory(fcb
->adsdata
.Buffer
, oldfcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
366 RtlCopyMemory(&fcb
->inode_item
, &oldfcb
->inode_item
, sizeof(INODE_ITEM
));
367 fcb
->inode_item_changed
= TRUE
;
369 if (oldfcb
->sd
&& RtlLengthSecurityDescriptor(oldfcb
->sd
) > 0) {
370 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, RtlLengthSecurityDescriptor(oldfcb
->sd
), ALLOC_TAG
);
372 ERR("out of memory\n");
374 return STATUS_INSUFFICIENT_RESOURCES
;
377 RtlCopyMemory(fcb
->sd
, oldfcb
->sd
, RtlLengthSecurityDescriptor(oldfcb
->sd
));
380 fcb
->atts
= oldfcb
->atts
;
382 le
= oldfcb
->extents
.Flink
;
383 while (le
!= &oldfcb
->extents
) {
384 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
387 extent
* ext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
390 ERR("out of memory\n");
392 return STATUS_INSUFFICIENT_RESOURCES
;
395 ext2
->offset
= ext
->offset
;
396 ext2
->datalen
= ext
->datalen
;
398 if (ext2
->datalen
> 0)
399 RtlCopyMemory(&ext2
->extent_data
, &ext
->extent_data
, ext2
->datalen
);
401 ext2
->unique
= FALSE
;
402 ext2
->ignore
= FALSE
;
403 ext2
->inserted
= TRUE
;
407 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
409 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
)
410 len
= (ULONG
)ed2
->num_bytes
;
412 len
= (ULONG
)ed2
->size
;
414 len
= len
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
;
416 ext2
->csum
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
418 ERR("out of memory\n");
420 return STATUS_INSUFFICIENT_RESOURCES
;
423 RtlCopyMemory(ext2
->csum
, ext
->csum
, len
);
427 InsertTailList(&fcb
->extents
, &ext2
->list_entry
);
433 le
= oldfcb
->hardlinks
.Flink
;
434 while (le
!= &oldfcb
->hardlinks
) {
435 hardlink
*hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
), *hl2
;
437 hl2
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
440 ERR("out of memory\n");
442 return STATUS_INSUFFICIENT_RESOURCES
;
445 hl2
->parent
= hl
->parent
;
446 hl2
->index
= hl
->index
;
448 hl2
->name
.Length
= hl2
->name
.MaximumLength
= hl
->name
.Length
;
449 hl2
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->name
.MaximumLength
, ALLOC_TAG
);
451 if (!hl2
->name
.Buffer
) {
452 ERR("out of memory\n");
455 return STATUS_INSUFFICIENT_RESOURCES
;
458 RtlCopyMemory(hl2
->name
.Buffer
, hl
->name
.Buffer
, hl
->name
.Length
);
460 hl2
->utf8
.Length
= hl2
->utf8
.MaximumLength
= hl
->utf8
.Length
;
461 hl2
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl2
->utf8
.MaximumLength
, ALLOC_TAG
);
463 if (!hl2
->utf8
.Buffer
) {
464 ERR("out of memory\n");
465 ExFreePool(hl2
->name
.Buffer
);
468 return STATUS_INSUFFICIENT_RESOURCES
;
471 RtlCopyMemory(hl2
->utf8
.Buffer
, hl
->utf8
.Buffer
, hl
->utf8
.Length
);
473 InsertTailList(&fcb
->hardlinks
, &hl2
->list_entry
);
478 if (oldfcb
->reparse_xattr
.Buffer
&& oldfcb
->reparse_xattr
.Length
> 0) {
479 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= oldfcb
->reparse_xattr
.Length
;
481 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.MaximumLength
, ALLOC_TAG
);
482 if (!fcb
->reparse_xattr
.Buffer
) {
483 ERR("out of memory\n");
485 return STATUS_INSUFFICIENT_RESOURCES
;
488 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, oldfcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
491 if (oldfcb
->ea_xattr
.Buffer
&& oldfcb
->ea_xattr
.Length
> 0) {
492 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= oldfcb
->ea_xattr
.Length
;
494 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fcb
->ea_xattr
.MaximumLength
, ALLOC_TAG
);
495 if (!fcb
->ea_xattr
.Buffer
) {
496 ERR("out of memory\n");
498 return STATUS_INSUFFICIENT_RESOURCES
;
501 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, oldfcb
->ea_xattr
.Buffer
, fcb
->ea_xattr
.Length
);
504 fcb
->prop_compression
= oldfcb
->prop_compression
;
506 le
= oldfcb
->xattrs
.Flink
;
507 while (le
!= &oldfcb
->xattrs
) {
508 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
510 if (xa
->valuelen
> 0) {
513 xa2
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + xa
->namelen
+ xa
->valuelen
, ALLOC_TAG
);
516 ERR("out of memory\n");
518 return STATUS_INSUFFICIENT_RESOURCES
;
521 xa2
->namelen
= xa
->namelen
;
522 xa2
->valuelen
= xa
->valuelen
;
523 xa2
->dirty
= xa
->dirty
;
524 memcpy(xa2
->data
, xa
->data
, xa
->namelen
+ xa
->valuelen
);
526 InsertTailList(&fcb
->xattrs
, &xa2
->list_entry
);
535 return STATUS_SUCCESS
;
538 typedef struct _move_entry
{
541 file_ref
* dummyfileref
;
542 struct _move_entry
* parent
;
543 LIST_ENTRY list_entry
;
546 static NTSTATUS
add_children_to_move_list(device_extension
* Vcb
, move_entry
* me
, PIRP Irp
) {
550 ExAcquireResourceSharedLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
552 le
= me
->fileref
->fcb
->dir_children_index
.Flink
;
554 while (le
!= &me
->fileref
->fcb
->dir_children_index
) {
555 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
559 Status
= open_fileref_child(Vcb
, me
->fileref
, &dc
->name
, TRUE
, TRUE
, dc
->index
== 0 ? TRUE
: FALSE
, PagedPool
, &fr
, Irp
);
561 if (!NT_SUCCESS(Status
)) {
562 ERR("open_fileref_child returned %08x\n", Status
);
563 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
567 me2
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
569 ERR("out of memory\n");
570 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
571 return STATUS_INSUFFICIENT_RESOURCES
;
575 me2
->dummyfcb
= NULL
;
576 me2
->dummyfileref
= NULL
;
579 InsertHeadList(&me
->list_entry
, &me2
->list_entry
);
584 ExReleaseResourceLite(&me
->fileref
->fcb
->nonpaged
->dir_children_lock
);
586 return STATUS_SUCCESS
;
589 void remove_dir_child_from_hash_lists(fcb
* fcb
, dir_child
* dc
) {
594 if (fcb
->hash_ptrs
[c
] == &dc
->list_entry_hash
) {
595 if (dc
->list_entry_hash
.Flink
== &fcb
->dir_children_hash
)
596 fcb
->hash_ptrs
[c
] = NULL
;
598 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash
.Flink
, dir_child
, list_entry_hash
);
600 if (dc2
->hash
>> 24 == c
)
601 fcb
->hash_ptrs
[c
] = &dc2
->list_entry_hash
;
603 fcb
->hash_ptrs
[c
] = NULL
;
607 RemoveEntryList(&dc
->list_entry_hash
);
609 c
= dc
->hash_uc
>> 24;
611 if (fcb
->hash_ptrs_uc
[c
] == &dc
->list_entry_hash_uc
) {
612 if (dc
->list_entry_hash_uc
.Flink
== &fcb
->dir_children_hash_uc
)
613 fcb
->hash_ptrs_uc
[c
] = NULL
;
615 dir_child
* dc2
= CONTAINING_RECORD(dc
->list_entry_hash_uc
.Flink
, dir_child
, list_entry_hash_uc
);
617 if (dc2
->hash_uc
>> 24 == c
)
618 fcb
->hash_ptrs_uc
[c
] = &dc2
->list_entry_hash_uc
;
620 fcb
->hash_ptrs_uc
[c
] = NULL
;
624 RemoveEntryList(&dc
->list_entry_hash_uc
);
627 static NTSTATUS
create_directory_fcb(device_extension
* Vcb
, root
* r
, fcb
* parfcb
, fcb
** pfcb
) {
630 SECURITY_SUBJECT_CONTEXT subjcont
;
636 fcb
= create_fcb(Vcb
, PagedPool
);
638 ERR("out of memory\n");
639 return STATUS_INSUFFICIENT_RESOURCES
;
642 KeQuerySystemTime(&time
);
643 win_time_to_unix(time
, &now
);
648 fcb
->inode
= InterlockedIncrement64(&r
->lastinode
);
649 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
651 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
652 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
653 fcb
->inode_item
.st_nlink
= 1;
654 fcb
->inode_item
.st_mode
= __S_IFDIR
| inherit_mode(parfcb
, TRUE
);
655 fcb
->inode_item
.st_atime
= fcb
->inode_item
.st_ctime
= fcb
->inode_item
.st_mtime
= fcb
->inode_item
.otime
= now
;
656 fcb
->inode_item
.st_gid
= GID_NOBODY
;
658 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, FALSE
, TRUE
, NULL
);
660 SeCaptureSubjectContext(&subjcont
);
662 Status
= SeAssignSecurity(parfcb
->sd
, NULL
, (void**)&fcb
->sd
, TRUE
, &subjcont
, IoGetFileObjectGenericMapping(), PagedPool
);
664 if (!NT_SUCCESS(Status
)) {
666 ERR("SeAssignSecurity returned %08x\n", Status
);
672 ERR("SeAssignSecurity returned NULL security descriptor\n");
673 return STATUS_INTERNAL_ERROR
;
676 Status
= RtlGetOwnerSecurityDescriptor(fcb
->sd
, &owner
, &defaulted
);
677 if (!NT_SUCCESS(Status
)) {
678 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status
);
679 fcb
->inode_item
.st_uid
= UID_NOBODY
;
680 fcb
->sd_dirty
= TRUE
;
682 fcb
->inode_item
.st_uid
= sid_to_uid(owner
);
683 fcb
->sd_dirty
= fcb
->inode_item
.st_uid
== UID_NOBODY
;
686 find_gid(fcb
, parfcb
, &subjcont
);
688 fcb
->inode_item_changed
= TRUE
;
690 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
691 fcb
->Header
.AllocationSize
.QuadPart
= 0;
692 fcb
->Header
.FileSize
.QuadPart
= 0;
693 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
697 if (parfcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
698 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
700 fcb
->prop_compression
= parfcb
->prop_compression
;
701 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
703 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
704 if (!fcb
->hash_ptrs
) {
705 ERR("out of memory\n");
706 return STATUS_INSUFFICIENT_RESOURCES
;
709 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
711 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
712 if (!fcb
->hash_ptrs_uc
) {
713 ERR("out of memory\n");
714 return STATUS_INSUFFICIENT_RESOURCES
;
717 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
719 acquire_fcb_lock_exclusive(Vcb
);
720 InsertTailList(&r
->fcbs
, &fcb
->list_entry
);
721 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
723 release_fcb_lock(Vcb
);
729 return STATUS_SUCCESS
;
732 static NTSTATUS
move_across_subvols(file_ref
* fileref
, ccb
* ccb
, file_ref
* destdir
, PANSI_STRING utf8
, PUNICODE_STRING fnus
, PIRP Irp
, LIST_ENTRY
* rollback
) {
734 LIST_ENTRY move_list
, *le
;
738 file_ref
* origparent
;
740 // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
742 InitializeListHead(&move_list
);
744 KeQuerySystemTime(&time
);
745 win_time_to_unix(time
, &now
);
747 acquire_fcb_lock_exclusive(fileref
->fcb
->Vcb
);
749 me
= ExAllocatePoolWithTag(PagedPool
, sizeof(move_entry
), ALLOC_TAG
);
752 ERR("out of memory\n");
753 Status
= STATUS_INSUFFICIENT_RESOURCES
;
757 origparent
= fileref
->parent
;
759 me
->fileref
= fileref
;
760 increase_fileref_refcount(me
->fileref
);
762 me
->dummyfileref
= NULL
;
765 InsertTailList(&move_list
, &me
->list_entry
);
767 le
= move_list
.Flink
;
768 while (le
!= &move_list
) {
769 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
771 ExAcquireResourceSharedLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
773 if (!me
->fileref
->fcb
->ads
&& me
->fileref
->fcb
->subvol
== origparent
->fcb
->subvol
) {
774 Status
= add_children_to_move_list(fileref
->fcb
->Vcb
, me
, Irp
);
776 if (!NT_SUCCESS(Status
)) {
777 ERR("add_children_to_move_list returned %08x\n", Status
);
782 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
787 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
789 // loop through list and create new inodes
791 le
= move_list
.Flink
;
792 while (le
!= &move_list
) {
793 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
795 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
&& me
->fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) {
798 BOOL inserted
= FALSE
;
801 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
803 Status
= duplicate_fcb(me
->fileref
->fcb
, &me
->dummyfcb
);
804 if (!NT_SUCCESS(Status
)) {
805 ERR("duplicate_fcb returned %08x\n", Status
);
806 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
810 me
->dummyfcb
->subvol
= me
->fileref
->fcb
->subvol
;
811 me
->dummyfcb
->inode
= me
->fileref
->fcb
->inode
;
813 if (!me
->dummyfcb
->ads
) {
814 me
->dummyfcb
->sd_dirty
= me
->fileref
->fcb
->sd_dirty
;
815 me
->dummyfcb
->atts_changed
= me
->fileref
->fcb
->atts_changed
;
816 me
->dummyfcb
->atts_deleted
= me
->fileref
->fcb
->atts_deleted
;
817 me
->dummyfcb
->extents_changed
= me
->fileref
->fcb
->extents_changed
;
818 me
->dummyfcb
->reparse_xattr_changed
= me
->fileref
->fcb
->reparse_xattr_changed
;
819 me
->dummyfcb
->ea_changed
= me
->fileref
->fcb
->ea_changed
;
822 me
->dummyfcb
->created
= me
->fileref
->fcb
->created
;
823 me
->dummyfcb
->deleted
= me
->fileref
->fcb
->deleted
;
824 mark_fcb_dirty(me
->dummyfcb
);
826 if (!me
->fileref
->fcb
->ads
) {
829 me
->fileref
->fcb
->subvol
= destdir
->fcb
->subvol
;
830 me
->fileref
->fcb
->inode
= InterlockedIncrement64(&destdir
->fcb
->subvol
->lastinode
);
831 me
->fileref
->fcb
->inode_item
.st_nlink
= 1;
833 defda
= get_file_attributes(me
->fileref
->fcb
->Vcb
, me
->fileref
->fcb
->subvol
, me
->fileref
->fcb
->inode
,
834 me
->fileref
->fcb
->type
, me
->fileref
->dc
&& me
->fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && me
->fileref
->dc
->name
.Buffer
[0] == '.',
837 me
->fileref
->fcb
->sd_dirty
= !!me
->fileref
->fcb
->sd
;
838 me
->fileref
->fcb
->atts_changed
= defda
!= me
->fileref
->fcb
->atts
;
839 me
->fileref
->fcb
->extents_changed
= !IsListEmpty(&me
->fileref
->fcb
->extents
);
840 me
->fileref
->fcb
->reparse_xattr_changed
= !!me
->fileref
->fcb
->reparse_xattr
.Buffer
;
841 me
->fileref
->fcb
->ea_changed
= !!me
->fileref
->fcb
->ea_xattr
.Buffer
;
842 me
->fileref
->fcb
->xattrs_changed
= !IsListEmpty(&me
->fileref
->fcb
->xattrs
);
843 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
845 le2
= me
->fileref
->fcb
->xattrs
.Flink
;
846 while (le2
!= &me
->fileref
->fcb
->xattrs
) {
847 xattr
* xa
= CONTAINING_RECORD(le2
, xattr
, list_entry
);
854 if (le
== move_list
.Flink
) { // first entry
855 me
->fileref
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
856 me
->fileref
->fcb
->inode_item
.sequence
++;
858 if (!ccb
->user_set_change_time
)
859 me
->fileref
->fcb
->inode_item
.st_ctime
= now
;
862 le2
= me
->fileref
->fcb
->extents
.Flink
;
863 while (le2
!= &me
->fileref
->fcb
->extents
) {
864 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
866 if (!ext
->ignore
&& (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
)) {
867 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
869 if (ed2
->size
!= 0) {
870 chunk
* c
= get_chunk_from_address(me
->fileref
->fcb
->Vcb
, ed2
->address
);
873 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
875 Status
= update_changed_extent_ref(me
->fileref
->fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, me
->fileref
->fcb
->subvol
->id
, me
->fileref
->fcb
->inode
,
876 ext
->offset
- ed2
->offset
, 1, me
->fileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
878 if (!NT_SUCCESS(Status
)) {
879 ERR("update_changed_extent_ref returned %08x\n", Status
);
880 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
891 me
->fileref
->fcb
->subvol
= me
->parent
->fileref
->fcb
->subvol
;
892 me
->fileref
->fcb
->inode
= me
->parent
->fileref
->fcb
->inode
;
895 me
->fileref
->fcb
->created
= TRUE
;
897 InsertHeadList(&me
->fileref
->fcb
->list_entry
, &me
->dummyfcb
->list_entry
);
898 RemoveEntryList(&me
->fileref
->fcb
->list_entry
);
900 le3
= destdir
->fcb
->subvol
->fcbs
.Flink
;
901 while (le3
!= &destdir
->fcb
->subvol
->fcbs
) {
902 fcb
* fcb
= CONTAINING_RECORD(le3
, struct _fcb
, list_entry
);
904 if (fcb
->inode
> me
->fileref
->fcb
->inode
) {
905 InsertHeadList(le3
->Blink
, &me
->fileref
->fcb
->list_entry
);
914 InsertTailList(&destdir
->fcb
->subvol
->fcbs
, &me
->fileref
->fcb
->list_entry
);
916 InsertTailList(&me
->fileref
->fcb
->Vcb
->all_fcbs
, &me
->dummyfcb
->list_entry_all
);
918 while (!IsListEmpty(&me
->fileref
->fcb
->hardlinks
)) {
919 hardlink
* hl
= CONTAINING_RECORD(RemoveHeadList(&me
->fileref
->fcb
->hardlinks
), hardlink
, list_entry
);
922 ExFreePool(hl
->name
.Buffer
);
925 ExFreePool(hl
->utf8
.Buffer
);
930 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
931 mark_fcb_dirty(me
->fileref
->fcb
);
933 if ((!me
->dummyfcb
->ads
&& me
->dummyfcb
->inode_item
.st_nlink
> 1) || (me
->dummyfcb
->ads
&& me
->parent
->dummyfcb
->inode_item
.st_nlink
> 1)) {
934 LIST_ENTRY
* le2
= le
->Flink
;
936 while (le2
!= &move_list
) {
937 move_entry
* me2
= CONTAINING_RECORD(le2
, move_entry
, list_entry
);
939 if (me2
->fileref
->fcb
== me
->fileref
->fcb
&& !me2
->fileref
->fcb
->ads
) {
940 me2
->dummyfcb
= me
->dummyfcb
;
941 InterlockedIncrement(&me
->dummyfcb
->refcount
);
948 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
950 ExAcquireResourceExclusiveLite(me
->fileref
->fcb
->Header
.Resource
, TRUE
);
951 me
->fileref
->fcb
->inode_item
.st_nlink
++;
952 me
->fileref
->fcb
->inode_item_changed
= TRUE
;
953 ExReleaseResourceLite(me
->fileref
->fcb
->Header
.Resource
);
960 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
961 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
963 // loop through list and create new filerefs
965 le
= move_list
.Flink
;
966 while (le
!= &move_list
) {
968 BOOL name_changed
= FALSE
;
970 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
972 me
->dummyfileref
= create_fileref(fileref
->fcb
->Vcb
);
973 if (!me
->dummyfileref
) {
974 ERR("out of memory\n");
975 Status
= STATUS_INSUFFICIENT_RESOURCES
;
979 if (me
->fileref
->fcb
== me
->fileref
->fcb
->Vcb
->dummy_fcb
) {
980 root
* r
= me
->parent
? me
->parent
->fileref
->fcb
->subvol
: destdir
->fcb
->subvol
;
982 Status
= create_directory_fcb(me
->fileref
->fcb
->Vcb
, r
, me
->fileref
->parent
->fcb
, &me
->fileref
->fcb
);
983 if (!NT_SUCCESS(Status
)) {
984 ERR("create_directory_fcb returnd %08x\n", Status
);
988 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
989 me
->fileref
->dc
->key
.obj_type
= TYPE_INODE_ITEM
;
991 me
->dummyfileref
->fcb
= me
->fileref
->fcb
->Vcb
->dummy_fcb
;
992 } else if (me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) {
993 me
->dummyfileref
->fcb
= me
->fileref
->fcb
;
995 me
->fileref
->fcb
->subvol
->parent
= le
== move_list
.Flink
? destdir
->fcb
->subvol
->id
: me
->parent
->fileref
->fcb
->subvol
->id
;
997 me
->dummyfileref
->fcb
= me
->dummyfcb
;
999 InterlockedIncrement(&me
->dummyfileref
->fcb
->refcount
);
1001 me
->dummyfileref
->oldutf8
= me
->fileref
->oldutf8
;
1002 me
->dummyfileref
->oldindex
= me
->fileref
->dc
->index
;
1004 if (le
== move_list
.Flink
&& (me
->fileref
->dc
->utf8
.Length
!= utf8
->Length
|| RtlCompareMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
) != utf8
->Length
))
1005 name_changed
= TRUE
;
1007 if ((le
== move_list
.Flink
|| me
->fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
) && !me
->dummyfileref
->oldutf8
.Buffer
) {
1008 me
->dummyfileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, me
->fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1009 if (!me
->dummyfileref
->oldutf8
.Buffer
) {
1010 ERR("out of memory\n");
1011 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1015 RtlCopyMemory(me
->dummyfileref
->oldutf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
1017 me
->dummyfileref
->oldutf8
.Length
= me
->dummyfileref
->oldutf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
1020 me
->dummyfileref
->delete_on_close
= me
->fileref
->delete_on_close
;
1021 me
->dummyfileref
->deleted
= me
->fileref
->deleted
;
1023 me
->dummyfileref
->created
= me
->fileref
->created
;
1024 me
->fileref
->created
= TRUE
;
1026 me
->dummyfileref
->parent
= me
->parent
? me
->parent
->dummyfileref
: origparent
;
1027 increase_fileref_refcount(me
->dummyfileref
->parent
);
1029 ExAcquireResourceExclusiveLite(&me
->dummyfileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1030 InsertTailList(&me
->dummyfileref
->parent
->children
, &me
->dummyfileref
->list_entry
);
1031 ExReleaseResourceLite(&me
->dummyfileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1033 me
->dummyfileref
->debug_desc
= me
->fileref
->debug_desc
;
1035 if (me
->dummyfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1036 me
->dummyfileref
->fcb
->fileref
= me
->dummyfileref
;
1039 RemoveEntryList(&me
->fileref
->list_entry
);
1041 increase_fileref_refcount(destdir
);
1043 if (me
->fileref
->dc
) {
1044 // remove from old parent
1045 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1046 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1047 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1048 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1050 me
->fileref
->parent
->fcb
->inode_item
.st_size
-= me
->fileref
->dc
->utf8
.Length
* 2;
1051 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1052 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1053 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1054 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1055 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1056 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1059 ExFreePool(me
->fileref
->dc
->utf8
.Buffer
);
1060 ExFreePool(me
->fileref
->dc
->name
.Buffer
);
1061 ExFreePool(me
->fileref
->dc
->name_uc
.Buffer
);
1063 me
->fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1064 if (!me
->fileref
->dc
->utf8
.Buffer
) {
1065 ERR("out of memory\n");
1066 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1070 me
->fileref
->dc
->utf8
.Length
= me
->fileref
->dc
->utf8
.MaximumLength
= utf8
->Length
;
1071 RtlCopyMemory(me
->fileref
->dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1073 me
->fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
->Length
, ALLOC_TAG
);
1074 if (!me
->fileref
->dc
->name
.Buffer
) {
1075 ERR("out of memory\n");
1076 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1080 me
->fileref
->dc
->name
.Length
= me
->fileref
->dc
->name
.MaximumLength
= fnus
->Length
;
1081 RtlCopyMemory(me
->fileref
->dc
->name
.Buffer
, fnus
->Buffer
, fnus
->Length
);
1083 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1084 if (!NT_SUCCESS(Status
)) {
1085 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1089 me
->fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1090 me
->fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)me
->fileref
->dc
->name_uc
.Buffer
, me
->fileref
->dc
->name_uc
.Length
);
1093 if (me
->fileref
->dc
->key
.obj_type
== TYPE_INODE_ITEM
)
1094 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1096 // add to new parent
1098 ExAcquireResourceExclusiveLite(&destdir
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1100 if (IsListEmpty(&destdir
->fcb
->dir_children_index
))
1101 me
->fileref
->dc
->index
= 2;
1103 dir_child
* dc2
= CONTAINING_RECORD(destdir
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1105 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1108 InsertTailList(&destdir
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1109 insert_dir_child_into_hash_lists(destdir
->fcb
, me
->fileref
->dc
);
1110 ExReleaseResourceLite(&destdir
->fcb
->nonpaged
->dir_children_lock
);
1113 free_fileref(me
->fileref
->parent
);
1114 me
->fileref
->parent
= destdir
;
1116 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1117 InsertTailList(&me
->fileref
->parent
->children
, &me
->fileref
->list_entry
);
1118 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1120 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
);
1121 me
->fileref
->parent
->fcb
->inode_item
.st_size
+= me
->fileref
->dc
->utf8
.Length
* 2;
1122 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
);
1123 me
->fileref
->parent
->fcb
->inode_item
.transid
= me
->fileref
->fcb
->Vcb
->superblock
.generation
;
1124 me
->fileref
->parent
->fcb
->inode_item
.sequence
++;
1125 me
->fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
1126 me
->fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
1127 me
->fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
1128 mark_fcb_dirty(me
->fileref
->parent
->fcb
);
1130 if (me
->fileref
->dc
) {
1131 ExAcquireResourceExclusiveLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1132 RemoveEntryList(&me
->fileref
->dc
->list_entry_index
);
1134 if (!me
->fileref
->fcb
->ads
)
1135 remove_dir_child_from_hash_lists(me
->fileref
->parent
->fcb
, me
->fileref
->dc
);
1137 ExReleaseResourceLite(&me
->fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1139 ExAcquireResourceExclusiveLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1141 if (me
->fileref
->fcb
->ads
)
1142 InsertHeadList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1144 if (me
->fileref
->fcb
->inode
!= SUBVOL_ROOT_INODE
)
1145 me
->fileref
->dc
->key
.obj_id
= me
->fileref
->fcb
->inode
;
1147 if (IsListEmpty(&me
->parent
->fileref
->fcb
->dir_children_index
))
1148 me
->fileref
->dc
->index
= 2;
1150 dir_child
* dc2
= CONTAINING_RECORD(me
->parent
->fileref
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1152 me
->fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1155 InsertTailList(&me
->parent
->fileref
->fcb
->dir_children_index
, &me
->fileref
->dc
->list_entry_index
);
1156 insert_dir_child_into_hash_lists(me
->parent
->fileref
->fcb
, me
->fileref
->dc
);
1159 ExReleaseResourceLite(&me
->parent
->fileref
->fcb
->nonpaged
->dir_children_lock
);
1163 if (!me
->dummyfileref
->fcb
->ads
) {
1164 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1165 if (!NT_SUCCESS(Status
)) {
1166 ERR("delete_fileref returned %08x\n", Status
);
1171 if (me
->fileref
->fcb
->inode_item
.st_nlink
> 1) {
1172 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1174 ERR("out of memory\n");
1175 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1179 hl
->parent
= me
->fileref
->parent
->fcb
->inode
;
1180 hl
->index
= me
->fileref
->dc
->index
;
1182 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= me
->fileref
->dc
->utf8
.Length
;
1183 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1184 if (!hl
->utf8
.Buffer
) {
1185 ERR("out of memory\n");
1186 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1191 RtlCopyMemory(hl
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Buffer
, me
->fileref
->dc
->utf8
.Length
);
1193 hl
->name
.Length
= hl
->name
.MaximumLength
= me
->fileref
->dc
->name
.Length
;
1194 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1195 if (!hl
->name
.Buffer
) {
1196 ERR("out of memory\n");
1197 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1198 ExFreePool(hl
->utf8
.Buffer
);
1203 RtlCopyMemory(hl
->name
.Buffer
, me
->fileref
->dc
->name
.Buffer
, me
->fileref
->dc
->name
.Length
);
1205 InsertTailList(&me
->fileref
->fcb
->hardlinks
, &hl
->list_entry
);
1208 mark_fileref_dirty(me
->fileref
);
1213 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1215 le
= move_list
.Flink
;
1216 while (le
!= &move_list
) {
1217 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1219 if (me
->dummyfileref
->fcb
->ads
&& me
->parent
->dummyfileref
->fcb
->deleted
) {
1220 Status
= delete_fileref(me
->dummyfileref
, NULL
, Irp
, rollback
);
1221 if (!NT_SUCCESS(Status
)) {
1222 ERR("delete_fileref returned %08x\n", Status
);
1230 destdir
->fcb
->subvol
->root_item
.ctransid
= destdir
->fcb
->Vcb
->superblock
.generation
;
1231 destdir
->fcb
->subvol
->root_item
.ctime
= now
;
1233 me
= CONTAINING_RECORD(move_list
.Flink
, move_entry
, list_entry
);
1234 send_notification_fileref(fileref
, fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1235 send_notification_fileref(me
->dummyfileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1236 send_notification_fileref(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1238 Status
= STATUS_SUCCESS
;
1241 while (!IsListEmpty(&move_list
)) {
1242 le
= RemoveHeadList(&move_list
);
1243 me
= CONTAINING_RECORD(le
, move_entry
, list_entry
);
1246 free_fcb(me
->dummyfcb
);
1248 if (me
->dummyfileref
)
1249 free_fileref(me
->dummyfileref
);
1251 free_fileref(me
->fileref
);
1256 destdir
->fcb
->subvol
->fcbs_version
++;
1257 fileref
->fcb
->subvol
->fcbs_version
++;
1259 release_fcb_lock(fileref
->fcb
->Vcb
);
1264 void insert_dir_child_into_hash_lists(fcb
* fcb
, dir_child
* dc
) {
1275 le
= fcb
->hash_ptrs
[d
];
1284 le
= fcb
->dir_children_hash
.Flink
;
1286 while (le
!= &fcb
->dir_children_hash
) {
1287 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
1289 if (dc2
->hash
> dc
->hash
) {
1290 InsertHeadList(le
->Blink
, &dc
->list_entry_hash
);
1299 InsertTailList(&fcb
->dir_children_hash
, &dc
->list_entry_hash
);
1301 if (!fcb
->hash_ptrs
[c
])
1302 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1304 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs
[c
], dir_child
, list_entry_hash
);
1306 if (dc2
->hash
> dc
->hash
)
1307 fcb
->hash_ptrs
[c
] = &dc
->list_entry_hash
;
1310 c
= dc
->hash_uc
>> 24;
1316 le
= fcb
->hash_ptrs_uc
[d
];
1325 le
= fcb
->dir_children_hash_uc
.Flink
;
1327 while (le
!= &fcb
->dir_children_hash_uc
) {
1328 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
1330 if (dc2
->hash_uc
> dc
->hash_uc
) {
1331 InsertHeadList(le
->Blink
, &dc
->list_entry_hash_uc
);
1340 InsertTailList(&fcb
->dir_children_hash_uc
, &dc
->list_entry_hash_uc
);
1342 if (!fcb
->hash_ptrs_uc
[c
])
1343 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1345 dir_child
* dc2
= CONTAINING_RECORD(fcb
->hash_ptrs_uc
[c
], dir_child
, list_entry_hash_uc
);
1347 if (dc2
->hash_uc
> dc
->hash_uc
)
1348 fcb
->hash_ptrs_uc
[c
] = &dc
->list_entry_hash_uc
;
1352 static NTSTATUS
set_rename_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
1353 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1354 FILE_RENAME_INFORMATION
* fri
= Irp
->AssociatedIrp
.SystemBuffer
;
1355 fcb
*fcb
= FileObject
->FsContext
;
1356 ccb
* ccb
= FileObject
->FsContext2
;
1357 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
1359 ULONG fnlen
, utf8len
, origutf8len
;
1360 UNICODE_STRING fnus
;
1365 LIST_ENTRY rollback
, *le
;
1367 SECURITY_SUBJECT_CONTEXT subjcont
;
1370 InitializeListHead(&rollback
);
1372 TRACE("tfo = %p\n", tfo
);
1373 TRACE("ReplaceIfExists = %u\n", IrpSp
->Parameters
.SetFile
.ReplaceIfExists
);
1374 TRACE("RootDirectory = %p\n", fri
->RootDirectory
);
1375 TRACE("FileName = %.*S\n", fri
->FileNameLength
/ sizeof(WCHAR
), fri
->FileName
);
1378 fnlen
= fri
->FileNameLength
/ sizeof(WCHAR
);
1381 if (!fileref
|| !fileref
->parent
) {
1382 ERR("no fileref set and no directory given\n");
1383 return STATUS_INVALID_PARAMETER
;
1388 while (fnlen
> 0 && (fri
->FileName
[fnlen
- 1] == '/' || fri
->FileName
[fnlen
- 1] == '\\'))
1392 return STATUS_INVALID_PARAMETER
;
1394 for (i
= fnlen
- 1; i
>= 0; i
--) {
1395 if (fri
->FileName
[i
] == '\\' || fri
->FileName
[i
] == '/') {
1396 fn
= &fri
->FileName
[i
+1];
1397 fnlen
= (fri
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
1403 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
1404 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
1405 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1408 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1409 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1410 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1411 Status
= STATUS_INVALID_PARAMETER
;
1416 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
1418 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1420 origutf8len
= fileref
->dc
->utf8
.Length
;
1422 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1423 if (!NT_SUCCESS(Status
))
1426 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
1427 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
1429 ERR("out of memory\n");
1430 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1434 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
1435 if (!NT_SUCCESS(Status
))
1438 if (tfo
&& tfo
->FsContext2
) {
1439 struct _ccb
* relatedccb
= tfo
->FsContext2
;
1441 related
= relatedccb
->fileref
;
1442 increase_fileref_refcount(related
);
1443 } else if (fnus
.Length
>= sizeof(WCHAR
) && fnus
.Buffer
[0] != '\\') {
1444 related
= fileref
->parent
;
1445 increase_fileref_refcount(related
);
1448 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1450 if (NT_SUCCESS(Status
)) {
1451 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref
));
1453 if (fileref
!= oldfileref
&& !oldfileref
->deleted
) {
1454 if (!IrpSp
->Parameters
.SetFile
.ReplaceIfExists
) {
1455 Status
= STATUS_OBJECT_NAME_COLLISION
;
1457 } else if ((oldfileref
->open_count
>= 1 || has_open_children(oldfileref
)) && !oldfileref
->deleted
) {
1458 WARN("trying to overwrite open file\n");
1459 Status
= STATUS_ACCESS_DENIED
;
1463 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1464 WARN("trying to overwrite directory\n");
1465 Status
= STATUS_ACCESS_DENIED
;
1470 if (fileref
== oldfileref
|| oldfileref
->deleted
) {
1471 free_fileref(oldfileref
);
1477 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
1479 if (!NT_SUCCESS(Status
)) {
1480 ERR("open_fileref returned %08x\n", Status
);
1485 if (related
->fcb
== Vcb
->dummy_fcb
) {
1486 Status
= STATUS_ACCESS_DENIED
;
1490 SeCaptureSubjectContext(&subjcont
);
1492 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
1493 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1494 SeReleaseSubjectContext(&subjcont
);
1495 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1499 SeReleaseSubjectContext(&subjcont
);
1501 if (has_open_children(fileref
)) {
1502 WARN("trying to rename file with open children\n");
1503 Status
= STATUS_ACCESS_DENIED
;
1508 SeCaptureSubjectContext(&subjcont
);
1510 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
1511 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
1512 SeReleaseSubjectContext(&subjcont
);
1513 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
1517 SeReleaseSubjectContext(&subjcont
);
1519 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
1520 if (!NT_SUCCESS(Status
)) {
1521 ERR("delete_fileref returned %08x\n", Status
);
1526 if (fileref
->parent
->fcb
->subvol
!= related
->fcb
->subvol
&& (fileref
->fcb
->subvol
== fileref
->parent
->fcb
->subvol
|| fileref
->fcb
== Vcb
->dummy_fcb
)) {
1527 Status
= move_across_subvols(fileref
, ccb
, related
, &utf8
, &fnus
, Irp
, &rollback
);
1528 if (!NT_SUCCESS(Status
)) {
1529 ERR("move_across_subvols returned %08x\n", Status
);
1534 if (related
== fileref
->parent
) { // keeping file in same directory
1535 UNICODE_STRING oldfn
, newfn
;
1537 ULONG reqlen
, oldutf8len
;
1539 oldfn
.Length
= oldfn
.MaximumLength
= 0;
1541 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1542 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1543 ERR("fileref_get_filename returned %08x\n", Status
);
1547 oldfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1548 if (!oldfn
.Buffer
) {
1549 ERR("out of memory\n");
1550 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1554 oldfn
.MaximumLength
= (UINT16
)reqlen
;
1556 Status
= fileref_get_filename(fileref
, &oldfn
, &name_offset
, &reqlen
);
1557 if (!NT_SUCCESS(Status
)) {
1558 ERR("fileref_get_filename returned %08x\n", Status
);
1559 ExFreePool(oldfn
.Buffer
);
1563 oldutf8len
= fileref
->dc
->utf8
.Length
;
1565 if (!fileref
->created
&& !fileref
->oldutf8
.Buffer
) {
1566 fileref
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1567 if (!fileref
->oldutf8
.Buffer
) {
1568 ERR("out of memory\n");
1569 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1573 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1574 RtlCopyMemory(fileref
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1577 TRACE("renaming %.*S to %.*S\n", fileref
->dc
->name
.Length
/ sizeof(WCHAR
), fileref
->dc
->name
.Buffer
, fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
1579 mark_fileref_dirty(fileref
);
1582 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1584 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1585 ExFreePool(fileref
->dc
->name
.Buffer
);
1586 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1588 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1589 if (!fileref
->dc
->utf8
.Buffer
) {
1590 ERR("out of memory\n");
1591 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1592 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1593 ExFreePool(oldfn
.Buffer
);
1597 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1598 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1600 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1601 if (!fileref
->dc
->name
.Buffer
) {
1602 ERR("out of memory\n");
1603 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1604 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1605 ExFreePool(oldfn
.Buffer
);
1609 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1610 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1612 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1613 if (!NT_SUCCESS(Status
)) {
1614 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1615 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1616 ExFreePool(oldfn
.Buffer
);
1620 remove_dir_child_from_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1622 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1623 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1625 insert_dir_child_into_hash_lists(fileref
->parent
->fcb
, fileref
->dc
);
1627 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1630 newfn
.Length
= newfn
.MaximumLength
= 0;
1632 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1633 if (Status
!= STATUS_BUFFER_OVERFLOW
) {
1634 ERR("fileref_get_filename returned %08x\n", Status
);
1635 ExFreePool(oldfn
.Buffer
);
1639 newfn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1640 if (!newfn
.Buffer
) {
1641 ERR("out of memory\n");
1642 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1643 ExFreePool(oldfn
.Buffer
);
1647 newfn
.MaximumLength
= (UINT16
)reqlen
;
1649 Status
= fileref_get_filename(fileref
, &newfn
, &name_offset
, &reqlen
);
1650 if (!NT_SUCCESS(Status
)) {
1651 ERR("fileref_get_filename returned %08x\n", Status
);
1652 ExFreePool(oldfn
.Buffer
);
1653 ExFreePool(newfn
.Buffer
);
1657 KeQuerySystemTime(&time
);
1658 win_time_to_unix(time
, &now
);
1660 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1661 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1662 fcb
->inode_item
.sequence
++;
1664 if (!ccb
->user_set_change_time
)
1665 fcb
->inode_item
.st_ctime
= now
;
1667 fcb
->inode_item_changed
= TRUE
;
1668 mark_fcb_dirty(fcb
);
1671 // update parent's INODE_ITEM
1673 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1674 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1675 related
->fcb
->inode_item
.st_size
= related
->fcb
->inode_item
.st_size
+ (2 * utf8
.Length
) - (2* oldutf8len
);
1676 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1677 related
->fcb
->inode_item
.sequence
++;
1678 related
->fcb
->inode_item
.st_ctime
= now
;
1679 related
->fcb
->inode_item
.st_mtime
= now
;
1681 related
->fcb
->inode_item_changed
= TRUE
;
1682 mark_fcb_dirty(related
->fcb
);
1683 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1685 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&oldfn
, name_offset
, NULL
, NULL
,
1686 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_OLD_NAME
, NULL
, NULL
);
1687 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&newfn
, name_offset
, NULL
, NULL
,
1688 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_RENAMED_NEW_NAME
, NULL
, NULL
);
1690 ExFreePool(oldfn
.Buffer
);
1691 ExFreePool(newfn
.Buffer
);
1693 Status
= STATUS_SUCCESS
;
1697 // We move files by moving the existing fileref to the new directory, and
1698 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1700 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
, NULL
);
1702 fr2
= create_fileref(Vcb
);
1704 fr2
->fcb
= fileref
->fcb
;
1705 fr2
->fcb
->refcount
++;
1707 fr2
->oldutf8
= fileref
->oldutf8
;
1708 fr2
->oldindex
= fileref
->dc
->index
;
1709 fr2
->delete_on_close
= fileref
->delete_on_close
;
1710 fr2
->deleted
= TRUE
;
1711 fr2
->created
= fileref
->created
;
1712 fr2
->parent
= fileref
->parent
;
1715 if (!fr2
->oldutf8
.Buffer
) {
1716 fr2
->oldutf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
1717 if (!fr2
->oldutf8
.Buffer
) {
1718 ERR("out of memory\n");
1719 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1723 RtlCopyMemory(fr2
->oldutf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1725 fr2
->oldutf8
.Length
= fr2
->oldutf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1728 if (fr2
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
1729 fr2
->fcb
->fileref
= fr2
;
1731 if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
)
1732 fileref
->fcb
->subvol
->parent
= related
->fcb
->subvol
->id
;
1734 fileref
->oldutf8
.Length
= fileref
->oldutf8
.MaximumLength
= 0;
1735 fileref
->oldutf8
.Buffer
= NULL
;
1736 fileref
->deleted
= FALSE
;
1737 fileref
->created
= TRUE
;
1738 fileref
->parent
= related
;
1740 ExAcquireResourceExclusiveLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1741 InsertHeadList(&fileref
->list_entry
, &fr2
->list_entry
);
1742 RemoveEntryList(&fileref
->list_entry
);
1743 ExReleaseResourceLite(&fileref
->parent
->fcb
->nonpaged
->dir_children_lock
);
1745 mark_fileref_dirty(fr2
);
1746 mark_fileref_dirty(fileref
);
1749 // remove from old parent
1750 ExAcquireResourceExclusiveLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1751 RemoveEntryList(&fileref
->dc
->list_entry_index
);
1752 remove_dir_child_from_hash_lists(fr2
->parent
->fcb
, fileref
->dc
);
1753 ExReleaseResourceLite(&fr2
->parent
->fcb
->nonpaged
->dir_children_lock
);
1755 if (fileref
->dc
->utf8
.Length
!= utf8
.Length
|| RtlCompareMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
) != utf8
.Length
) {
1756 // handle changed name
1758 ExFreePool(fileref
->dc
->utf8
.Buffer
);
1759 ExFreePool(fileref
->dc
->name
.Buffer
);
1760 ExFreePool(fileref
->dc
->name_uc
.Buffer
);
1762 fileref
->dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.Length
, ALLOC_TAG
);
1763 if (!fileref
->dc
->utf8
.Buffer
) {
1764 ERR("out of memory\n");
1765 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1769 fileref
->dc
->utf8
.Length
= fileref
->dc
->utf8
.MaximumLength
= utf8
.Length
;
1770 RtlCopyMemory(fileref
->dc
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
1772 fileref
->dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
1773 if (!fileref
->dc
->name
.Buffer
) {
1774 ERR("out of memory\n");
1775 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1779 fileref
->dc
->name
.Length
= fileref
->dc
->name
.MaximumLength
= fnus
.Length
;
1780 RtlCopyMemory(fileref
->dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1782 Status
= RtlUpcaseUnicodeString(&fileref
->dc
->name_uc
, &fileref
->dc
->name
, TRUE
);
1783 if (!NT_SUCCESS(Status
)) {
1784 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1788 fileref
->dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
1789 fileref
->dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)fileref
->dc
->name_uc
.Buffer
, fileref
->dc
->name_uc
.Length
);
1792 // add to new parent
1793 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1795 if (IsListEmpty(&related
->fcb
->dir_children_index
))
1796 fileref
->dc
->index
= 2;
1798 dir_child
* dc2
= CONTAINING_RECORD(related
->fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1800 fileref
->dc
->index
= max(2, dc2
->index
+ 1);
1803 InsertTailList(&related
->fcb
->dir_children_index
, &fileref
->dc
->list_entry_index
);
1804 insert_dir_child_into_hash_lists(related
->fcb
, fileref
->dc
);
1805 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
1808 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1809 InsertTailList(&related
->children
, &fileref
->list_entry
);
1810 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
1812 if (fcb
->inode_item
.st_nlink
> 1) {
1813 // add new hardlink entry to fcb
1815 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
1817 ERR("out of memory\n");
1818 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1822 hl
->parent
= related
->fcb
->inode
;
1823 hl
->index
= fileref
->dc
->index
;
1825 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
1826 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
1828 if (!hl
->name
.Buffer
) {
1829 ERR("out of memory\n");
1831 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1835 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
1837 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
1838 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
1840 if (!hl
->utf8
.Buffer
) {
1841 ERR("out of memory\n");
1842 ExFreePool(hl
->name
.Buffer
);
1844 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1848 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
1850 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
1853 // delete old hardlink entry from fcb
1855 le
= fcb
->hardlinks
.Flink
;
1856 while (le
!= &fcb
->hardlinks
) {
1857 hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1859 if (hl
->parent
== fr2
->parent
->fcb
->inode
&& hl
->index
== fr2
->oldindex
) {
1860 RemoveEntryList(&hl
->list_entry
);
1862 if (hl
->utf8
.Buffer
)
1863 ExFreePool(hl
->utf8
.Buffer
);
1865 if (hl
->name
.Buffer
)
1866 ExFreePool(hl
->name
.Buffer
);
1875 // update inode's INODE_ITEM
1877 KeQuerySystemTime(&time
);
1878 win_time_to_unix(time
, &now
);
1880 if (fcb
!= Vcb
->dummy_fcb
&& (fileref
->parent
->fcb
->subvol
== fcb
->subvol
|| !is_subvol_readonly(fcb
->subvol
, Irp
))) {
1881 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1882 fcb
->inode_item
.sequence
++;
1884 if (!ccb
->user_set_change_time
)
1885 fcb
->inode_item
.st_ctime
= now
;
1887 fcb
->inode_item_changed
= TRUE
;
1888 mark_fcb_dirty(fcb
);
1891 // update new parent's INODE_ITEM
1893 related
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1894 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1895 related
->fcb
->inode_item
.st_size
+= 2 * utf8len
;
1896 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related
->fcb
->inode
, related
->fcb
->inode_item
.st_size
);
1897 related
->fcb
->inode_item
.sequence
++;
1898 related
->fcb
->inode_item
.st_ctime
= now
;
1899 related
->fcb
->inode_item
.st_mtime
= now
;
1901 related
->fcb
->inode_item_changed
= TRUE
;
1902 mark_fcb_dirty(related
->fcb
);
1904 // update old parent's INODE_ITEM
1906 fr2
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1907 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1908 fr2
->parent
->fcb
->inode_item
.st_size
-= 2 * origutf8len
;
1909 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2
->parent
->fcb
->inode
, fr2
->parent
->fcb
->inode_item
.st_size
);
1910 fr2
->parent
->fcb
->inode_item
.sequence
++;
1911 fr2
->parent
->fcb
->inode_item
.st_ctime
= now
;
1912 fr2
->parent
->fcb
->inode_item
.st_mtime
= now
;
1916 fr2
->parent
->fcb
->inode_item_changed
= TRUE
;
1917 mark_fcb_dirty(fr2
->parent
->fcb
);
1919 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
1920 send_notification_fileref(related
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1921 send_notification_fileref(fr2
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
1923 Status
= STATUS_SUCCESS
;
1927 free_fileref(oldfileref
);
1929 if (!NT_SUCCESS(Status
) && related
)
1930 free_fileref(related
);
1932 if (!NT_SUCCESS(Status
) && fr2
)
1935 if (NT_SUCCESS(Status
))
1936 clear_rollback(&rollback
);
1938 do_rollback(Vcb
, &rollback
);
1940 ExReleaseResourceLite(fcb
->Header
.Resource
);
1941 ExReleaseResourceLite(&Vcb
->fileref_lock
);
1942 ExReleaseResourceLite(&Vcb
->tree_lock
);
1947 NTSTATUS
stream_set_end_of_file_information(device_extension
* Vcb
, UINT16 end
, fcb
* fcb
, file_ref
* fileref
, BOOL advance_only
) {
1951 TRACE("setting new end to %llx bytes (currently %x)\n", end
, fcb
->adsdata
.Length
);
1953 if (!fileref
|| !fileref
->parent
) {
1954 ERR("no fileref for stream\n");
1955 return STATUS_INTERNAL_ERROR
;
1958 if (end
< fcb
->adsdata
.Length
) {
1960 return STATUS_SUCCESS
;
1962 TRACE("truncating stream to %llx bytes\n", end
);
1964 fcb
->adsdata
.Length
= end
;
1965 } else if (end
> fcb
->adsdata
.Length
) {
1966 TRACE("extending stream to %llx bytes\n", end
);
1968 if (end
> fcb
->adsmaxlen
) {
1969 ERR("error - xattr too long (%u > %u)\n", end
, fcb
->adsmaxlen
);
1970 return STATUS_DISK_FULL
;
1973 if (end
> fcb
->adsdata
.MaximumLength
) {
1974 char* data
= ExAllocatePoolWithTag(PagedPool
, end
, ALLOC_TAG
);
1976 ERR("out of memory\n");
1978 return STATUS_INSUFFICIENT_RESOURCES
;
1981 if (fcb
->adsdata
.Buffer
) {
1982 RtlCopyMemory(data
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
1983 ExFreePool(fcb
->adsdata
.Buffer
);
1986 fcb
->adsdata
.Buffer
= data
;
1987 fcb
->adsdata
.MaximumLength
= end
;
1990 RtlZeroMemory(&fcb
->adsdata
.Buffer
[fcb
->adsdata
.Length
], end
- fcb
->adsdata
.Length
);
1992 fcb
->adsdata
.Length
= end
;
1995 mark_fcb_dirty(fcb
);
1997 fcb
->Header
.AllocationSize
.QuadPart
= end
;
1998 fcb
->Header
.FileSize
.QuadPart
= end
;
1999 fcb
->Header
.ValidDataLength
.QuadPart
= end
;
2001 KeQuerySystemTime(&time
);
2002 win_time_to_unix(time
, &now
);
2004 fileref
->parent
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2005 fileref
->parent
->fcb
->inode_item
.sequence
++;
2006 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2008 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2009 mark_fcb_dirty(fileref
->parent
->fcb
);
2011 fileref
->parent
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2012 fileref
->parent
->fcb
->subvol
->root_item
.ctime
= now
;
2014 return STATUS_SUCCESS
;
2017 static NTSTATUS
set_end_of_file_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, BOOL advance_only
, BOOL prealloc
) {
2018 FILE_END_OF_FILE_INFORMATION
* feofi
= Irp
->AssociatedIrp
.SystemBuffer
;
2019 fcb
* fcb
= FileObject
->FsContext
;
2020 ccb
* ccb
= FileObject
->FsContext2
;
2021 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2025 LIST_ENTRY rollback
;
2026 BOOL set_size
= FALSE
;
2030 ERR("fileref is NULL\n");
2031 return STATUS_INVALID_PARAMETER
;
2034 InitializeListHead(&rollback
);
2036 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2038 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2040 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2041 Status
= STATUS_FILE_CLOSED
;
2046 if (feofi
->EndOfFile
.QuadPart
> 0xffff) {
2047 Status
= STATUS_DISK_FULL
;
2051 if (feofi
->EndOfFile
.QuadPart
< 0) {
2052 Status
= STATUS_INVALID_PARAMETER
;
2056 Status
= stream_set_end_of_file_information(Vcb
, (UINT16
)feofi
->EndOfFile
.QuadPart
, fcb
, fileref
, advance_only
);
2058 if (NT_SUCCESS(Status
)) {
2059 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2060 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2061 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2065 filter
= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
2067 if (!ccb
->user_set_write_time
) {
2068 KeQuerySystemTime(&time
);
2069 win_time_to_unix(time
, &fileref
->parent
->fcb
->inode_item
.st_mtime
);
2070 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2072 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
2073 mark_fcb_dirty(fileref
->parent
->fcb
);
2076 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED_STREAM
, &fileref
->dc
->name
);
2081 TRACE("file: %S\n", file_desc(FileObject
));
2082 TRACE("paging IO: %s\n", Irp
->Flags
& IRP_PAGING_IO
? "TRUE" : "FALSE");
2083 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2084 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2086 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi
->EndOfFile
.QuadPart
, fcb
->inode_item
.st_size
);
2088 if ((UINT64
)feofi
->EndOfFile
.QuadPart
< fcb
->inode_item
.st_size
) {
2090 Status
= STATUS_SUCCESS
;
2094 TRACE("truncating file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2096 if (!MmCanFileBeTruncated(&fcb
->nonpaged
->segment_object
, &feofi
->EndOfFile
)) {
2097 Status
= STATUS_USER_MAPPED_FILE
;
2101 Status
= truncate_file(fcb
, feofi
->EndOfFile
.QuadPart
, Irp
, &rollback
);
2102 if (!NT_SUCCESS(Status
)) {
2103 ERR("error - truncate_file failed\n");
2106 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
> fcb
->inode_item
.st_size
) {
2107 if (Irp
->Flags
& IRP_PAGING_IO
) {
2108 TRACE("paging IO tried to extend file size\n");
2109 Status
= STATUS_SUCCESS
;
2113 TRACE("extending file to %llx bytes\n", feofi
->EndOfFile
.QuadPart
);
2115 Status
= extend_file(fcb
, fileref
, feofi
->EndOfFile
.QuadPart
, prealloc
, NULL
, &rollback
);
2116 if (!NT_SUCCESS(Status
)) {
2117 ERR("error - extend_file failed\n");
2120 } else if ((UINT64
)feofi
->EndOfFile
.QuadPart
== fcb
->inode_item
.st_size
&& advance_only
) {
2121 Status
= STATUS_SUCCESS
;
2125 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2126 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2127 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
2130 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2132 if (!ccb
->user_set_write_time
) {
2133 KeQuerySystemTime(&time
);
2134 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2135 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2138 fcb
->inode_item_changed
= TRUE
;
2139 mark_fcb_dirty(fcb
);
2140 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2142 Status
= STATUS_SUCCESS
;
2145 if (NT_SUCCESS(Status
))
2146 clear_rollback(&rollback
);
2148 do_rollback(Vcb
, &rollback
);
2150 ExReleaseResourceLite(fcb
->Header
.Resource
);
2154 CcSetFileSizes(FileObject
, &ccfs
);
2155 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2156 Status
= _SEH2_GetExceptionCode();
2159 if (!NT_SUCCESS(Status
))
2160 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2163 ExReleaseResourceLite(&Vcb
->tree_lock
);
2168 static NTSTATUS
set_position_information(PFILE_OBJECT FileObject
, PIRP Irp
) {
2169 FILE_POSITION_INFORMATION
* fpi
= (FILE_POSITION_INFORMATION
*)Irp
->AssociatedIrp
.SystemBuffer
;
2171 TRACE("setting the position on %S to %llx\n", file_desc(FileObject
), fpi
->CurrentByteOffset
.QuadPart
);
2173 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2175 FileObject
->CurrentByteOffset
= fpi
->CurrentByteOffset
;
2177 return STATUS_SUCCESS
;
2180 static NTSTATUS
set_link_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
, PFILE_OBJECT tfo
) {
2181 FILE_LINK_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
2182 fcb
*fcb
= FileObject
->FsContext
, *tfofcb
, *parfcb
;
2183 ccb
* ccb
= FileObject
->FsContext2
;
2184 file_ref
*fileref
= ccb
? ccb
->fileref
: NULL
, *oldfileref
= NULL
, *related
= NULL
, *fr2
= NULL
;
2186 ULONG fnlen
, utf8len
;
2187 UNICODE_STRING fnus
;
2192 LIST_ENTRY rollback
;
2195 SECURITY_SUBJECT_CONTEXT subjcont
;
2196 dir_child
* dc
= NULL
;
2198 InitializeListHead(&rollback
);
2200 // FIXME - check fli length
2201 // FIXME - don't ignore fli->RootDirectory
2203 TRACE("ReplaceIfExists = %x\n", fli
->ReplaceIfExists
);
2204 TRACE("RootDirectory = %p\n", fli
->RootDirectory
);
2205 TRACE("FileNameLength = %x\n", fli
->FileNameLength
);
2206 TRACE("FileName = %.*S\n", fli
->FileNameLength
/ sizeof(WCHAR
), fli
->FileName
);
2209 fnlen
= fli
->FileNameLength
/ sizeof(WCHAR
);
2212 if (!fileref
|| !fileref
->parent
) {
2213 ERR("no fileref set and no directory given\n");
2214 return STATUS_INVALID_PARAMETER
;
2217 parfcb
= fileref
->parent
->fcb
;
2222 tfofcb
= tfo
->FsContext
;
2225 while (fnlen
> 0 && (fli
->FileName
[fnlen
- 1] == '/' || fli
->FileName
[fnlen
- 1] == '\\'))
2229 return STATUS_INVALID_PARAMETER
;
2231 for (i
= fnlen
- 1; i
>= 0; i
--) {
2232 if (fli
->FileName
[i
] == '\\' || fli
->FileName
[i
] == '/') {
2233 fn
= &fli
->FileName
[i
+1];
2234 fnlen
= (fli
->FileNameLength
/ sizeof(WCHAR
)) - i
- 1;
2240 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2241 ExAcquireResourceExclusiveLite(&Vcb
->fileref_lock
, TRUE
);
2242 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2244 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2245 WARN("tried to create hard link on directory\n");
2246 Status
= STATUS_FILE_IS_A_DIRECTORY
;
2251 WARN("tried to create hard link on stream\n");
2252 Status
= STATUS_INVALID_PARAMETER
;
2256 if (fcb
->inode_item
.st_nlink
>= 65535) {
2257 Status
= STATUS_TOO_MANY_LINKS
;
2262 fnus
.Length
= fnus
.MaximumLength
= (UINT16
)(fnlen
* sizeof(WCHAR
));
2264 TRACE("fnus = %.*S\n", fnus
.Length
/ sizeof(WCHAR
), fnus
.Buffer
);
2266 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2267 if (!NT_SUCCESS(Status
))
2270 utf8
.MaximumLength
= utf8
.Length
= (UINT16
)utf8len
;
2271 utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
.MaximumLength
, ALLOC_TAG
);
2273 ERR("out of memory\n");
2274 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2278 Status
= RtlUnicodeToUTF8N(utf8
.Buffer
, utf8len
, &utf8len
, fn
, (ULONG
)fnlen
* sizeof(WCHAR
));
2279 if (!NT_SUCCESS(Status
))
2282 if (tfo
&& tfo
->FsContext2
) {
2283 struct _ccb
* relatedccb
= tfo
->FsContext2
;
2285 related
= relatedccb
->fileref
;
2286 increase_fileref_refcount(related
);
2289 Status
= open_fileref(Vcb
, &oldfileref
, &fnus
, related
, FALSE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2291 if (NT_SUCCESS(Status
)) {
2292 if (!oldfileref
->deleted
) {
2293 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref
));
2295 if (!fli
->ReplaceIfExists
) {
2296 Status
= STATUS_OBJECT_NAME_COLLISION
;
2298 } else if (oldfileref
->open_count
>= 1 && !oldfileref
->deleted
) {
2299 WARN("trying to overwrite open file\n");
2300 Status
= STATUS_ACCESS_DENIED
;
2302 } else if (fileref
== oldfileref
) {
2303 Status
= STATUS_ACCESS_DENIED
;
2307 if (oldfileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2308 WARN("trying to overwrite directory\n");
2309 Status
= STATUS_ACCESS_DENIED
;
2313 free_fileref(oldfileref
);
2319 Status
= open_fileref(Vcb
, &related
, &fnus
, NULL
, TRUE
, NULL
, NULL
, PagedPool
, ccb
->case_sensitive
, Irp
);
2321 if (!NT_SUCCESS(Status
)) {
2322 ERR("open_fileref returned %08x\n", Status
);
2327 SeCaptureSubjectContext(&subjcont
);
2329 if (!SeAccessCheck(related
->fcb
->sd
, &subjcont
, FALSE
, FILE_ADD_FILE
, 0, NULL
,
2330 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2331 SeReleaseSubjectContext(&subjcont
);
2332 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2336 SeReleaseSubjectContext(&subjcont
);
2338 if (fcb
->subvol
!= parfcb
->subvol
) {
2339 WARN("can't create hard link over subvolume boundary\n");
2340 Status
= STATUS_INVALID_PARAMETER
;
2345 SeCaptureSubjectContext(&subjcont
);
2347 if (!SeAccessCheck(oldfileref
->fcb
->sd
, &subjcont
, FALSE
, DELETE
, 0, NULL
,
2348 IoGetFileObjectGenericMapping(), Irp
->RequestorMode
, &access
, &Status
)) {
2349 SeReleaseSubjectContext(&subjcont
);
2350 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
2354 SeReleaseSubjectContext(&subjcont
);
2356 Status
= delete_fileref(oldfileref
, NULL
, Irp
, &rollback
);
2357 if (!NT_SUCCESS(Status
)) {
2358 ERR("delete_fileref returned %08x\n", Status
);
2363 fr2
= create_fileref(Vcb
);
2368 fr2
->created
= TRUE
;
2369 fr2
->parent
= related
;
2371 Status
= add_dir_child(related
->fcb
, fcb
->inode
, FALSE
, &utf8
, &fnus
, fcb
->type
, &dc
);
2372 if (!NT_SUCCESS(Status
))
2373 WARN("add_dir_child returned %08x\n", Status
);
2378 ExAcquireResourceExclusiveLite(&related
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2379 InsertTailList(&related
->children
, &fr2
->list_entry
);
2380 ExReleaseResourceLite(&related
->fcb
->nonpaged
->dir_children_lock
);
2382 // add hardlink for existing fileref, if it's not there already
2383 if (IsListEmpty(&fcb
->hardlinks
)) {
2384 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2386 ERR("out of memory\n");
2387 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2391 hl
->parent
= fileref
->parent
->fcb
->inode
;
2392 hl
->index
= fileref
->dc
->index
;
2394 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2395 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fnus
.Length
, ALLOC_TAG
);
2397 if (!hl
->name
.Buffer
) {
2398 ERR("out of memory\n");
2400 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2404 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2406 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= fileref
->dc
->utf8
.Length
;
2407 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2409 if (!hl
->utf8
.Buffer
) {
2410 ERR("out of memory\n");
2411 ExFreePool(hl
->name
.Buffer
);
2413 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2417 RtlCopyMemory(hl
->utf8
.Buffer
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
2419 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2422 hl
= ExAllocatePoolWithTag(PagedPool
, sizeof(hardlink
), ALLOC_TAG
);
2424 ERR("out of memory\n");
2425 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2429 hl
->parent
= related
->fcb
->inode
;
2430 hl
->index
= dc
->index
;
2432 hl
->name
.Length
= hl
->name
.MaximumLength
= fnus
.Length
;
2433 hl
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->name
.MaximumLength
, ALLOC_TAG
);
2435 if (!hl
->name
.Buffer
) {
2436 ERR("out of memory\n");
2438 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2442 RtlCopyMemory(hl
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
);
2444 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= utf8
.Length
;
2445 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
2447 if (!hl
->utf8
.Buffer
) {
2448 ERR("out of memory\n");
2449 ExFreePool(hl
->name
.Buffer
);
2451 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2455 RtlCopyMemory(hl
->utf8
.Buffer
, utf8
.Buffer
, utf8
.Length
);
2456 ExFreePool(utf8
.Buffer
);
2458 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
2460 mark_fileref_dirty(fr2
);
2463 // update inode's INODE_ITEM
2465 KeQuerySystemTime(&time
);
2466 win_time_to_unix(time
, &now
);
2468 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2469 fcb
->inode_item
.sequence
++;
2470 fcb
->inode_item
.st_nlink
++;
2472 if (!ccb
->user_set_change_time
)
2473 fcb
->inode_item
.st_ctime
= now
;
2475 fcb
->inode_item_changed
= TRUE
;
2476 mark_fcb_dirty(fcb
);
2478 // update parent's INODE_ITEM
2480 parfcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2481 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2482 parfcb
->inode_item
.st_size
+= 2 * utf8len
;
2483 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb
->inode
, parfcb
->inode_item
.st_size
);
2484 parfcb
->inode_item
.sequence
++;
2485 parfcb
->inode_item
.st_ctime
= now
;
2487 parfcb
->inode_item_changed
= TRUE
;
2488 mark_fcb_dirty(parfcb
);
2490 send_notification_fileref(fr2
, FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2492 Status
= STATUS_SUCCESS
;
2496 free_fileref(oldfileref
);
2498 if (!NT_SUCCESS(Status
) && related
)
2499 free_fileref(related
);
2501 if (!NT_SUCCESS(Status
) && fr2
)
2504 if (NT_SUCCESS(Status
))
2505 clear_rollback(&rollback
);
2507 do_rollback(Vcb
, &rollback
);
2509 ExReleaseResourceLite(fcb
->Header
.Resource
);
2510 ExReleaseResourceLite(&Vcb
->fileref_lock
);
2511 ExReleaseResourceLite(&Vcb
->tree_lock
);
2516 static NTSTATUS
set_valid_data_length_information(device_extension
* Vcb
, PIRP Irp
, PFILE_OBJECT FileObject
) {
2517 FILE_VALID_DATA_LENGTH_INFORMATION
* fvdli
= Irp
->AssociatedIrp
.SystemBuffer
;
2518 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2519 fcb
* fcb
= FileObject
->FsContext
;
2520 ccb
* ccb
= FileObject
->FsContext2
;
2521 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
2525 LIST_ENTRY rollback
;
2526 BOOL set_size
= FALSE
;
2529 if (IrpSp
->Parameters
.SetFile
.Length
< sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
)) {
2530 ERR("input buffer length was %u, expected %u\n", IrpSp
->Parameters
.SetFile
.Length
, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
));
2531 return STATUS_INVALID_PARAMETER
;
2535 ERR("fileref is NULL\n");
2536 return STATUS_INVALID_PARAMETER
;
2539 InitializeListHead(&rollback
);
2541 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2543 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
2545 if (fcb
->atts
& FILE_ATTRIBUTE_SPARSE_FILE
) {
2546 Status
= STATUS_INVALID_PARAMETER
;
2550 if (fvdli
->ValidDataLength
.QuadPart
<= fcb
->Header
.ValidDataLength
.QuadPart
|| fvdli
->ValidDataLength
.QuadPart
> fcb
->Header
.FileSize
.QuadPart
) {
2551 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli
->ValidDataLength
.QuadPart
,
2552 fcb
->Header
.ValidDataLength
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
);
2553 Status
= STATUS_INVALID_PARAMETER
;
2557 if (fileref
? fileref
->deleted
: fcb
->deleted
) {
2558 Status
= STATUS_FILE_CLOSED
;
2562 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2563 // and we set it to the max anyway.
2565 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
2566 ccfs
.FileSize
= fcb
->Header
.FileSize
;
2567 ccfs
.ValidDataLength
= fvdli
->ValidDataLength
;
2570 filter
= FILE_NOTIFY_CHANGE_SIZE
;
2572 if (!ccb
->user_set_write_time
) {
2573 KeQuerySystemTime(&time
);
2574 win_time_to_unix(time
, &fcb
->inode_item
.st_mtime
);
2575 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2578 fcb
->inode_item_changed
= TRUE
;
2579 mark_fcb_dirty(fcb
);
2581 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
2583 Status
= STATUS_SUCCESS
;
2586 if (NT_SUCCESS(Status
))
2587 clear_rollback(&rollback
);
2589 do_rollback(Vcb
, &rollback
);
2591 ExReleaseResourceLite(fcb
->Header
.Resource
);
2595 CcSetFileSizes(FileObject
, &ccfs
);
2596 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2597 Status
= _SEH2_GetExceptionCode();
2600 if (!NT_SUCCESS(Status
))
2601 ERR("CcSetFileSizes threw exception %08x\n", Status
);
2603 fcb
->Header
.AllocationSize
= ccfs
.AllocationSize
;
2606 ExReleaseResourceLite(&Vcb
->tree_lock
);
2611 _Dispatch_type_(IRP_MJ_SET_INFORMATION
)
2612 _Function_class_(DRIVER_DISPATCH
)
2613 NTSTATUS NTAPI
drv_set_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2615 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2616 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2617 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
2618 ccb
* ccb
= IrpSp
->FileObject
->FsContext2
;
2621 FsRtlEnterFileSystem();
2623 top_level
= is_top_level(Irp
);
2625 Irp
->IoStatus
.Information
= 0;
2627 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
2628 Status
= vol_set_information(DeviceObject
, Irp
);
2630 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
2631 Status
= STATUS_INVALID_PARAMETER
;
2635 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
2636 Status
= STATUS_ACCESS_DENIED
;
2640 if (Vcb
->readonly
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
) {
2641 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2647 Status
= STATUS_INVALID_PARAMETER
;
2653 Status
= STATUS_INVALID_PARAMETER
;
2657 if (fcb
!= Vcb
->dummy_fcb
&& is_subvol_readonly(fcb
->subvol
, Irp
) && IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FilePositionInformation
&&
2658 (fcb
->inode
!= SUBVOL_ROOT_INODE
|| (IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileBasicInformation
&& IrpSp
->Parameters
.SetFile
.FileInformationClass
!= FileRenameInformation
))) {
2659 Status
= STATUS_ACCESS_DENIED
;
2663 Status
= STATUS_NOT_IMPLEMENTED
;
2665 TRACE("set information\n");
2667 switch (IrpSp
->Parameters
.SetFile
.FileInformationClass
) {
2668 case FileAllocationInformation
:
2670 TRACE("FileAllocationInformation\n");
2672 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_DATA
)) {
2673 WARN("insufficient privileges\n");
2674 Status
= STATUS_ACCESS_DENIED
;
2678 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, FALSE
, TRUE
);
2682 case FileBasicInformation
:
2684 TRACE("FileBasicInformation\n");
2686 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_ATTRIBUTES
)) {
2687 WARN("insufficient privileges\n");
2688 Status
= STATUS_ACCESS_DENIED
;
2692 Status
= set_basic_information(Vcb
, Irp
, IrpSp
->FileObject
);
2697 case FileDispositionInformation
:
2699 TRACE("FileDispositionInformation\n");
2701 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& DELETE
)) {
2702 WARN("insufficient privileges\n");
2703 Status
= STATUS_ACCESS_DENIED
;
2707 Status
= set_disposition_information(Vcb
, Irp
, IrpSp
->FileObject
);
2712 case FileEndOfFileInformation
:
2714 TRACE("FileEndOfFileInformation\n");
2716 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2717 WARN("insufficient privileges\n");
2718 Status
= STATUS_ACCESS_DENIED
;
2722 Status
= set_end_of_file_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.AdvanceOnly
, FALSE
);
2727 case FileLinkInformation
:
2728 TRACE("FileLinkInformation\n");
2729 Status
= set_link_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2732 case FilePositionInformation
:
2733 TRACE("FilePositionInformation\n");
2734 Status
= set_position_information(IrpSp
->FileObject
, Irp
);
2737 case FileRenameInformation
:
2738 TRACE("FileRenameInformation\n");
2739 // FIXME - make this work with streams
2740 Status
= set_rename_information(Vcb
, Irp
, IrpSp
->FileObject
, IrpSp
->Parameters
.SetFile
.FileObject
);
2743 case FileValidDataLengthInformation
:
2745 TRACE("FileValidDataLengthInformation\n");
2747 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
2748 WARN("insufficient privileges\n");
2749 Status
= STATUS_ACCESS_DENIED
;
2753 Status
= set_valid_data_length_information(Vcb
, Irp
, IrpSp
->FileObject
);
2759 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.SetFile
.FileInformationClass
);
2763 Irp
->IoStatus
.Status
= Status
;
2765 TRACE("returning %08x\n", Status
);
2767 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2770 IoSetTopLevelIrp(NULL
);
2772 FsRtlExitFileSystem();
2777 static NTSTATUS
fill_in_file_basic_information(FILE_BASIC_INFORMATION
* fbi
, INODE_ITEM
* ii
, LONG
* length
, fcb
* fcb
, file_ref
* fileref
) {
2778 RtlZeroMemory(fbi
, sizeof(FILE_BASIC_INFORMATION
));
2780 *length
-= sizeof(FILE_BASIC_INFORMATION
);
2782 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2785 KeQuerySystemTime(&time
);
2786 fbi
->CreationTime
= fbi
->LastAccessTime
= fbi
->LastWriteTime
= fbi
->ChangeTime
= time
;
2788 fbi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2789 fbi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2790 fbi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2791 fbi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2795 if (!fileref
|| !fileref
->parent
) {
2796 ERR("no fileref for stream\n");
2797 return STATUS_INTERNAL_ERROR
;
2799 fbi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2801 fbi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2803 return STATUS_SUCCESS
;
2806 static NTSTATUS
fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION
* fnoi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2809 if (*length
< (LONG
)sizeof(FILE_NETWORK_OPEN_INFORMATION
)) {
2811 return STATUS_BUFFER_OVERFLOW
;
2814 RtlZeroMemory(fnoi
, sizeof(FILE_NETWORK_OPEN_INFORMATION
));
2816 *length
-= sizeof(FILE_NETWORK_OPEN_INFORMATION
);
2819 if (!fileref
|| !fileref
->parent
) {
2820 ERR("no fileref for stream\n");
2821 return STATUS_INTERNAL_ERROR
;
2824 ii
= &fileref
->parent
->fcb
->inode_item
;
2826 ii
= &fcb
->inode_item
;
2828 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
2831 KeQuerySystemTime(&time
);
2832 fnoi
->CreationTime
= fnoi
->LastAccessTime
= fnoi
->LastWriteTime
= fnoi
->ChangeTime
= time
;
2834 fnoi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
2835 fnoi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
2836 fnoi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
2837 fnoi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
2841 fnoi
->AllocationSize
.QuadPart
= fnoi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2842 fnoi
->FileAttributes
= fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fileref
->parent
->fcb
->atts
;
2844 fnoi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2845 fnoi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2846 fnoi
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
2849 return STATUS_SUCCESS
;
2852 static NTSTATUS
fill_in_file_standard_information(FILE_STANDARD_INFORMATION
* fsi
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
2853 RtlZeroMemory(fsi
, sizeof(FILE_STANDARD_INFORMATION
));
2855 *length
-= sizeof(FILE_STANDARD_INFORMATION
);
2858 if (!fileref
|| !fileref
->parent
) {
2859 ERR("no fileref for stream\n");
2860 return STATUS_INTERNAL_ERROR
;
2863 fsi
->AllocationSize
.QuadPart
= fsi
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
2864 fsi
->NumberOfLinks
= fileref
->parent
->fcb
->inode_item
.st_nlink
;
2865 fsi
->Directory
= FALSE
;
2867 fsi
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
2868 fsi
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
2869 fsi
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
2870 fsi
->Directory
= S_ISDIR(fcb
->inode_item
.st_mode
);
2873 TRACE("length = %llu\n", fsi
->EndOfFile
.QuadPart
);
2875 fsi
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
2877 return STATUS_SUCCESS
;
2880 static NTSTATUS
fill_in_file_internal_information(FILE_INTERNAL_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
2881 *length
-= sizeof(FILE_INTERNAL_INFORMATION
);
2883 fii
->IndexNumber
.QuadPart
= make_file_id(fcb
->subvol
, fcb
->inode
);
2885 return STATUS_SUCCESS
;
2888 static NTSTATUS
fill_in_file_ea_information(FILE_EA_INFORMATION
* eai
, fcb
* fcb
, LONG
* length
) {
2889 *length
-= sizeof(FILE_EA_INFORMATION
);
2891 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2892 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2893 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2896 eai
->EaSize
= fcb
->ealen
;
2898 return STATUS_SUCCESS
;
2901 static NTSTATUS
fill_in_file_position_information(FILE_POSITION_INFORMATION
* fpi
, PFILE_OBJECT FileObject
, LONG
* length
) {
2902 RtlZeroMemory(fpi
, sizeof(FILE_POSITION_INFORMATION
));
2904 *length
-= sizeof(FILE_POSITION_INFORMATION
);
2906 fpi
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
2908 return STATUS_SUCCESS
;
2911 NTSTATUS
fileref_get_filename(file_ref
* fileref
, PUNICODE_STRING fn
, USHORT
* name_offset
, ULONG
* preqlen
) {
2916 BOOL overflow
= FALSE
;
2918 // FIXME - we need a lock on filerefs' filepart
2920 if (fileref
== fileref
->fcb
->Vcb
->root_fileref
) {
2921 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2922 fn
->Buffer
[0] = '\\';
2923 fn
->Length
= sizeof(WCHAR
);
2928 return STATUS_SUCCESS
;
2931 *preqlen
= sizeof(WCHAR
);
2934 return STATUS_BUFFER_OVERFLOW
;
2941 while (fr
->parent
) {
2945 return STATUS_INTERNAL_ERROR
;
2948 if (fr
->dc
->name
.Length
+ sizeof(WCHAR
) + fn
->Length
> fn
->MaximumLength
)
2953 movelen
= fn
->MaximumLength
- fr
->dc
->name
.Length
- sizeof(WCHAR
);
2955 movelen
= fn
->Length
;
2957 if ((!overflow
|| fn
->MaximumLength
> fr
->dc
->name
.Length
+ sizeof(WCHAR
)) && movelen
> 0) {
2958 RtlMoveMemory(&fn
->Buffer
[(fr
->dc
->name
.Length
/ sizeof(WCHAR
)) + 1], fn
->Buffer
, movelen
);
2959 offset
+= fr
->dc
->name
.Length
+ sizeof(WCHAR
);
2962 if (fn
->MaximumLength
>= sizeof(WCHAR
)) {
2963 fn
->Buffer
[0] = fr
->fcb
->ads
? ':' : '\\';
2964 fn
->Length
+= sizeof(WCHAR
);
2966 if (fn
->MaximumLength
> sizeof(WCHAR
)) {
2967 RtlCopyMemory(&fn
->Buffer
[1], fr
->dc
->name
.Buffer
, min(fr
->dc
->name
.Length
, fn
->MaximumLength
- sizeof(WCHAR
)));
2968 fn
->Length
+= fr
->dc
->name
.Length
;
2971 if (fn
->Length
> fn
->MaximumLength
) {
2972 fn
->Length
= fn
->MaximumLength
;
2977 reqlen
+= sizeof(WCHAR
) + fr
->dc
->name
.Length
;
2982 offset
+= sizeof(WCHAR
);
2987 Status
= STATUS_BUFFER_OVERFLOW
;
2990 *name_offset
= offset
;
2992 Status
= STATUS_SUCCESS
;
2998 static NTSTATUS
fill_in_file_name_information(FILE_NAME_INFORMATION
* fni
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3002 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
3003 UINT16 datasuflen
= sizeof(datasuf
) - sizeof(WCHAR
);
3006 ERR("called without fileref\n");
3007 return STATUS_INVALID_PARAMETER
;
3010 *length
-= (LONG
)offsetof(FILE_NAME_INFORMATION
, FileName
[0]);
3012 TRACE("maximum length is %u\n", *length
);
3013 fni
->FileNameLength
= 0;
3015 fni
->FileName
[0] = 0;
3017 fn
.Buffer
= fni
->FileName
;
3019 fn
.MaximumLength
= (UINT16
)*length
;
3021 Status
= fileref_get_filename(fileref
, &fn
, NULL
, &reqlen
);
3022 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
3023 ERR("fileref_get_filename returned %08x\n", Status
);
3028 if (Status
== STATUS_BUFFER_OVERFLOW
)
3029 reqlen
+= datasuflen
;
3031 if (fn
.Length
+ datasuflen
> fn
.MaximumLength
) {
3032 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, fn
.MaximumLength
- fn
.Length
);
3033 reqlen
+= datasuflen
;
3034 Status
= STATUS_BUFFER_OVERFLOW
;
3036 RtlCopyMemory(&fn
.Buffer
[fn
.Length
/ sizeof(WCHAR
)], datasuf
, datasuflen
);
3037 fn
.Length
+= datasuflen
;
3042 if (Status
== STATUS_BUFFER_OVERFLOW
) {
3044 fni
->FileNameLength
= reqlen
;
3045 TRACE("%.*S (truncated)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3047 *length
-= fn
.Length
;
3048 fni
->FileNameLength
= fn
.Length
;
3049 TRACE("%.*S\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3055 static NTSTATUS
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION
* ati
, fcb
* fcb
, ccb
* ccb
, LONG
* length
) {
3056 *length
-= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
);
3059 if (!ccb
->fileref
|| !ccb
->fileref
->parent
) {
3060 ERR("no fileref for stream\n");
3061 return STATUS_INTERNAL_ERROR
;
3064 ati
->FileAttributes
= ccb
->fileref
->parent
->fcb
->atts
;
3066 ati
->FileAttributes
= fcb
->atts
;
3068 if (!(ati
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3069 ati
->ReparseTag
= 0;
3071 ati
->ReparseTag
= get_reparse_tag_fcb(fcb
);
3073 return STATUS_SUCCESS
;
3076 static NTSTATUS
fill_in_file_stream_information(FILE_STREAM_INFORMATION
* fsi
, file_ref
* fileref
, LONG
* length
) {
3079 FILE_STREAM_INFORMATION
*entry
, *lastentry
;
3082 static const WCHAR datasuf
[] = L
":$DATA";
3086 ERR("fileref was NULL\n");
3087 return STATUS_INVALID_PARAMETER
;
3090 suf
.Buffer
= (WCHAR
*)datasuf
;
3091 suf
.Length
= suf
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
3093 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
3094 reqsize
= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
);
3098 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
3100 le
= fileref
->fcb
->dir_children_index
.Flink
;
3101 while (le
!= &fileref
->fcb
->dir_children_index
) {
3102 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3104 if (dc
->index
== 0) {
3105 reqsize
= (ULONG
)sector_align(reqsize
, sizeof(LONGLONG
));
3106 reqsize
+= sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
;
3113 TRACE("length = %i, reqsize = %u\n", *length
, reqsize
);
3115 if (reqsize
> *length
) {
3116 Status
= STATUS_BUFFER_OVERFLOW
;
3123 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
3126 entry
->NextEntryOffset
= 0;
3127 entry
->StreamNameLength
= suf
.Length
+ sizeof(WCHAR
);
3128 entry
->StreamSize
.QuadPart
= fileref
->fcb
->inode_item
.st_size
;
3129 entry
->StreamAllocationSize
.QuadPart
= fcb_alloc_size(fileref
->fcb
);
3131 entry
->StreamName
[0] = ':';
3132 RtlCopyMemory(&entry
->StreamName
[1], suf
.Buffer
, suf
.Length
);
3134 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
), sizeof(LONGLONG
));
3137 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3140 le
= fileref
->fcb
->dir_children_index
.Flink
;
3141 while (le
!= &fileref
->fcb
->dir_children_index
) {
3142 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3144 if (dc
->index
== 0) {
3147 entry
->NextEntryOffset
= 0;
3148 entry
->StreamNameLength
= dc
->name
.Length
+ suf
.Length
+ sizeof(WCHAR
);
3151 entry
->StreamSize
.QuadPart
= dc
->fileref
->fcb
->adsdata
.Length
;
3153 entry
->StreamSize
.QuadPart
= dc
->size
;
3155 entry
->StreamAllocationSize
.QuadPart
= entry
->StreamSize
.QuadPart
;
3157 entry
->StreamName
[0] = ':';
3159 RtlCopyMemory(&entry
->StreamName
[1], dc
->name
.Buffer
, dc
->name
.Length
);
3160 RtlCopyMemory(&entry
->StreamName
[1 + (dc
->name
.Length
/ sizeof(WCHAR
))], suf
.Buffer
, suf
.Length
);
3163 lastentry
->NextEntryOffset
= (UINT32
)((UINT8
*)entry
- (UINT8
*)lastentry
);
3165 off
= (ULONG
)sector_align(sizeof(FILE_STREAM_INFORMATION
) - sizeof(WCHAR
) + suf
.Length
+ sizeof(WCHAR
) + dc
->name
.Length
, sizeof(LONGLONG
));
3168 entry
= (FILE_STREAM_INFORMATION
*)((UINT8
*)entry
+ off
);
3177 Status
= STATUS_SUCCESS
;
3180 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
3186 static NTSTATUS
fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION
* fsli
, fcb
* fcb
, file_ref
* fileref
, LONG
* length
) {
3187 TRACE("FileStandardLinkInformation\n");
3189 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3191 fsli
->NumberOfAccessibleLinks
= fcb
->inode_item
.st_nlink
;
3192 fsli
->TotalNumberOfLinks
= fcb
->inode_item
.st_nlink
;
3193 fsli
->DeletePending
= fileref
? fileref
->delete_on_close
: FALSE
;
3194 fsli
->Directory
= (!fcb
->ads
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
) ? TRUE
: FALSE
;
3196 *length
-= sizeof(FILE_STANDARD_LINK_INFORMATION
);
3198 return STATUS_SUCCESS
;
3201 static NTSTATUS
fill_in_hard_link_information(FILE_LINKS_INFORMATION
* fli
, file_ref
* fileref
, PIRP Irp
, LONG
* length
) {
3205 FILE_LINK_ENTRY_INFORMATION
* feli
;
3206 BOOL overflow
= FALSE
;
3207 fcb
* fcb
= fileref
->fcb
;
3211 return STATUS_INVALID_PARAMETER
;
3213 if (*length
< (LONG
)offsetof(FILE_LINKS_INFORMATION
, Entry
))
3214 return STATUS_INVALID_PARAMETER
;
3216 RtlZeroMemory(fli
, *length
);
3218 bytes_needed
= offsetof(FILE_LINKS_INFORMATION
, Entry
);
3222 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3224 if (fcb
->inode
== SUBVOL_ROOT_INODE
) {
3227 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
)
3228 namelen
= sizeof(WCHAR
);
3230 namelen
= fileref
->dc
->name
.Length
;
3232 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) - sizeof(WCHAR
) + namelen
;
3234 if (bytes_needed
> *length
)
3240 feli
->NextEntryOffset
= 0;
3241 feli
->ParentFileId
= 0; // we use an inode of 0 to mean the parent of a subvolume
3243 if (fcb
== fcb
->Vcb
->root_fileref
->fcb
) {
3244 feli
->FileNameLength
= 1;
3245 feli
->FileName
[0] = '.';
3247 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3248 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3251 fli
->EntriesReturned
++;
3256 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fileref_lock
, TRUE
);
3258 if (IsListEmpty(&fcb
->hardlinks
)) {
3259 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fileref
->dc
->name
.Length
- sizeof(WCHAR
);
3261 if (bytes_needed
> *length
)
3267 feli
->NextEntryOffset
= 0;
3268 feli
->ParentFileId
= fileref
->parent
->fcb
->inode
;
3269 feli
->FileNameLength
= fileref
->dc
->name
.Length
/ sizeof(WCHAR
);
3270 RtlCopyMemory(feli
->FileName
, fileref
->dc
->name
.Buffer
, fileref
->dc
->name
.Length
);
3272 fli
->EntriesReturned
++;
3277 le
= fcb
->hardlinks
.Flink
;
3278 while (le
!= &fcb
->hardlinks
) {
3279 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
3282 TRACE("parent %llx, index %llx, name %.*S\n", hl
->parent
, hl
->index
, hl
->name
.Length
/ sizeof(WCHAR
), hl
->name
.Buffer
);
3284 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, Irp
);
3286 if (!NT_SUCCESS(Status
)) {
3287 ERR("open_fileref_by_inode returned %08x\n", Status
);
3288 } else if (!parfr
->deleted
) {
3290 BOOL found
= FALSE
, deleted
= FALSE
;
3291 UNICODE_STRING
* fn
= NULL
;
3293 le2
= parfr
->children
.Flink
;
3294 while (le2
!= &parfr
->children
) {
3295 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
3297 if (fr2
->dc
->index
== hl
->index
) {
3299 deleted
= fr2
->deleted
;
3302 fn
= &fr2
->dc
->name
;
3314 TRACE("fn = %.*S (found = %u)\n", fn
->Length
/ sizeof(WCHAR
), fn
->Buffer
, found
);
3317 bytes_needed
= (LONG
)sector_align(bytes_needed
, 8);
3319 bytes_needed
+= sizeof(FILE_LINK_ENTRY_INFORMATION
) + fn
->Length
- sizeof(WCHAR
);
3321 if (bytes_needed
> *length
)
3326 feli
->NextEntryOffset
= (ULONG
)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION
) + ((feli
->FileNameLength
- 1) * sizeof(WCHAR
)), 8);
3327 feli
= (FILE_LINK_ENTRY_INFORMATION
*)((UINT8
*)feli
+ feli
->NextEntryOffset
);
3331 feli
->NextEntryOffset
= 0;
3332 feli
->ParentFileId
= parfr
->fcb
->inode
;
3333 feli
->FileNameLength
= fn
->Length
/ sizeof(WCHAR
);
3334 RtlCopyMemory(feli
->FileName
, fn
->Buffer
, fn
->Length
);
3336 fli
->EntriesReturned
++;
3342 free_fileref(parfr
);
3349 ExReleaseResourceLite(&fcb
->Vcb
->fileref_lock
);
3352 fli
->BytesNeeded
= bytes_needed
;
3356 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
3358 ExReleaseResourceLite(fcb
->Header
.Resource
);
3362 #endif /* __REACTOS__ */
3364 #if (NTDDI_VERSION >= NTDDI_WIN10)
3366 typedef struct _FILE_ID_128
{
3367 UCHAR Identifier
[16];
3368 } FILE_ID_128
, *PFILE_ID_128
;
3370 typedef struct _FILE_ID_INFORMATION
{
3371 ULONGLONG VolumeSerialNumber
;
3373 } FILE_ID_INFORMATION
, *PFILE_ID_INFORMATION
;
3376 static NTSTATUS
fill_in_file_id_information(FILE_ID_INFORMATION
* fii
, fcb
* fcb
, LONG
* length
) {
3377 RtlCopyMemory(&fii
->VolumeSerialNumber
, &fcb
->Vcb
->superblock
.uuid
.uuid
[8], sizeof(UINT64
));
3378 RtlCopyMemory(&fii
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
3379 RtlCopyMemory(&fii
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
3381 *length
-= sizeof(FILE_ID_INFORMATION
);
3383 return STATUS_SUCCESS
;
3388 static NTSTATUS
fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION
* fsli
, fcb
* fcb
, ccb
* ccb
, LONG
* length
) {
3391 fsli
->FileId
.LowPart
= (UINT32
)fcb
->inode
;
3392 fsli
->FileId
.HighPart
= (UINT32
)fcb
->subvol
->id
;
3395 ii
= &ccb
->fileref
->parent
->fcb
->inode_item
;
3397 ii
= &fcb
->inode_item
;
3399 if (fcb
== fcb
->Vcb
->dummy_fcb
) {
3402 KeQuerySystemTime(&time
);
3403 fsli
->CreationTime
= fsli
->LastAccessTime
= fsli
->LastWriteTime
= fsli
->ChangeTime
= time
;
3405 fsli
->CreationTime
.QuadPart
= unix_time_to_win(&ii
->otime
);
3406 fsli
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
->st_atime
);
3407 fsli
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
->st_mtime
);
3408 fsli
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
->st_ctime
);
3412 fsli
->AllocationSize
.QuadPart
= fsli
->EndOfFile
.QuadPart
= fcb
->adsdata
.Length
;
3413 fsli
->FileAttributes
= ccb
->fileref
->parent
->fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: ccb
->fileref
->parent
->fcb
->atts
;
3415 fsli
->AllocationSize
.QuadPart
= fcb_alloc_size(fcb
);
3416 fsli
->EndOfFile
.QuadPart
= S_ISDIR(fcb
->inode_item
.st_mode
) ? 0 : fcb
->inode_item
.st_size
;
3417 fsli
->FileAttributes
= fcb
->atts
== 0 ? FILE_ATTRIBUTE_NORMAL
: fcb
->atts
;
3420 if (fcb
->type
== BTRFS_TYPE_SOCKET
)
3421 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_SOCKET
;
3422 else if (fcb
->type
== BTRFS_TYPE_FIFO
)
3423 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_FIFO
;
3424 else if (fcb
->type
== BTRFS_TYPE_CHARDEV
)
3425 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_CHARDEV
;
3426 else if (fcb
->type
== BTRFS_TYPE_BLOCKDEV
)
3427 fsli
->ReparseTag
= IO_REPARSE_TAG_LXSS_BLOCKDEV
;
3428 else if (!(fsli
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
3429 fsli
->ReparseTag
= 0;
3431 fsli
->ReparseTag
= get_reparse_tag_fcb(fcb
);
3433 if (fcb
->type
== BTRFS_TYPE_SOCKET
|| fcb
->type
== BTRFS_TYPE_FIFO
|| fcb
->type
== BTRFS_TYPE_CHARDEV
|| fcb
->type
== BTRFS_TYPE_BLOCKDEV
)
3434 fsli
->FileAttributes
|= FILE_ATTRIBUTE_REPARSE_POINT
;
3437 fsli
->NumberOfLinks
= ccb
->fileref
->parent
->fcb
->inode_item
.st_nlink
;
3439 fsli
->NumberOfLinks
= fcb
->inode_item
.st_nlink
;
3441 fsli
->EffectiveAccess
= ccb
->access
;
3442 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
3443 fsli
->LxUid
= ii
->st_uid
;
3444 fsli
->LxGid
= ii
->st_gid
;
3445 fsli
->LxMode
= ii
->st_mode
;
3447 if (ii
->st_mode
& __S_IFBLK
|| ii
->st_mode
& __S_IFCHR
) {
3448 fsli
->LxDeviceIdMajor
= (ii
->st_rdev
& 0xFFFFFFFFFFF00000) >> 20;
3449 fsli
->LxDeviceIdMinor
= (ii
->st_rdev
& 0xFFFFF);
3451 fsli
->LxDeviceIdMajor
= 0;
3452 fsli
->LxDeviceIdMinor
= 0;
3455 *length
-= sizeof(FILE_STAT_LX_INFORMATION
);
3457 return STATUS_SUCCESS
;
3461 static NTSTATUS
query_info(device_extension
* Vcb
, PFILE_OBJECT FileObject
, PIRP Irp
) {
3462 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3463 LONG length
= IrpSp
->Parameters
.QueryFile
.Length
;
3464 fcb
* fcb
= FileObject
->FsContext
;
3465 ccb
* ccb
= FileObject
->FsContext2
;
3466 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
3469 TRACE("(%p, %p, %p)\n", Vcb
, FileObject
, Irp
);
3470 TRACE("fcb = %p\n", fcb
);
3472 if (fcb
== Vcb
->volume_fcb
)
3473 return STATUS_INVALID_PARAMETER
;
3476 ERR("ccb is NULL\n");
3477 return STATUS_INVALID_PARAMETER
;
3480 switch (IrpSp
->Parameters
.QueryFile
.FileInformationClass
) {
3481 case FileAllInformation
:
3483 FILE_ALL_INFORMATION
* fai
= Irp
->AssociatedIrp
.SystemBuffer
;
3486 TRACE("FileAllInformation\n");
3488 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3489 WARN("insufficient privileges\n");
3490 Status
= STATUS_ACCESS_DENIED
;
3495 if (!fileref
|| !fileref
->parent
) {
3496 ERR("no fileref for stream\n");
3497 Status
= STATUS_INTERNAL_ERROR
;
3501 ii
= &fileref
->parent
->fcb
->inode_item
;
3503 ii
= &fcb
->inode_item
;
3505 // Access, mode, and alignment are all filled in by the kernel
3508 fill_in_file_basic_information(&fai
->BasicInformation
, ii
, &length
, fcb
, fileref
);
3511 fill_in_file_standard_information(&fai
->StandardInformation
, fcb
, fileref
, &length
);
3514 fill_in_file_internal_information(&fai
->InternalInformation
, fcb
, &length
);
3517 fill_in_file_ea_information(&fai
->EaInformation
, fcb
, &length
);
3519 length
-= sizeof(FILE_ACCESS_INFORMATION
);
3522 fill_in_file_position_information(&fai
->PositionInformation
, FileObject
, &length
);
3524 length
-= sizeof(FILE_MODE_INFORMATION
);
3526 length
-= sizeof(FILE_ALIGNMENT_INFORMATION
);
3529 fill_in_file_name_information(&fai
->NameInformation
, fcb
, fileref
, &length
);
3531 Status
= STATUS_SUCCESS
;
3536 case FileAttributeTagInformation
:
3538 FILE_ATTRIBUTE_TAG_INFORMATION
* ati
= Irp
->AssociatedIrp
.SystemBuffer
;
3540 TRACE("FileAttributeTagInformation\n");
3542 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3543 WARN("insufficient privileges\n");
3544 Status
= STATUS_ACCESS_DENIED
;
3548 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3549 Status
= fill_in_file_attribute_information(ati
, fcb
, ccb
, &length
);
3550 ExReleaseResourceLite(&Vcb
->tree_lock
);
3555 case FileBasicInformation
:
3557 FILE_BASIC_INFORMATION
* fbi
= Irp
->AssociatedIrp
.SystemBuffer
;
3560 TRACE("FileBasicInformation\n");
3562 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3563 WARN("insufficient privileges\n");
3564 Status
= STATUS_ACCESS_DENIED
;
3568 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_BASIC_INFORMATION
)) {
3570 Status
= STATUS_BUFFER_OVERFLOW
;
3575 if (!fileref
|| !fileref
->parent
) {
3576 ERR("no fileref for stream\n");
3577 Status
= STATUS_INTERNAL_ERROR
;
3581 ii
= &fileref
->parent
->fcb
->inode_item
;
3583 ii
= &fcb
->inode_item
;
3585 Status
= fill_in_file_basic_information(fbi
, ii
, &length
, fcb
, fileref
);
3589 case FileCompressionInformation
:
3590 FIXME("STUB: FileCompressionInformation\n");
3591 Status
= STATUS_INVALID_PARAMETER
;
3594 case FileEaInformation
:
3596 FILE_EA_INFORMATION
* eai
= Irp
->AssociatedIrp
.SystemBuffer
;
3598 TRACE("FileEaInformation\n");
3600 Status
= fill_in_file_ea_information(eai
, fcb
, &length
);
3605 case FileInternalInformation
:
3607 FILE_INTERNAL_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
3609 TRACE("FileInternalInformation\n");
3611 Status
= fill_in_file_internal_information(fii
, fcb
, &length
);
3616 case FileNameInformation
:
3618 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3620 TRACE("FileNameInformation\n");
3622 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3627 case FileNetworkOpenInformation
:
3629 FILE_NETWORK_OPEN_INFORMATION
* fnoi
= Irp
->AssociatedIrp
.SystemBuffer
;
3631 TRACE("FileNetworkOpenInformation\n");
3633 if (Irp
->RequestorMode
!= KernelMode
&& !(ccb
->access
& (FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
))) {
3634 WARN("insufficient privileges\n");
3635 Status
= STATUS_ACCESS_DENIED
;
3639 Status
= fill_in_file_network_open_information(fnoi
, fcb
, fileref
, &length
);
3644 case FilePositionInformation
:
3646 FILE_POSITION_INFORMATION
* fpi
= Irp
->AssociatedIrp
.SystemBuffer
;
3648 TRACE("FilePositionInformation\n");
3650 Status
= fill_in_file_position_information(fpi
, FileObject
, &length
);
3655 case FileStandardInformation
:
3657 FILE_STANDARD_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3659 TRACE("FileStandardInformation\n");
3661 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STANDARD_INFORMATION
)) {
3663 Status
= STATUS_BUFFER_OVERFLOW
;
3667 Status
= fill_in_file_standard_information(fsi
, fcb
, ccb
->fileref
, &length
);
3672 case FileStreamInformation
:
3674 FILE_STREAM_INFORMATION
* fsi
= Irp
->AssociatedIrp
.SystemBuffer
;
3676 TRACE("FileStreamInformation\n");
3678 Status
= fill_in_file_stream_information(fsi
, fileref
, &length
);
3683 #if (NTDDI_VERSION >= NTDDI_VISTA)
3684 case FileHardLinkInformation
:
3686 FILE_LINKS_INFORMATION
* fli
= Irp
->AssociatedIrp
.SystemBuffer
;
3688 TRACE("FileHardLinkInformation\n");
3690 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3691 Status
= fill_in_hard_link_information(fli
, fileref
, Irp
, &length
);
3692 ExReleaseResourceLite(&Vcb
->tree_lock
);
3697 case FileNormalizedNameInformation
:
3699 FILE_NAME_INFORMATION
* fni
= Irp
->AssociatedIrp
.SystemBuffer
;
3701 TRACE("FileNormalizedNameInformation\n");
3703 Status
= fill_in_file_name_information(fni
, fcb
, fileref
, &length
);
3709 #if (NTDDI_VERSION >= NTDDI_WIN7)
3710 case FileStandardLinkInformation
:
3712 FILE_STANDARD_LINK_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
3714 TRACE("FileStandardLinkInformation\n");
3716 Status
= fill_in_file_standard_link_information(fsli
, fcb
, ccb
->fileref
, &length
);
3721 case FileRemoteProtocolInformation
:
3722 TRACE("FileRemoteProtocolInformation\n");
3723 Status
= STATUS_INVALID_PARAMETER
;
3727 #if (NTDDI_VERSION >= NTDDI_WIN10)
3729 #pragma GCC diagnostic push
3730 #pragma GCC diagnostic ignored "-Wswitch"
3732 case FileIdInformation
:
3734 FILE_ID_INFORMATION
* fii
= Irp
->AssociatedIrp
.SystemBuffer
;
3736 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_ID_INFORMATION
)) {
3738 Status
= STATUS_BUFFER_OVERFLOW
;
3742 TRACE("FileIdInformation\n");
3744 Status
= fill_in_file_id_information(fii
, fcb
, &length
);
3749 case FileStatLxInformation
:
3751 FILE_STAT_LX_INFORMATION
* fsli
= Irp
->AssociatedIrp
.SystemBuffer
;
3753 if (IrpSp
->Parameters
.QueryFile
.Length
< sizeof(FILE_STAT_LX_INFORMATION
)) {
3755 Status
= STATUS_BUFFER_OVERFLOW
;
3759 TRACE("FileStatLxInformation\n");
3761 Status
= fill_in_file_stat_lx_information(fsli
, fcb
, ccb
, &length
);
3766 #pragma GCC diagnostic pop
3771 WARN("unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryFile
.FileInformationClass
);
3772 Status
= STATUS_INVALID_PARAMETER
;
3778 Status
= STATUS_BUFFER_OVERFLOW
;
3781 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- length
;
3784 TRACE("query_info returning %08x\n", Status
);
3789 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION
)
3790 _Function_class_(DRIVER_DISPATCH
)
3791 NTSTATUS NTAPI
drv_query_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3792 PIO_STACK_LOCATION IrpSp
;
3795 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3798 FsRtlEnterFileSystem();
3800 top_level
= is_top_level(Irp
);
3802 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3803 Status
= vol_query_information(DeviceObject
, Irp
);
3805 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3806 Status
= STATUS_INVALID_PARAMETER
;
3810 Irp
->IoStatus
.Information
= 0;
3812 TRACE("query information\n");
3814 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3816 fcb
= IrpSp
->FileObject
->FsContext
;
3817 TRACE("fcb = %p\n", fcb
);
3818 TRACE("fcb->subvol = %p\n", fcb
->subvol
);
3820 Status
= query_info(fcb
->Vcb
, IrpSp
->FileObject
, Irp
);
3823 TRACE("returning %08x\n", Status
);
3825 Irp
->IoStatus
.Status
= Status
;
3827 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
3830 IoSetTopLevelIrp(NULL
);
3832 FsRtlExitFileSystem();
3837 _Dispatch_type_(IRP_MJ_QUERY_EA
)
3838 _Function_class_(DRIVER_DISPATCH
)
3839 NTSTATUS NTAPI
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3842 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3843 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3844 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
3847 FILE_FULL_EA_INFORMATION
* ffei
;
3850 FsRtlEnterFileSystem();
3852 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
3854 top_level
= is_top_level(Irp
);
3856 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3857 Status
= vol_query_ea(DeviceObject
, Irp
);
3859 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3860 Status
= STATUS_INVALID_PARAMETER
;
3864 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
3866 ERR("could not get output buffer\n");
3867 Status
= STATUS_INVALID_PARAMETER
;
3872 ERR("no file object\n");
3873 Status
= STATUS_INVALID_PARAMETER
;
3877 fcb
= FileObject
->FsContext
;
3881 Status
= STATUS_INVALID_PARAMETER
;
3885 ccb
= FileObject
->FsContext2
;
3889 Status
= STATUS_INVALID_PARAMETER
;
3893 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_READ_EA
| FILE_WRITE_EA
))) {
3894 WARN("insufficient privileges\n");
3895 Status
= STATUS_ACCESS_DENIED
;
3900 fcb
= ccb
->fileref
->parent
->fcb
;
3902 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3904 Status
= STATUS_SUCCESS
;
3906 if (fcb
->ea_xattr
.Length
== 0)
3909 if (IrpSp
->Parameters
.QueryEa
.EaList
) {
3910 FILE_FULL_EA_INFORMATION
*ea
, *out
;
3911 FILE_GET_EA_INFORMATION
* in
;
3913 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
3917 s
.Length
= s
.MaximumLength
= in
->EaNameLength
;
3918 s
.Buffer
= in
->EaName
;
3920 RtlUpperString(&s
, &s
);
3922 if (in
->NextEntryOffset
== 0)
3925 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
3928 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
3934 in
= IrpSp
->Parameters
.QueryEa
.EaList
;
3936 if (in
->EaNameLength
== ea
->EaNameLength
&&
3937 RtlCompareMemory(in
->EaName
, ea
->EaName
, in
->EaNameLength
) == in
->EaNameLength
) {
3942 if (in
->NextEntryOffset
== 0)
3945 in
= (FILE_GET_EA_INFORMATION
*)(((UINT8
*)in
) + in
->NextEntryOffset
);
3949 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
3951 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
3952 Status
= STATUS_BUFFER_OVERFLOW
;
3960 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
3961 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
3965 out
->NextEntryOffset
= 0;
3966 out
->Flags
= ea
->Flags
;
3967 out
->EaNameLength
= ea
->EaNameLength
;
3968 out
->EaValueLength
= ea
->EaValueLength
;
3969 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
3971 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
3973 if (IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
3977 if (ea
->NextEntryOffset
== 0)
3980 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
3983 FILE_FULL_EA_INFORMATION
*ea
, *out
;
3986 if (IrpSp
->Flags
& SL_INDEX_SPECIFIED
) {
3987 // The index is 1-based
3988 if (IrpSp
->Parameters
.QueryEa
.EaIndex
== 0) {
3989 Status
= STATUS_NONEXISTENT_EA_ENTRY
;
3992 index
= IrpSp
->Parameters
.QueryEa
.EaIndex
- 1;
3993 } else if (IrpSp
->Flags
& SL_RESTART_SCAN
)
3994 index
= ccb
->ea_index
= 0;
3996 index
= ccb
->ea_index
;
3998 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4003 for (i
= 0; i
< index
; i
++) {
4004 if (ea
->NextEntryOffset
== 0) // last item
4007 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4014 UINT8 padding
= retlen
% 4 > 0 ? (4 - (retlen
% 4)) : 0;
4016 if (offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
> IrpSp
->Parameters
.QueryEa
.Length
- retlen
- padding
) {
4017 Status
= retlen
== 0 ? STATUS_BUFFER_TOO_SMALL
: STATUS_BUFFER_OVERFLOW
;
4024 out
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + out
->EaNameLength
+ 1 + out
->EaValueLength
+ padding
;
4025 out
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)out
) + out
->NextEntryOffset
);
4029 out
->NextEntryOffset
= 0;
4030 out
->Flags
= ea
->Flags
;
4031 out
->EaNameLength
= ea
->EaNameLength
;
4032 out
->EaValueLength
= ea
->EaValueLength
;
4033 RtlCopyMemory(out
->EaName
, ea
->EaName
, ea
->EaNameLength
+ ea
->EaValueLength
+ 1);
4035 retlen
+= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ 1 + ea
->EaValueLength
;
4037 if (!(IrpSp
->Flags
& SL_INDEX_SPECIFIED
))
4040 if (ea
->NextEntryOffset
== 0 || IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
)
4043 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4048 ExReleaseResourceLite(fcb
->Header
.Resource
);
4051 TRACE("returning %08x\n", Status
);
4053 Irp
->IoStatus
.Status
= Status
;
4054 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) || Status
== STATUS_BUFFER_OVERFLOW
? retlen
: 0;
4056 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4059 IoSetTopLevelIrp(NULL
);
4061 FsRtlExitFileSystem();
4066 _Dispatch_type_(IRP_MJ_SET_EA
)
4067 _Function_class_(DRIVER_DISPATCH
)
4068 NTSTATUS NTAPI
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4069 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4072 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4073 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4077 FILE_FULL_EA_INFORMATION
* ffei
;
4081 FILE_FULL_EA_INFORMATION
* ea
;
4086 FsRtlEnterFileSystem();
4088 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
4090 top_level
= is_top_level(Irp
);
4092 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4093 Status
= vol_set_ea(DeviceObject
, Irp
);
4095 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4096 Status
= STATUS_INVALID_PARAMETER
;
4100 if (Vcb
->readonly
) {
4101 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4105 ffei
= map_user_buffer(Irp
, NormalPagePriority
);
4107 ERR("could not get output buffer\n");
4108 Status
= STATUS_INVALID_PARAMETER
;
4112 Status
= IoCheckEaBufferValidity(ffei
, IrpSp
->Parameters
.SetEa
.Length
, &offset
);
4113 if (!NT_SUCCESS(Status
)) {
4114 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
4119 ERR("no file object\n");
4120 Status
= STATUS_INVALID_PARAMETER
;
4124 fcb
= FileObject
->FsContext
;
4128 Status
= STATUS_INVALID_PARAMETER
;
4132 ccb
= FileObject
->FsContext2
;
4136 Status
= STATUS_INVALID_PARAMETER
;
4140 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_WRITE_EA
)) {
4141 WARN("insufficient privileges\n");
4142 Status
= STATUS_ACCESS_DENIED
;
4147 fileref
= ccb
->fileref
->parent
;
4150 fileref
= ccb
->fileref
;
4152 InitializeListHead(&ealist
);
4154 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
4156 if (fcb
->ea_xattr
.Length
> 0) {
4157 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4160 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4162 ERR("out of memory\n");
4163 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4167 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4168 item
->name
.Buffer
= ea
->EaName
;
4170 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4171 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4173 item
->flags
= ea
->Flags
;
4175 InsertTailList(&ealist
, &item
->list_entry
);
4177 if (ea
->NextEntryOffset
== 0)
4180 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4190 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
4191 s
.Buffer
= ea
->EaName
;
4193 RtlUpperString(&s
, &s
);
4196 while (le
!= &ealist
) {
4197 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4199 if (item
->name
.Length
== s
.Length
&&
4200 RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
4201 item
->flags
= ea
->Flags
;
4202 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4203 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4212 item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
4214 ERR("out of memory\n");
4215 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4219 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
4220 item
->name
.Buffer
= ea
->EaName
;
4222 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
4223 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
4225 item
->flags
= ea
->Flags
;
4227 InsertTailList(&ealist
, &item
->list_entry
);
4230 if (ea
->NextEntryOffset
== 0)
4233 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4236 // remove entries with zero-length value
4238 while (le
!= &ealist
) {
4239 LIST_ENTRY
* le2
= le
->Flink
;
4241 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4243 if (item
->value
.Length
== 0) {
4244 RemoveEntryList(&item
->list_entry
);
4251 // handle LXSS values
4253 while (le
!= &ealist
) {
4254 LIST_ENTRY
* le2
= le
->Flink
;
4256 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4258 if (item
->name
.Length
== sizeof(lxuid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxuid
, item
->name
.Length
) == item
->name
.Length
) {
4259 if (item
->value
.Length
< sizeof(UINT32
)) {
4260 ERR("uid value was shorter than expected\n");
4261 Status
= STATUS_INVALID_PARAMETER
;
4265 if (Irp
->RequestorMode
== KernelMode
) {
4266 RtlCopyMemory(&fcb
->inode_item
.st_uid
, item
->value
.Buffer
, sizeof(UINT32
));
4267 fcb
->sd_dirty
= TRUE
;
4268 fcb
->sd_deleted
= FALSE
;
4271 RemoveEntryList(&item
->list_entry
);
4273 } else if (item
->name
.Length
== sizeof(lxgid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxgid
, item
->name
.Length
) == item
->name
.Length
) {
4274 if (item
->value
.Length
< sizeof(UINT32
)) {
4275 ERR("gid value was shorter than expected\n");
4276 Status
= STATUS_INVALID_PARAMETER
;
4280 if (Irp
->RequestorMode
== KernelMode
)
4281 RtlCopyMemory(&fcb
->inode_item
.st_gid
, item
->value
.Buffer
, sizeof(UINT32
));
4283 RemoveEntryList(&item
->list_entry
);
4285 } else if (item
->name
.Length
== sizeof(lxmod
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxmod
, item
->name
.Length
) == item
->name
.Length
) {
4286 if (item
->value
.Length
< sizeof(UINT32
)) {
4287 ERR("mode value was shorter than expected\n");
4288 Status
= STATUS_INVALID_PARAMETER
;
4292 if (Irp
->RequestorMode
== KernelMode
) {
4293 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
;
4296 RtlCopyMemory(&val
, item
->value
.Buffer
, sizeof(UINT32
));
4298 fcb
->inode_item
.st_mode
&= ~allowed
;
4299 fcb
->inode_item
.st_mode
|= val
& allowed
;
4302 RemoveEntryList(&item
->list_entry
);
4309 if (IsListEmpty(&ealist
)) {
4312 if (fcb
->ea_xattr
.Buffer
)
4313 ExFreePool(fcb
->ea_xattr
.Buffer
);
4315 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= 0;
4316 fcb
->ea_xattr
.Buffer
= NULL
;
4322 while (le
!= &ealist
) {
4323 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4326 size
+= 4 - (size
% 4);
4328 size
+= (UINT16
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
4333 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
4335 ERR("out of memory\n");
4336 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4340 oldbuf
= fcb
->ea_xattr
.Buffer
;
4342 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
4343 fcb
->ea_xattr
.Buffer
= buf
;
4349 while (le
!= &ealist
) {
4350 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4353 ea
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
4355 if (ea
->NextEntryOffset
% 4 > 0)
4356 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
4358 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
4360 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
4362 ea
->NextEntryOffset
= 0;
4363 ea
->Flags
= item
->flags
;
4364 ea
->EaNameLength
= (UCHAR
)item
->name
.Length
;
4365 ea
->EaValueLength
= item
->value
.Length
;
4367 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
4368 ea
->EaName
[item
->name
.Length
] = 0;
4369 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
4371 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
4380 fcb
->ea_changed
= TRUE
;
4382 KeQuerySystemTime(&time
);
4383 win_time_to_unix(time
, &now
);
4385 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
4386 fcb
->inode_item
.sequence
++;
4388 if (!ccb
->user_set_change_time
)
4389 fcb
->inode_item
.st_ctime
= now
;
4391 fcb
->inode_item_changed
= TRUE
;
4392 mark_fcb_dirty(fcb
);
4394 send_notification_fileref(fileref
, FILE_NOTIFY_CHANGE_EA
, FILE_ACTION_MODIFIED
, NULL
);
4396 Status
= STATUS_SUCCESS
;
4399 ExReleaseResourceLite(fcb
->Header
.Resource
);
4401 while (!IsListEmpty(&ealist
)) {
4402 le
= RemoveHeadList(&ealist
);
4404 item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
4410 TRACE("returning %08x\n", Status
);
4412 Irp
->IoStatus
.Status
= Status
;
4413 Irp
->IoStatus
.Information
= 0;
4415 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4418 IoSetTopLevelIrp(NULL
);
4420 FsRtlExitFileSystem();