1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
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.
9  *
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.
14  *
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/>. */
17 
18 #include "btrfs_drv.h"
19 
20 #if (NTDDI_VERSION >= NTDDI_WIN10)
21 // not currently in mingw - introduced with Windows 10
22 #ifndef _MSC_VER
23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
24 #define FileHardLinkFullIdInformation (enum _FILE_INFORMATION_CLASS)62
25 #define FileDispositionInformationEx (enum _FILE_INFORMATION_CLASS)64
26 #define FileRenameInformationEx (enum _FILE_INFORMATION_CLASS)65
27 #define FileStatInformation (enum _FILE_INFORMATION_CLASS)68
28 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
29 #define FileCaseSensitiveInformation (enum _FILE_INFORMATION_CLASS)71
30 #define FileLinkInformationEx (enum _FILE_INFORMATION_CLASS)72
31 
32 typedef struct _FILE_ID_INFORMATION {
33     ULONGLONG VolumeSerialNumber;
34     FILE_ID_128 FileId;
35 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
36 
37 typedef struct _FILE_STAT_INFORMATION {
38     LARGE_INTEGER FileId;
39     LARGE_INTEGER CreationTime;
40     LARGE_INTEGER LastAccessTime;
41     LARGE_INTEGER LastWriteTime;
42     LARGE_INTEGER ChangeTime;
43     LARGE_INTEGER AllocationSize;
44     LARGE_INTEGER EndOfFile;
45     ULONG FileAttributes;
46     ULONG ReparseTag;
47     ULONG NumberOfLinks;
48     ACCESS_MASK EffectiveAccess;
49 } FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION;
50 
51 typedef struct _FILE_STAT_LX_INFORMATION {
52     LARGE_INTEGER FileId;
53     LARGE_INTEGER CreationTime;
54     LARGE_INTEGER LastAccessTime;
55     LARGE_INTEGER LastWriteTime;
56     LARGE_INTEGER ChangeTime;
57     LARGE_INTEGER AllocationSize;
58     LARGE_INTEGER EndOfFile;
59     ULONG         FileAttributes;
60     ULONG         ReparseTag;
61     ULONG         NumberOfLinks;
62     ACCESS_MASK   EffectiveAccess;
63     ULONG         LxFlags;
64     ULONG         LxUid;
65     ULONG         LxGid;
66     ULONG         LxMode;
67     ULONG         LxDeviceIdMajor;
68     ULONG         LxDeviceIdMinor;
69 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
70 
71 #define LX_FILE_METADATA_HAS_UID        0x01
72 #define LX_FILE_METADATA_HAS_GID        0x02
73 #define LX_FILE_METADATA_HAS_MODE       0x04
74 #define LX_FILE_METADATA_HAS_DEVICE_ID  0x08
75 #define LX_FILE_CASE_SENSITIVE_DIR      0x10
76 
77 typedef struct _FILE_RENAME_INFORMATION_EX {
78     union {
79         BOOLEAN ReplaceIfExists;
80         ULONG Flags;
81     };
82     HANDLE RootDirectory;
83     ULONG FileNameLength;
84     WCHAR FileName[1];
85 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
86 
87 typedef struct _FILE_DISPOSITION_INFORMATION_EX {
88     ULONG Flags;
89 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
90 
91 typedef struct _FILE_LINK_INFORMATION_EX {
92     union {
93         BOOLEAN ReplaceIfExists;
94         ULONG Flags;
95     };
96     HANDLE RootDirectory;
97     ULONG FileNameLength;
98     WCHAR FileName[1];
99 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
100 
101 typedef struct _FILE_CASE_SENSITIVE_INFORMATION {
102     ULONG Flags;
103 } FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION;
104 
105 typedef struct _FILE_LINK_ENTRY_FULL_ID_INFORMATION {
106     ULONG NextEntryOffset;
107     FILE_ID_128 ParentFileId;
108     ULONG FileNameLength;
109     WCHAR FileName[1];
110 } FILE_LINK_ENTRY_FULL_ID_INFORMATION, *PFILE_LINK_ENTRY_FULL_ID_INFORMATION;
111 
112 typedef struct _FILE_LINKS_FULL_ID_INFORMATION {
113     ULONG BytesNeeded;
114     ULONG EntriesReturned;
115     FILE_LINK_ENTRY_FULL_ID_INFORMATION Entry;
116 } FILE_LINKS_FULL_ID_INFORMATION, *PFILE_LINKS_FULL_ID_INFORMATION;
117 
118 #define FILE_RENAME_REPLACE_IF_EXISTS                       0x001
119 #define FILE_RENAME_POSIX_SEMANTICS                         0x002
120 #define FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE          0x004
121 #define FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
122 #define FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE             0x010
123 #define FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE             0x020
124 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE               0x040
125 #define FILE_RENAME_FORCE_RESIZE_TARGET_SR                  0x080
126 #define FILE_RENAME_FORCE_RESIZE_SOURCE_SR                  0x100
127 
128 #define FILE_DISPOSITION_DELETE                         0x1
129 #define FILE_DISPOSITION_POSIX_SEMANTICS                0x2
130 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK      0x4
131 #define FILE_DISPOSITION_ON_CLOSE                       0x8
132 
133 #define FILE_LINK_REPLACE_IF_EXISTS                       0x001
134 #define FILE_LINK_POSIX_SEMANTICS                         0x002
135 #define FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
136 #define FILE_LINK_NO_INCREASE_AVAILABLE_SPACE             0x010
137 #define FILE_LINK_NO_DECREASE_AVAILABLE_SPACE             0x020
138 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE               0x040
139 #define FILE_LINK_FORCE_RESIZE_TARGET_SR                  0x080
140 #define FILE_LINK_FORCE_RESIZE_SOURCE_SR                  0x100
141 
142 #define FILE_CS_FLAG_CASE_SENSITIVE_DIR                 1
143 
144 #else
145 
146 #define FILE_RENAME_INFORMATION_EX FILE_RENAME_INFORMATION
147 #define FILE_LINK_INFORMATION_EX FILE_LINK_INFORMATION
148 
149 #endif
150 #endif
151 
152 #ifdef __REACTOS__
153 typedef struct _FILE_RENAME_INFORMATION_EX {
154     union {
155         BOOLEAN ReplaceIfExists;
156         ULONG Flags;
157     };
158     HANDLE RootDirectory;
159     ULONG FileNameLength;
160     WCHAR FileName[1];
161 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
162 
163 typedef struct _FILE_DISPOSITION_INFORMATION_EX {
164     ULONG Flags;
165 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
166 
167 typedef struct _FILE_LINK_INFORMATION_EX {
168     union {
169         BOOLEAN ReplaceIfExists;
170         ULONG Flags;
171     };
172     HANDLE RootDirectory;
173     ULONG FileNameLength;
174     WCHAR FileName[1];
175 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
176 
177 #define FILE_RENAME_REPLACE_IF_EXISTS                       0x001
178 #define FILE_RENAME_POSIX_SEMANTICS                         0x002
179 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE               0x040
180 
181 #define FILE_DISPOSITION_DELETE                         0x1
182 #define FILE_DISPOSITION_POSIX_SEMANTICS                0x2
183 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK      0x4
184 
185 #define FILE_LINK_REPLACE_IF_EXISTS                       0x001
186 #define FILE_LINK_POSIX_SEMANTICS                         0x002
187 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE               0x040
188 #endif
189 
190 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
191     FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
192     fcb* fcb = FileObject->FsContext;
193     ccb* ccb = FileObject->FsContext2;
194     file_ref* fileref = ccb ? ccb->fileref : NULL;
195     ULONG defda, filter = 0;
196     bool inode_item_changed = false;
197     NTSTATUS Status;
198 
199     if (fcb->ads) {
200         if (fileref && fileref->parent)
201             fcb = fileref->parent->fcb;
202         else {
203             ERR("stream did not have fileref\n");
204             return STATUS_INTERNAL_ERROR;
205         }
206     }
207 
208     if (!ccb) {
209         ERR("ccb was NULL\n");
210         return STATUS_INVALID_PARAMETER;
211     }
212 
213     TRACE("file = %p, attributes = %x\n", FileObject, fbi->FileAttributes);
214 
215     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
216 
217     if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
218         WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
219         Status = STATUS_INVALID_PARAMETER;
220         goto end;
221     }
222 
223     if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) &&
224         (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
225         Status = STATUS_ACCESS_DENIED;
226         goto end;
227     }
228 
229     // don't allow readonly subvol to be made r/w if send operation running on it
230     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY &&
231         fcb->subvol->send_ops > 0) {
232         Status = STATUS_DEVICE_NOT_READY;
233         goto end;
234     }
235 
236     // times of -2 are some sort of undocumented behaviour to do with LXSS
237 
238     if (fbi->CreationTime.QuadPart == -2)
239         fbi->CreationTime.QuadPart = 0;
240 
241     if (fbi->LastAccessTime.QuadPart == -2)
242         fbi->LastAccessTime.QuadPart = 0;
243 
244     if (fbi->LastWriteTime.QuadPart == -2)
245         fbi->LastWriteTime.QuadPart = 0;
246 
247     if (fbi->ChangeTime.QuadPart == -2)
248         fbi->ChangeTime.QuadPart = 0;
249 
250     if (fbi->CreationTime.QuadPart == -1)
251         ccb->user_set_creation_time = true;
252     else if (fbi->CreationTime.QuadPart != 0) {
253         win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
254         inode_item_changed = true;
255         filter |= FILE_NOTIFY_CHANGE_CREATION;
256 
257         ccb->user_set_creation_time = true;
258     }
259 
260     if (fbi->LastAccessTime.QuadPart == -1)
261         ccb->user_set_access_time = true;
262     else if (fbi->LastAccessTime.QuadPart != 0) {
263         win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
264         inode_item_changed = true;
265         filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
266 
267         ccb->user_set_access_time = true;
268     }
269 
270     if (fbi->LastWriteTime.QuadPart == -1)
271         ccb->user_set_write_time = true;
272     else if (fbi->LastWriteTime.QuadPart != 0) {
273         win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
274         inode_item_changed = true;
275         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
276 
277         ccb->user_set_write_time = true;
278     }
279 
280     if (fbi->ChangeTime.QuadPart == -1)
281         ccb->user_set_change_time = true;
282     else if (fbi->ChangeTime.QuadPart != 0) {
283         win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
284         inode_item_changed = true;
285         // no filter for this
286 
287         ccb->user_set_change_time = true;
288     }
289 
290     // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
291     if (fbi->FileAttributes != 0) {
292         LARGE_INTEGER time;
293         BTRFS_TIME now;
294 
295         fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
296 
297         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] == '.',
298                                     true, Irp);
299 
300         if (fcb->type == BTRFS_TYPE_DIRECTORY)
301             fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
302         else if (fcb->type == BTRFS_TYPE_SYMLINK)
303             fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
304 
305         fcb->atts_changed = true;
306 
307         if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)
308             fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
309 
310         if (defda == fbi->FileAttributes)
311             fcb->atts_deleted = true;
312         else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY))
313             fcb->atts_deleted = true;
314 
315         fcb->atts = fbi->FileAttributes;
316 
317         KeQuerySystemTime(&time);
318         win_time_to_unix(time, &now);
319 
320         if (!ccb->user_set_change_time)
321             fcb->inode_item.st_ctime = now;
322 
323         fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
324         fcb->subvol->root_item.ctime = now;
325 
326         if (fcb->inode == SUBVOL_ROOT_INODE) {
327             if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)
328                 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY;
329             else
330                 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY;
331         }
332 
333         inode_item_changed = true;
334 
335         filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
336     }
337 
338     if (inode_item_changed) {
339         fcb->inode_item.transid = Vcb->superblock.generation;
340         fcb->inode_item.sequence++;
341         fcb->inode_item_changed = true;
342 
343         mark_fcb_dirty(fcb);
344     }
345 
346     if (filter != 0)
347         queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
348 
349     Status = STATUS_SUCCESS;
350 
351 end:
352     ExReleaseResourceLite(fcb->Header.Resource);
353 
354     return Status;
355 }
356 
357 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool ex) {
358     fcb* fcb = FileObject->FsContext;
359     ccb* ccb = FileObject->FsContext2;
360     file_ref* fileref = ccb ? ccb->fileref : NULL;
361     ULONG atts, flags;
362     NTSTATUS Status;
363 
364     if (!fileref)
365         return STATUS_INVALID_PARAMETER;
366 
367     if (ex) {
368         FILE_DISPOSITION_INFORMATION_EX* fdi = Irp->AssociatedIrp.SystemBuffer;
369 
370         flags = fdi->Flags;
371     } else {
372         FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
373 
374         flags = fdi->DeleteFile ? FILE_DISPOSITION_DELETE : 0;
375     }
376 
377     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
378 
379     TRACE("changing delete_on_close to %s for fcb %p\n", flags & FILE_DISPOSITION_DELETE ? "true" : "false", fcb);
380 
381     if (fcb->ads) {
382         if (fileref->parent)
383             atts = fileref->parent->fcb->atts;
384         else {
385             ERR("no fileref for stream\n");
386             Status = STATUS_INTERNAL_ERROR;
387             goto end;
388         }
389     } else
390         atts = fcb->atts;
391 
392     TRACE("atts = %x\n", atts);
393 
394     if (atts & FILE_ATTRIBUTE_READONLY) {
395         TRACE("not allowing readonly file to be deleted\n");
396         Status = STATUS_CANNOT_DELETE;
397         goto end;
398     }
399 
400     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
401         WARN("not allowing \\$Root to be deleted\n");
402         Status = STATUS_ACCESS_DENIED;
403         goto end;
404     }
405 
406     // FIXME - can we skip this bit for subvols?
407     if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
408         TRACE("directory not empty\n");
409         Status = STATUS_DIRECTORY_NOT_EMPTY;
410         goto end;
411     }
412 
413     if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
414         if (!ex || flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) {
415             TRACE("trying to delete file which is being mapped as an image\n");
416             Status = STATUS_CANNOT_DELETE;
417             goto end;
418         }
419     }
420 
421     ccb->fileref->delete_on_close = flags & FILE_DISPOSITION_DELETE;
422 
423     FileObject->DeletePending = flags & FILE_DISPOSITION_DELETE;
424 
425     if (flags & FILE_DISPOSITION_DELETE && flags & FILE_DISPOSITION_POSIX_SEMANTICS)
426         ccb->fileref->posix_delete = true;
427 
428     Status = STATUS_SUCCESS;
429 
430 end:
431     ExReleaseResourceLite(fcb->Header.Resource);
432 
433     // send notification that directory is about to be deleted
434     if (NT_SUCCESS(Status) && flags & FILE_DISPOSITION_DELETE && fcb->type == BTRFS_TYPE_DIRECTORY) {
435         FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
436                                        NULL, false, false, 0, NULL, NULL, NULL);
437     }
438 
439     return Status;
440 }
441 
442 bool has_open_children(file_ref* fileref) {
443     LIST_ENTRY* le = fileref->children.Flink;
444 
445     if (IsListEmpty(&fileref->children))
446         return false;
447 
448     while (le != &fileref->children) {
449         file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
450 
451         if (c->open_count > 0)
452             return true;
453 
454         if (has_open_children(c))
455             return true;
456 
457         le = le->Flink;
458     }
459 
460     return false;
461 }
462 
463 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
464     device_extension* Vcb = oldfcb->Vcb;
465     fcb* fcb;
466     LIST_ENTRY* le;
467 
468     // FIXME - we can skip a lot of this if the inode is about to be deleted
469 
470     fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
471     if (!fcb) {
472         ERR("out of memory\n");
473         return STATUS_INSUFFICIENT_RESOURCES;
474     }
475 
476     fcb->Vcb = Vcb;
477 
478     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
479     fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
480     fcb->Header.FileSize = oldfcb->Header.FileSize;
481     fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength;
482 
483     fcb->type = oldfcb->type;
484 
485     if (oldfcb->ads) {
486         fcb->ads = true;
487         fcb->adshash = oldfcb->adshash;
488         fcb->adsmaxlen = oldfcb->adsmaxlen;
489 
490         if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
491             fcb->adsxattr.Length = oldfcb->adsxattr.Length;
492             fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
493             fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
494 
495             if (!fcb->adsxattr.Buffer) {
496                 ERR("out of memory\n");
497                 free_fcb(fcb);
498                 return STATUS_INSUFFICIENT_RESOURCES;
499             }
500 
501             RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
502             fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
503         }
504 
505         if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
506             fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
507             fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
508 
509             if (!fcb->adsdata.Buffer) {
510                 ERR("out of memory\n");
511                 free_fcb(fcb);
512                 return STATUS_INSUFFICIENT_RESOURCES;
513             }
514 
515             RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
516         }
517 
518         goto end;
519     }
520 
521     RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM));
522     fcb->inode_item_changed = true;
523 
524     if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) {
525         fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
526         if (!fcb->sd) {
527             ERR("out of memory\n");
528             free_fcb(fcb);
529             return STATUS_INSUFFICIENT_RESOURCES;
530         }
531 
532         RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd));
533     }
534 
535     fcb->atts = oldfcb->atts;
536 
537     le = oldfcb->extents.Flink;
538     while (le != &oldfcb->extents) {
539         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
540 
541         if (!ext->ignore) {
542             extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
543 
544             if (!ext2) {
545                 ERR("out of memory\n");
546                 free_fcb(fcb);
547                 return STATUS_INSUFFICIENT_RESOURCES;
548             }
549 
550             ext2->offset = ext->offset;
551             ext2->datalen = ext->datalen;
552 
553             if (ext2->datalen > 0)
554                 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
555 
556             ext2->unique = false;
557             ext2->ignore = false;
558             ext2->inserted = true;
559 
560             if (ext->csum) {
561                 ULONG len;
562                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
563 
564                 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
565                     len = (ULONG)ed2->num_bytes;
566                 else
567                     len = (ULONG)ed2->size;
568 
569                 len = len * sizeof(uint32_t) / Vcb->superblock.sector_size;
570 
571                 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
572                 if (!ext2->csum) {
573                     ERR("out of memory\n");
574                     free_fcb(fcb);
575                     return STATUS_INSUFFICIENT_RESOURCES;
576                 }
577 
578                 RtlCopyMemory(ext2->csum, ext->csum, len);
579             } else
580                 ext2->csum = NULL;
581 
582             InsertTailList(&fcb->extents, &ext2->list_entry);
583         }
584 
585         le = le->Flink;
586     }
587 
588     le = oldfcb->hardlinks.Flink;
589     while (le != &oldfcb->hardlinks) {
590         hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2;
591 
592         hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
593 
594         if (!hl2) {
595             ERR("out of memory\n");
596             free_fcb(fcb);
597             return STATUS_INSUFFICIENT_RESOURCES;
598         }
599 
600         hl2->parent = hl->parent;
601         hl2->index = hl->index;
602 
603         hl2->name.Length = hl2->name.MaximumLength = hl->name.Length;
604         hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG);
605 
606         if (!hl2->name.Buffer) {
607             ERR("out of memory\n");
608             ExFreePool(hl2);
609             free_fcb(fcb);
610             return STATUS_INSUFFICIENT_RESOURCES;
611         }
612 
613         RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length);
614 
615         hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length;
616         hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG);
617 
618         if (!hl2->utf8.Buffer) {
619             ERR("out of memory\n");
620             ExFreePool(hl2->name.Buffer);
621             ExFreePool(hl2);
622             free_fcb(fcb);
623             return STATUS_INSUFFICIENT_RESOURCES;
624         }
625 
626         RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
627 
628         InsertTailList(&fcb->hardlinks, &hl2->list_entry);
629 
630         le = le->Flink;
631     }
632 
633     if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
634         fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
635 
636         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
637         if (!fcb->reparse_xattr.Buffer) {
638             ERR("out of memory\n");
639             free_fcb(fcb);
640             return STATUS_INSUFFICIENT_RESOURCES;
641         }
642 
643         RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
644     }
645 
646     if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) {
647         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length;
648 
649         fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
650         if (!fcb->ea_xattr.Buffer) {
651             ERR("out of memory\n");
652             free_fcb(fcb);
653             return STATUS_INSUFFICIENT_RESOURCES;
654         }
655 
656         RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
657     }
658 
659     fcb->prop_compression = oldfcb->prop_compression;
660 
661     le = oldfcb->xattrs.Flink;
662     while (le != &oldfcb->xattrs) {
663         xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
664 
665         if (xa->valuelen > 0) {
666             xattr* xa2;
667 
668             xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG);
669 
670             if (!xa2) {
671                 ERR("out of memory\n");
672                 free_fcb(fcb);
673                 return STATUS_INSUFFICIENT_RESOURCES;
674             }
675 
676             xa2->namelen = xa->namelen;
677             xa2->valuelen = xa->valuelen;
678             xa2->dirty = xa->dirty;
679             memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen);
680 
681             InsertTailList(&fcb->xattrs, &xa2->list_entry);
682         }
683 
684         le = le->Flink;
685     }
686 
687 end:
688     *pfcb = fcb;
689 
690     return STATUS_SUCCESS;
691 }
692 
693 typedef struct _move_entry {
694     file_ref* fileref;
695     fcb* dummyfcb;
696     file_ref* dummyfileref;
697     struct _move_entry* parent;
698     LIST_ENTRY list_entry;
699 } move_entry;
700 
701 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
702     NTSTATUS Status;
703     LIST_ENTRY* le;
704 
705     ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, true);
706 
707     le = me->fileref->fcb->dir_children_index.Flink;
708 
709     while (le != &me->fileref->fcb->dir_children_index) {
710         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
711         file_ref* fr;
712         move_entry* me2;
713 
714         Status = open_fileref_child(Vcb, me->fileref, &dc->name, true, true, dc->index == 0 ? true : false, PagedPool, &fr, Irp);
715 
716         if (!NT_SUCCESS(Status)) {
717             ERR("open_fileref_child returned %08x\n", Status);
718             ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
719             return Status;
720         }
721 
722         me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
723         if (!me2) {
724             ERR("out of memory\n");
725             ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
726             return STATUS_INSUFFICIENT_RESOURCES;
727         }
728 
729         me2->fileref = fr;
730         me2->dummyfcb = NULL;
731         me2->dummyfileref = NULL;
732         me2->parent = me;
733 
734         InsertHeadList(&me->list_entry, &me2->list_entry);
735 
736         le = le->Flink;
737     }
738 
739     ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
740 
741     return STATUS_SUCCESS;
742 }
743 
744 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
745     uint8_t c;
746 
747     c = dc->hash >> 24;
748 
749     if (fcb->hash_ptrs[c] == &dc->list_entry_hash) {
750         if (dc->list_entry_hash.Flink == &fcb->dir_children_hash)
751             fcb->hash_ptrs[c] = NULL;
752         else {
753             dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash);
754 
755             if (dc2->hash >> 24 == c)
756                 fcb->hash_ptrs[c] = &dc2->list_entry_hash;
757             else
758                 fcb->hash_ptrs[c] = NULL;
759         }
760     }
761 
762     RemoveEntryList(&dc->list_entry_hash);
763 
764     c = dc->hash_uc >> 24;
765 
766     if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) {
767         if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc)
768             fcb->hash_ptrs_uc[c] = NULL;
769         else {
770             dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc);
771 
772             if (dc2->hash_uc >> 24 == c)
773                 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc;
774             else
775                 fcb->hash_ptrs_uc[c] = NULL;
776         }
777     }
778 
779     RemoveEntryList(&dc->list_entry_hash_uc);
780 }
781 
782 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) {
783     NTSTATUS Status;
784     fcb* fcb;
785     SECURITY_SUBJECT_CONTEXT subjcont;
786     PSID owner;
787     BOOLEAN defaulted;
788     LARGE_INTEGER time;
789     BTRFS_TIME now;
790 
791     fcb = create_fcb(Vcb, PagedPool);
792     if (!fcb) {
793         ERR("out of memory\n");
794         return STATUS_INSUFFICIENT_RESOURCES;
795     }
796 
797     KeQuerySystemTime(&time);
798     win_time_to_unix(time, &now);
799 
800     fcb->Vcb = Vcb;
801 
802     fcb->subvol = r;
803     fcb->inode = InterlockedIncrement64(&r->lastinode);
804     fcb->type = BTRFS_TYPE_DIRECTORY;
805 
806     fcb->inode_item.generation = Vcb->superblock.generation;
807     fcb->inode_item.transid = Vcb->superblock.generation;
808     fcb->inode_item.st_nlink = 1;
809     fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, true);
810     fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now;
811     fcb->inode_item.st_gid = GID_NOBODY;
812 
813     fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, false, true, NULL);
814 
815     SeCaptureSubjectContext(&subjcont);
816 
817     Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
818 
819     if (!NT_SUCCESS(Status)) {
820         reap_fcb(fcb);
821         ERR("SeAssignSecurity returned %08x\n", Status);
822         return Status;
823     }
824 
825     if (!fcb->sd) {
826         reap_fcb(fcb);
827         ERR("SeAssignSecurity returned NULL security descriptor\n");
828         return STATUS_INTERNAL_ERROR;
829     }
830 
831     Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
832     if (!NT_SUCCESS(Status)) {
833         ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
834         fcb->inode_item.st_uid = UID_NOBODY;
835         fcb->sd_dirty = true;
836     } else {
837         fcb->inode_item.st_uid = sid_to_uid(owner);
838         fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
839     }
840 
841     find_gid(fcb, parfcb, &subjcont);
842 
843     fcb->inode_item_changed = true;
844 
845     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
846     fcb->Header.AllocationSize.QuadPart = 0;
847     fcb->Header.FileSize.QuadPart = 0;
848     fcb->Header.ValidDataLength.QuadPart = 0;
849 
850     fcb->created = true;
851 
852     if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
853         fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
854 
855     fcb->prop_compression = parfcb->prop_compression;
856     fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
857 
858     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
859     if (!fcb->hash_ptrs) {
860         ERR("out of memory\n");
861         return STATUS_INSUFFICIENT_RESOURCES;
862     }
863 
864     RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
865 
866     fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
867     if (!fcb->hash_ptrs_uc) {
868         ERR("out of memory\n");
869         return STATUS_INSUFFICIENT_RESOURCES;
870     }
871 
872     RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
873 
874     acquire_fcb_lock_exclusive(Vcb);
875     InsertTailList(&r->fcbs, &fcb->list_entry);
876     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
877     r->fcbs_version++;
878     release_fcb_lock(Vcb);
879 
880     mark_fcb_dirty(fcb);
881 
882     *pfcb = fcb;
883 
884     return STATUS_SUCCESS;
885 }
886 
887 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
888     NTSTATUS Status;
889     LIST_ENTRY move_list, *le;
890     move_entry* me;
891     LARGE_INTEGER time;
892     BTRFS_TIME now;
893     file_ref* origparent;
894 
895     // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
896 
897     InitializeListHead(&move_list);
898 
899     KeQuerySystemTime(&time);
900     win_time_to_unix(time, &now);
901 
902     acquire_fcb_lock_exclusive(fileref->fcb->Vcb);
903 
904     me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
905 
906     if (!me) {
907         ERR("out of memory\n");
908         Status = STATUS_INSUFFICIENT_RESOURCES;
909         goto end;
910     }
911 
912     origparent = fileref->parent;
913 
914     me->fileref = fileref;
915     increase_fileref_refcount(me->fileref);
916     me->dummyfcb = NULL;
917     me->dummyfileref = NULL;
918     me->parent = NULL;
919 
920     InsertTailList(&move_list, &me->list_entry);
921 
922     le = move_list.Flink;
923     while (le != &move_list) {
924         me = CONTAINING_RECORD(le, move_entry, list_entry);
925 
926         ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, true);
927 
928         if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
929             Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
930 
931             if (!NT_SUCCESS(Status)) {
932                 ERR("add_children_to_move_list returned %08x\n", Status);
933                 goto end;
934             }
935         }
936 
937         ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
938 
939         le = le->Flink;
940     }
941 
942     send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
943 
944     // loop through list and create new inodes
945 
946     le = move_list.Flink;
947     while (le != &move_list) {
948         me = CONTAINING_RECORD(le, move_entry, list_entry);
949 
950         if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) {
951             if (!me->dummyfcb) {
952                 ULONG defda;
953                 bool inserted = false;
954                 LIST_ENTRY* le3;
955 
956                 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true);
957 
958                 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb);
959                 if (!NT_SUCCESS(Status)) {
960                     ERR("duplicate_fcb returned %08x\n", Status);
961                     ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
962                     goto end;
963                 }
964 
965                 me->dummyfcb->subvol = me->fileref->fcb->subvol;
966                 me->dummyfcb->inode = me->fileref->fcb->inode;
967 
968                 if (!me->dummyfcb->ads) {
969                     me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty;
970                     me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed;
971                     me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted;
972                     me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed;
973                     me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed;
974                     me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed;
975                 }
976 
977                 me->dummyfcb->created = me->fileref->fcb->created;
978                 me->dummyfcb->deleted = me->fileref->fcb->deleted;
979                 mark_fcb_dirty(me->dummyfcb);
980 
981                 if (!me->fileref->fcb->ads) {
982                     LIST_ENTRY* le2;
983 
984                     me->fileref->fcb->subvol = destdir->fcb->subvol;
985                     me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
986                     me->fileref->fcb->inode_item.st_nlink = 1;
987 
988                     defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
989                                                 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
990                                                 true, Irp);
991 
992                     me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
993                     me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
994                     me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents);
995                     me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer;
996                     me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
997                     me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs);
998                     me->fileref->fcb->inode_item_changed = true;
999 
1000                     le2 = me->fileref->fcb->xattrs.Flink;
1001                     while (le2 != &me->fileref->fcb->xattrs) {
1002                         xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry);
1003 
1004                         xa->dirty = true;
1005 
1006                         le2 = le2->Flink;
1007                     }
1008 
1009                     if (le == move_list.Flink) { // first entry
1010                         me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1011                         me->fileref->fcb->inode_item.sequence++;
1012 
1013                         if (!ccb->user_set_change_time)
1014                             me->fileref->fcb->inode_item.st_ctime = now;
1015                     }
1016 
1017                     le2 = me->fileref->fcb->extents.Flink;
1018                     while (le2 != &me->fileref->fcb->extents) {
1019                         extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
1020 
1021                         if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
1022                             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1023 
1024                             if (ed2->size != 0) {
1025                                 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
1026 
1027                                 if (!c) {
1028                                     ERR("get_chunk_from_address(%I64x) failed\n", ed2->address);
1029                                 } else {
1030                                     Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
1031                                                                        ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
1032 
1033                                     if (!NT_SUCCESS(Status)) {
1034                                         ERR("update_changed_extent_ref returned %08x\n", Status);
1035                                         ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1036                                         goto end;
1037                                     }
1038                                 }
1039 
1040                             }
1041                         }
1042 
1043                         le2 = le2->Flink;
1044                     }
1045                 } else {
1046                     me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol;
1047                     me->fileref->fcb->inode = me->parent->fileref->fcb->inode;
1048                 }
1049 
1050                 me->fileref->fcb->created = true;
1051 
1052                 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry);
1053                 RemoveEntryList(&me->fileref->fcb->list_entry);
1054 
1055                 le3 = destdir->fcb->subvol->fcbs.Flink;
1056                 while (le3 != &destdir->fcb->subvol->fcbs) {
1057                     fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry);
1058 
1059                     if (fcb->inode > me->fileref->fcb->inode) {
1060                         InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry);
1061                         inserted = true;
1062                         break;
1063                     }
1064 
1065                     le3 = le3->Flink;
1066                 }
1067 
1068                 if (!inserted)
1069                     InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry);
1070 
1071                 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
1072 
1073                 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
1074                     hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
1075 
1076                     if (hl->name.Buffer)
1077                         ExFreePool(hl->name.Buffer);
1078 
1079                     if (hl->utf8.Buffer)
1080                         ExFreePool(hl->utf8.Buffer);
1081 
1082                     ExFreePool(hl);
1083                 }
1084 
1085                 me->fileref->fcb->inode_item_changed = true;
1086                 mark_fcb_dirty(me->fileref->fcb);
1087 
1088                 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) {
1089                     LIST_ENTRY* le2 = le->Flink;
1090 
1091                     while (le2 != &move_list) {
1092                         move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry);
1093 
1094                         if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) {
1095                             me2->dummyfcb = me->dummyfcb;
1096                             InterlockedIncrement(&me->dummyfcb->refcount);
1097                         }
1098 
1099                         le2 = le2->Flink;
1100                     }
1101                 }
1102 
1103                 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1104             } else {
1105                 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true);
1106                 me->fileref->fcb->inode_item.st_nlink++;
1107                 me->fileref->fcb->inode_item_changed = true;
1108                 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1109             }
1110         }
1111 
1112         le = le->Flink;
1113     }
1114 
1115     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
1116     fileref->fcb->subvol->root_item.ctime = now;
1117 
1118     // loop through list and create new filerefs
1119 
1120     le = move_list.Flink;
1121     while (le != &move_list) {
1122         hardlink* hl;
1123         bool name_changed = false;
1124 
1125         me = CONTAINING_RECORD(le, move_entry, list_entry);
1126 
1127         me->dummyfileref = create_fileref(fileref->fcb->Vcb);
1128         if (!me->dummyfileref) {
1129             ERR("out of memory\n");
1130             Status = STATUS_INSUFFICIENT_RESOURCES;
1131             goto end;
1132         }
1133 
1134         if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) {
1135             root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol;
1136 
1137             Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb);
1138             if (!NT_SUCCESS(Status)) {
1139                 ERR("create_directory_fcb returnd %08x\n", Status);
1140                 goto end;
1141             }
1142 
1143             me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1144             me->fileref->dc->key.obj_type = TYPE_INODE_ITEM;
1145 
1146             me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb;
1147         } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) {
1148             me->dummyfileref->fcb = me->fileref->fcb;
1149 
1150             me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id;
1151         } else
1152             me->dummyfileref->fcb = me->dummyfcb;
1153 
1154         InterlockedIncrement(&me->dummyfileref->fcb->refcount);
1155 
1156         me->dummyfileref->oldutf8 = me->fileref->oldutf8;
1157         me->dummyfileref->oldindex = me->fileref->dc->index;
1158 
1159         if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length))
1160             name_changed = true;
1161 
1162         if ((le == move_list.Flink || me->fileref->fcb->inode == SUBVOL_ROOT_INODE) && !me->dummyfileref->oldutf8.Buffer) {
1163             me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
1164             if (!me->dummyfileref->oldutf8.Buffer) {
1165                 ERR("out of memory\n");
1166                 Status = STATUS_INSUFFICIENT_RESOURCES;
1167                 goto end;
1168             }
1169 
1170             RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1171 
1172             me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
1173         }
1174 
1175         me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
1176         me->dummyfileref->deleted = me->fileref->deleted;
1177 
1178         me->dummyfileref->created = me->fileref->created;
1179         me->fileref->created = true;
1180 
1181         me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
1182         increase_fileref_refcount(me->dummyfileref->parent);
1183 
1184         ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, true);
1185         InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
1186         ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
1187 
1188         if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
1189             me->dummyfileref->fcb->fileref = me->dummyfileref;
1190 
1191         if (!me->parent) {
1192             RemoveEntryList(&me->fileref->list_entry);
1193 
1194             increase_fileref_refcount(destdir);
1195 
1196             if (me->fileref->dc) {
1197                 // remove from old parent
1198                 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1199                 RemoveEntryList(&me->fileref->dc->list_entry_index);
1200                 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1201                 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1202 
1203                 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
1204                 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1205                 me->fileref->parent->fcb->inode_item.sequence++;
1206                 me->fileref->parent->fcb->inode_item.st_ctime = now;
1207                 me->fileref->parent->fcb->inode_item.st_mtime = now;
1208                 me->fileref->parent->fcb->inode_item_changed = true;
1209                 mark_fcb_dirty(me->fileref->parent->fcb);
1210 
1211                 if (name_changed) {
1212                     ExFreePool(me->fileref->dc->utf8.Buffer);
1213                     ExFreePool(me->fileref->dc->name.Buffer);
1214                     ExFreePool(me->fileref->dc->name_uc.Buffer);
1215 
1216                     me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1217                     if (!me->fileref->dc->utf8.Buffer) {
1218                         ERR("out of memory\n");
1219                         Status = STATUS_INSUFFICIENT_RESOURCES;
1220                         goto end;
1221                     }
1222 
1223                     me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
1224                     RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1225 
1226                     me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
1227                     if (!me->fileref->dc->name.Buffer) {
1228                         ERR("out of memory\n");
1229                         Status = STATUS_INSUFFICIENT_RESOURCES;
1230                         goto end;
1231                     }
1232 
1233                     me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
1234                     RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
1235 
1236                     Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
1237                     if (!NT_SUCCESS(Status)) {
1238                         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1239                         goto end;
1240                     }
1241 
1242                     me->fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1243                     me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
1244                 }
1245 
1246                 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
1247                     me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1248 
1249                 // add to new parent
1250 
1251                 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, true);
1252 
1253                 if (IsListEmpty(&destdir->fcb->dir_children_index))
1254                     me->fileref->dc->index = 2;
1255                 else {
1256                     dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1257 
1258                     me->fileref->dc->index = max(2, dc2->index + 1);
1259                 }
1260 
1261                 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1262                 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
1263                 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
1264             }
1265 
1266             free_fileref(me->fileref->parent);
1267             me->fileref->parent = destdir;
1268 
1269             ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1270             InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
1271             ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1272 
1273             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1274             me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
1275             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1276             me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1277             me->fileref->parent->fcb->inode_item.sequence++;
1278             me->fileref->parent->fcb->inode_item.st_ctime = now;
1279             me->fileref->parent->fcb->inode_item.st_mtime = now;
1280             me->fileref->parent->fcb->inode_item_changed = true;
1281             mark_fcb_dirty(me->fileref->parent->fcb);
1282         } else {
1283             if (me->fileref->dc) {
1284                 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1285                 RemoveEntryList(&me->fileref->dc->list_entry_index);
1286 
1287                 if (!me->fileref->fcb->ads)
1288                     remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1289 
1290                 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1291 
1292                 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, true);
1293 
1294                 if (me->fileref->fcb->ads)
1295                     InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1296                 else {
1297                     if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE)
1298                         me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1299 
1300                     if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index))
1301                         me->fileref->dc->index = 2;
1302                     else {
1303                         dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1304 
1305                         me->fileref->dc->index = max(2, dc2->index + 1);
1306                     }
1307 
1308                     InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1309                     insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
1310                 }
1311 
1312                 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
1313             }
1314         }
1315 
1316         if (!me->dummyfileref->fcb->ads) {
1317             Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback);
1318             if (!NT_SUCCESS(Status)) {
1319                 ERR("delete_fileref returned %08x\n", Status);
1320                 goto end;
1321             }
1322         }
1323 
1324         if (me->fileref->fcb->inode_item.st_nlink > 1) {
1325             hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1326             if (!hl) {
1327                 ERR("out of memory\n");
1328                 Status = STATUS_INSUFFICIENT_RESOURCES;
1329                 goto end;
1330             }
1331 
1332             hl->parent = me->fileref->parent->fcb->inode;
1333             hl->index = me->fileref->dc->index;
1334 
1335             hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
1336             hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1337             if (!hl->utf8.Buffer) {
1338                 ERR("out of memory\n");
1339                 Status = STATUS_INSUFFICIENT_RESOURCES;
1340                 ExFreePool(hl);
1341                 goto end;
1342             }
1343 
1344             RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1345 
1346             hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
1347             hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1348             if (!hl->name.Buffer) {
1349                 ERR("out of memory\n");
1350                 Status = STATUS_INSUFFICIENT_RESOURCES;
1351                 ExFreePool(hl->utf8.Buffer);
1352                 ExFreePool(hl);
1353                 goto end;
1354             }
1355 
1356             RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1357 
1358             InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
1359         }
1360 
1361         mark_fileref_dirty(me->fileref);
1362 
1363         le = le->Flink;
1364     }
1365 
1366     // loop through, and only mark streams as deleted if their parent inodes are also deleted
1367 
1368     le = move_list.Flink;
1369     while (le != &move_list) {
1370         me = CONTAINING_RECORD(le, move_entry, list_entry);
1371 
1372         if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
1373             Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback);
1374             if (!NT_SUCCESS(Status)) {
1375                 ERR("delete_fileref returned %08x\n", Status);
1376                 goto end;
1377             }
1378         }
1379 
1380         le = le->Flink;
1381     }
1382 
1383     destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation;
1384     destdir->fcb->subvol->root_item.ctime = now;
1385 
1386     me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
1387     send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1388     send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1389     send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1390 
1391     Status = STATUS_SUCCESS;
1392 
1393 end:
1394     while (!IsListEmpty(&move_list)) {
1395         le = RemoveHeadList(&move_list);
1396         me = CONTAINING_RECORD(le, move_entry, list_entry);
1397 
1398         if (me->dummyfcb)
1399             free_fcb(me->dummyfcb);
1400 
1401         if (me->dummyfileref)
1402             free_fileref(me->dummyfileref);
1403 
1404         free_fileref(me->fileref);
1405 
1406         ExFreePool(me);
1407     }
1408 
1409     destdir->fcb->subvol->fcbs_version++;
1410     fileref->fcb->subvol->fcbs_version++;
1411 
1412     release_fcb_lock(fileref->fcb->Vcb);
1413 
1414     return Status;
1415 }
1416 
1417 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
1418     bool inserted;
1419     LIST_ENTRY* le;
1420     uint8_t c, d;
1421 
1422     c = dc->hash >> 24;
1423 
1424     inserted = false;
1425 
1426     d = c;
1427     do {
1428         le = fcb->hash_ptrs[d];
1429 
1430         if (d == 0)
1431             break;
1432 
1433         d--;
1434     } while (!le);
1435 
1436     if (!le)
1437         le = fcb->dir_children_hash.Flink;
1438 
1439     while (le != &fcb->dir_children_hash) {
1440         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
1441 
1442         if (dc2->hash > dc->hash) {
1443             InsertHeadList(le->Blink, &dc->list_entry_hash);
1444             inserted = true;
1445             break;
1446         }
1447 
1448         le = le->Flink;
1449     }
1450 
1451     if (!inserted)
1452         InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash);
1453 
1454     if (!fcb->hash_ptrs[c])
1455         fcb->hash_ptrs[c] = &dc->list_entry_hash;
1456     else {
1457         dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash);
1458 
1459         if (dc2->hash > dc->hash)
1460             fcb->hash_ptrs[c] = &dc->list_entry_hash;
1461     }
1462 
1463     c = dc->hash_uc >> 24;
1464 
1465     inserted = false;
1466 
1467     d = c;
1468     do {
1469         le = fcb->hash_ptrs_uc[d];
1470 
1471         if (d == 0)
1472             break;
1473 
1474         d--;
1475     } while (!le);
1476 
1477     if (!le)
1478         le = fcb->dir_children_hash_uc.Flink;
1479 
1480     while (le != &fcb->dir_children_hash_uc) {
1481         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
1482 
1483         if (dc2->hash_uc > dc->hash_uc) {
1484             InsertHeadList(le->Blink, &dc->list_entry_hash_uc);
1485             inserted = true;
1486             break;
1487         }
1488 
1489         le = le->Flink;
1490     }
1491 
1492     if (!inserted)
1493         InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc);
1494 
1495     if (!fcb->hash_ptrs_uc[c])
1496         fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1497     else {
1498         dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc);
1499 
1500         if (dc2->hash_uc > dc->hash_uc)
1501             fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1502     }
1503 }
1504 
1505 static NTSTATUS rename_stream_to_file(device_extension* Vcb, file_ref* fileref, ccb* ccb, ULONG flags,
1506                                       PIRP Irp, LIST_ENTRY* rollback) {
1507     NTSTATUS Status;
1508     file_ref* ofr;
1509     ANSI_STRING adsdata;
1510     dir_child* dc;
1511     fcb* dummyfcb;
1512 
1513     if (fileref->fcb->type != BTRFS_TYPE_FILE)
1514         return STATUS_INVALID_PARAMETER;
1515 
1516     if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
1517         WARN("trying to rename stream on readonly file\n");
1518         return STATUS_ACCESS_DENIED;
1519     }
1520 
1521     if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
1522         WARN("insufficient permissions\n");
1523         return STATUS_ACCESS_DENIED;
1524     }
1525 
1526     if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) // file will always exist
1527         return STATUS_OBJECT_NAME_COLLISION;
1528 
1529     // FIXME - POSIX overwrites of stream?
1530 
1531     ofr = fileref->parent;
1532 
1533     if (ofr->open_count > 0) {
1534         WARN("trying to overwrite open file\n");
1535         return STATUS_ACCESS_DENIED;
1536     }
1537 
1538     if (ofr->fcb->inode_item.st_size > 0) {
1539         WARN("can only overwrite existing stream if it is zero-length\n");
1540         return STATUS_INVALID_PARAMETER;
1541     }
1542 
1543     dummyfcb = create_fcb(Vcb, PagedPool);
1544     if (!dummyfcb) {
1545         ERR("out of memory\n");
1546         return STATUS_INSUFFICIENT_RESOURCES;
1547     }
1548 
1549     // copy parent fcb onto this one
1550 
1551     fileref->fcb->subvol = ofr->fcb->subvol;
1552     fileref->fcb->inode = ofr->fcb->inode;
1553     fileref->fcb->hash = ofr->fcb->hash;
1554     fileref->fcb->type = ofr->fcb->type;
1555     fileref->fcb->inode_item = ofr->fcb->inode_item;
1556 
1557     fileref->fcb->sd = ofr->fcb->sd;
1558     ofr->fcb->sd = NULL;
1559 
1560     fileref->fcb->deleted = ofr->fcb->deleted;
1561     fileref->fcb->atts = ofr->fcb->atts;
1562 
1563     fileref->fcb->reparse_xattr = ofr->fcb->reparse_xattr;
1564     ofr->fcb->reparse_xattr.Buffer = NULL;
1565     ofr->fcb->reparse_xattr.Length = ofr->fcb->reparse_xattr.MaximumLength = 0;
1566 
1567     fileref->fcb->ea_xattr = ofr->fcb->ea_xattr;
1568     ofr->fcb->ea_xattr.Buffer = NULL;
1569     ofr->fcb->ea_xattr.Length = ofr->fcb->ea_xattr.MaximumLength = 0;
1570 
1571     fileref->fcb->ealen = ofr->fcb->ealen;
1572 
1573     while (!IsListEmpty(&ofr->fcb->hardlinks)) {
1574         InsertTailList(&fileref->fcb->hardlinks, RemoveHeadList(&ofr->fcb->hardlinks));
1575     }
1576 
1577     fileref->fcb->inode_item_changed = true;
1578     fileref->fcb->prop_compression = ofr->fcb->prop_compression;
1579 
1580     while (!IsListEmpty(&ofr->fcb->xattrs)) {
1581         InsertTailList(&fileref->fcb->xattrs, RemoveHeadList(&ofr->fcb->xattrs));
1582     }
1583 
1584     fileref->fcb->marked_as_orphan = ofr->fcb->marked_as_orphan;
1585     fileref->fcb->case_sensitive = ofr->fcb->case_sensitive;
1586     fileref->fcb->case_sensitive_set = ofr->fcb->case_sensitive_set;
1587 
1588     while (!IsListEmpty(&ofr->fcb->dir_children_index)) {
1589         InsertTailList(&fileref->fcb->dir_children_index, RemoveHeadList(&ofr->fcb->dir_children_index));
1590     }
1591 
1592     while (!IsListEmpty(&ofr->fcb->dir_children_hash)) {
1593         InsertTailList(&fileref->fcb->dir_children_hash, RemoveHeadList(&ofr->fcb->dir_children_hash));
1594     }
1595 
1596     while (!IsListEmpty(&ofr->fcb->dir_children_hash_uc)) {
1597         InsertTailList(&fileref->fcb->dir_children_hash_uc, RemoveHeadList(&ofr->fcb->dir_children_hash_uc));
1598     }
1599 
1600     fileref->fcb->hash_ptrs = ofr->fcb->hash_ptrs;
1601     fileref->fcb->hash_ptrs_uc = ofr->fcb->hash_ptrs_uc;
1602 
1603     ofr->fcb->hash_ptrs = NULL;
1604     ofr->fcb->hash_ptrs_uc = NULL;
1605 
1606     fileref->fcb->sd_dirty = ofr->fcb->sd_dirty;
1607     fileref->fcb->sd_deleted = ofr->fcb->sd_deleted;
1608     fileref->fcb->atts_changed = ofr->fcb->atts_changed;
1609     fileref->fcb->atts_deleted = ofr->fcb->atts_deleted;
1610     fileref->fcb->extents_changed = true;
1611     fileref->fcb->reparse_xattr_changed = ofr->fcb->reparse_xattr_changed;
1612     fileref->fcb->ea_changed = ofr->fcb->ea_changed;
1613     fileref->fcb->prop_compression_changed = ofr->fcb->prop_compression_changed;
1614     fileref->fcb->xattrs_changed = ofr->fcb->xattrs_changed;
1615     fileref->fcb->created = ofr->fcb->created;
1616     fileref->fcb->ads = false;
1617 
1618     if (fileref->fcb->adsxattr.Buffer) {
1619         ExFreePool(fileref->fcb->adsxattr.Buffer);
1620         fileref->fcb->adsxattr.Length = fileref->fcb->adsxattr.MaximumLength = 0;
1621         fileref->fcb->adsxattr.Buffer = NULL;
1622     }
1623 
1624     adsdata = fileref->fcb->adsdata;
1625 
1626     fileref->fcb->adsdata.Buffer = NULL;
1627     fileref->fcb->adsdata.Length = fileref->fcb->adsdata.MaximumLength = 0;
1628 
1629     InsertHeadList(ofr->fcb->list_entry.Blink, &fileref->fcb->list_entry);
1630 
1631     if (fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] == &ofr->fcb->list_entry)
1632         fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] = &fileref->fcb->list_entry;
1633 
1634     RemoveEntryList(&ofr->fcb->list_entry);
1635     ofr->fcb->list_entry.Flink = ofr->fcb->list_entry.Blink = NULL;
1636 
1637     mark_fcb_dirty(fileref->fcb);
1638 
1639     // mark old parent fcb so it gets ignored by flush_fcb
1640     ofr->fcb->created = true;
1641     ofr->fcb->deleted = true;
1642 
1643     mark_fcb_dirty(ofr->fcb);
1644 
1645     // copy parent fileref onto this one
1646 
1647     fileref->oldutf8 = ofr->oldutf8;
1648     ofr->oldutf8.Buffer = NULL;
1649     ofr->oldutf8.Length = ofr->oldutf8.MaximumLength = 0;
1650 
1651     fileref->oldindex = ofr->oldindex;
1652     fileref->delete_on_close = ofr->delete_on_close;
1653     fileref->posix_delete = ofr->posix_delete;
1654     fileref->deleted = ofr->deleted;
1655     fileref->created = ofr->created;
1656 
1657     fileref->parent = ofr->parent;
1658 
1659     RemoveEntryList(&fileref->list_entry);
1660     InsertHeadList(ofr->list_entry.Blink, &fileref->list_entry);
1661     RemoveEntryList(&ofr->list_entry);
1662     ofr->list_entry.Flink = ofr->list_entry.Blink = NULL;
1663 
1664     while (!IsListEmpty(&ofr->children)) {
1665         file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&ofr->children), file_ref, list_entry);
1666 
1667         free_fileref(fr->parent);
1668 
1669         fr->parent = fileref;
1670         InterlockedIncrement(&fileref->refcount);
1671 
1672         InsertTailList(&fileref->children, &fr->list_entry);
1673     }
1674 
1675     dc = fileref->dc;
1676 
1677     fileref->dc = ofr->dc;
1678     fileref->dc->fileref = fileref;
1679 
1680     mark_fileref_dirty(fileref);
1681 
1682     // mark old parent fileref so it gets ignored by flush_fileref
1683     ofr->created = true;
1684     ofr->deleted = true;
1685 
1686     // write file data
1687 
1688     fileref->fcb->inode_item.st_size = adsdata.Length;
1689 
1690     if (adsdata.Length > 0) {
1691         bool make_inline = adsdata.Length <= Vcb->options.max_inline;
1692 
1693         if (make_inline) {
1694             EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), ALLOC_TAG);
1695             if (!ed) {
1696                 ERR("out of memory\n");
1697                 ExFreePool(adsdata.Buffer);
1698                 reap_fcb(dummyfcb);
1699                 return STATUS_INSUFFICIENT_RESOURCES;
1700             }
1701 
1702             ed->generation = Vcb->superblock.generation;
1703             ed->decoded_size = adsdata.Length;
1704             ed->compression = BTRFS_COMPRESSION_NONE;
1705             ed->encryption = BTRFS_ENCRYPTION_NONE;
1706             ed->encoding = BTRFS_ENCODING_NONE;
1707             ed->type = EXTENT_TYPE_INLINE;
1708 
1709             RtlCopyMemory(ed->data, adsdata.Buffer, adsdata.Length);
1710 
1711             ExFreePool(adsdata.Buffer);
1712 
1713             Status = add_extent_to_fcb(fileref->fcb, 0, ed, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), false, NULL, rollback);
1714             if (!NT_SUCCESS(Status)) {
1715                 ERR("add_extent_to_fcb returned %08x\n", Status);
1716                 ExFreePool(ed);
1717                 reap_fcb(dummyfcb);
1718                 return Status;
1719             }
1720 
1721             ExFreePool(ed);
1722         } else if (adsdata.Length % Vcb->superblock.sector_size) {
1723             char* newbuf = ExAllocatePoolWithTag(PagedPool, (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size), ALLOC_TAG);
1724             if (!newbuf) {
1725                 ERR("out of memory\n");
1726                 ExFreePool(adsdata.Buffer);
1727                 reap_fcb(dummyfcb);
1728                 return STATUS_INSUFFICIENT_RESOURCES;
1729             }
1730 
1731             RtlCopyMemory(newbuf, adsdata.Buffer, adsdata.Length);
1732             RtlZeroMemory(newbuf + adsdata.Length, (uint16_t)(sector_align(adsdata.Length, Vcb->superblock.sector_size) - adsdata.Length));
1733 
1734             ExFreePool(adsdata.Buffer);
1735 
1736             adsdata.Buffer = newbuf;
1737             adsdata.Length = adsdata.MaximumLength = (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size);
1738         }
1739 
1740         if (!make_inline) {
1741             Status = do_write_file(fileref->fcb, 0, adsdata.Length, adsdata.Buffer, Irp, false, 0, rollback);
1742             if (!NT_SUCCESS(Status)) {
1743                 ERR("do_write_file returned %08x\n", Status);
1744                 ExFreePool(adsdata.Buffer);
1745                 reap_fcb(dummyfcb);
1746                 return Status;
1747             }
1748 
1749             ExFreePool(adsdata.Buffer);
1750         }
1751 
1752         fileref->fcb->inode_item.st_blocks = adsdata.Length;
1753         fileref->fcb->inode_item_changed = true;
1754     }
1755 
1756     RemoveEntryList(&dc->list_entry_index);
1757 
1758     if (dc->utf8.Buffer)
1759         ExFreePool(dc->utf8.Buffer);
1760 
1761     if (dc->name.Buffer)
1762         ExFreePool(dc->name.Buffer);
1763 
1764     if (dc->name_uc.Buffer)
1765         ExFreePool(dc->name_uc.Buffer);
1766 
1767     ExFreePool(dc);
1768 
1769     // FIXME - csums?
1770 
1771     // add dummy deleted xattr with old name
1772 
1773     dummyfcb->Vcb = Vcb;
1774     dummyfcb->subvol = fileref->fcb->subvol;
1775     dummyfcb->inode = fileref->fcb->inode;
1776     dummyfcb->adsxattr = fileref->fcb->adsxattr;
1777     dummyfcb->adshash = fileref->fcb->adshash;
1778     dummyfcb->ads = true;
1779     dummyfcb->deleted = true;
1780 
1781     // FIXME - dummyfileref as well?
1782 
1783     mark_fcb_dirty(dummyfcb);
1784 
1785     free_fcb(dummyfcb);
1786 
1787     return STATUS_SUCCESS;
1788 }
1789 
1790 static NTSTATUS rename_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
1791                               ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
1792     NTSTATUS Status;
1793     UNICODE_STRING fn;
1794     file_ref* sf = NULL;
1795     uint16_t newmaxlen;
1796     ULONG utf8len;
1797     ANSI_STRING utf8;
1798     UNICODE_STRING utf16, utf16uc;
1799     ANSI_STRING adsxattr;
1800     uint32_t crc32;
1801     fcb* dummyfcb;
1802 
1803     static const WCHAR datasuf[] = L":$DATA";
1804     static const char xapref[] = "user.";
1805 
1806     if (!fileref) {
1807         ERR("fileref not set\n");
1808         return STATUS_INVALID_PARAMETER;
1809     }
1810 
1811     if (!fileref->parent) {
1812         ERR("fileref->parent not set\n");
1813         return STATUS_INVALID_PARAMETER;
1814     }
1815 
1816     if (fri->FileNameLength < sizeof(WCHAR)) {
1817         WARN("filename too short\n");
1818         return STATUS_OBJECT_NAME_INVALID;
1819     }
1820 
1821     if (fri->FileName[0] != ':') {
1822         WARN("destination filename must begin with a colon\n");
1823         return STATUS_INVALID_PARAMETER;
1824     }
1825 
1826     if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
1827         WARN("insufficient permissions\n");
1828         return STATUS_ACCESS_DENIED;
1829     }
1830 
1831     fn.Buffer = &fri->FileName[1];
1832     fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
1833 
1834     // remove :$DATA suffix
1835     if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
1836         RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
1837         fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
1838 
1839     if (fn.Length == 0)
1840         return rename_stream_to_file(Vcb, fileref, ccb, flags, Irp, rollback);
1841 
1842     if (!is_file_name_valid(&fn, false, true)) {
1843         WARN("invalid stream name %.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
1844         return STATUS_OBJECT_NAME_INVALID;
1845     }
1846 
1847     if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
1848         WARN("trying to rename stream on readonly file\n");
1849         return STATUS_ACCESS_DENIED;
1850     }
1851 
1852     Status = open_fileref_child(Vcb, fileref->parent, &fn, fileref->parent->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
1853     if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
1854         if (Status == STATUS_SUCCESS) {
1855             if (fileref == sf || sf->deleted) {
1856                 free_fileref(sf);
1857                 sf = NULL;
1858             } else {
1859                 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
1860                     Status = STATUS_OBJECT_NAME_COLLISION;
1861                     goto end;
1862                 }
1863 
1864                 // FIXME - POSIX overwrites of stream?
1865 
1866                 if (sf->open_count > 0) {
1867                     WARN("trying to overwrite open file\n");
1868                     Status = STATUS_ACCESS_DENIED;
1869                     goto end;
1870                 }
1871 
1872                 if (sf->fcb->adsdata.Length > 0) {
1873                     WARN("can only overwrite existing stream if it is zero-length\n");
1874                     Status = STATUS_INVALID_PARAMETER;
1875                     goto end;
1876                 }
1877 
1878                 Status = delete_fileref(sf, NULL, false, Irp, rollback);
1879                 if (!NT_SUCCESS(Status)) {
1880                     ERR("delete_fileref returned %08x\n", Status);
1881                     goto end;
1882                 }
1883             }
1884         } else {
1885             ERR("open_fileref_child returned %08x\n", Status);
1886             goto end;
1887         }
1888     }
1889 
1890     Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
1891     if (!NT_SUCCESS(Status))
1892         goto end;
1893 
1894     utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
1895     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1896     if (!utf8.Buffer) {
1897         ERR("out of memory\n");
1898         Status = STATUS_INSUFFICIENT_RESOURCES;
1899         goto end;
1900     }
1901 
1902     Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
1903     if (!NT_SUCCESS(Status)) {
1904         ExFreePool(utf8.Buffer);
1905         goto end;
1906     }
1907 
1908     adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
1909     adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
1910     if (!adsxattr.Buffer) {
1911         ERR("out of memory\n");
1912         Status = STATUS_INSUFFICIENT_RESOURCES;
1913         ExFreePool(utf8.Buffer);
1914         goto end;
1915     }
1916 
1917     RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
1918     RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
1919 
1920     // don't allow if it's one of our reserved names
1921 
1922     if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
1923         (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
1924         (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
1925         (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
1926         Status = STATUS_OBJECT_NAME_INVALID;
1927         ExFreePool(utf8.Buffer);
1928         ExFreePool(adsxattr.Buffer);
1929         goto end;
1930     }
1931 
1932     utf16.Length = utf16.MaximumLength = fn.Length;
1933     utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
1934     if (!utf16.Buffer) {
1935         ERR("out of memory\n");
1936         Status = STATUS_INSUFFICIENT_RESOURCES;
1937         ExFreePool(utf8.Buffer);
1938         ExFreePool(adsxattr.Buffer);
1939         goto end;
1940     }
1941 
1942     RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
1943 
1944     newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
1945                 offsetof(DIR_ITEM, name[0]);
1946 
1947     if (newmaxlen < adsxattr.Length) {
1948         WARN("cannot rename as data too long\n");
1949         Status = STATUS_INVALID_PARAMETER;
1950         ExFreePool(utf8.Buffer);
1951         ExFreePool(utf16.Buffer);
1952         ExFreePool(adsxattr.Buffer);
1953         goto end;
1954     }
1955 
1956     newmaxlen -= adsxattr.Length;
1957 
1958     if (newmaxlen < fileref->fcb->adsdata.Length) {
1959         WARN("cannot rename as data too long\n");
1960         Status = STATUS_INVALID_PARAMETER;
1961         ExFreePool(utf8.Buffer);
1962         ExFreePool(utf16.Buffer);
1963         ExFreePool(adsxattr.Buffer);
1964         goto end;
1965     }
1966 
1967     Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
1968     if (!NT_SUCCESS(Status)) {
1969         ERR("RtlUpcaseUnicodeString returned %08x\n");
1970         ExFreePool(utf8.Buffer);
1971         ExFreePool(utf16.Buffer);
1972         ExFreePool(adsxattr.Buffer);
1973         goto end;
1974     }
1975 
1976     // add dummy deleted xattr with old name
1977 
1978     dummyfcb = create_fcb(Vcb, PagedPool);
1979     if (!dummyfcb) {
1980         ERR("out of memory\n");
1981         Status = STATUS_INSUFFICIENT_RESOURCES;
1982         ExFreePool(utf8.Buffer);
1983         ExFreePool(utf16.Buffer);
1984         ExFreePool(utf16uc.Buffer);
1985         ExFreePool(adsxattr.Buffer);
1986         goto end;
1987     }
1988 
1989     dummyfcb->Vcb = Vcb;
1990     dummyfcb->subvol = fileref->fcb->subvol;
1991     dummyfcb->inode = fileref->fcb->inode;
1992     dummyfcb->adsxattr = fileref->fcb->adsxattr;
1993     dummyfcb->adshash = fileref->fcb->adshash;
1994     dummyfcb->ads = true;
1995     dummyfcb->deleted = true;
1996 
1997     mark_fcb_dirty(dummyfcb);
1998 
1999     free_fcb(dummyfcb);
2000 
2001     // change fcb values
2002 
2003     fileref->dc->utf8 = utf8;
2004     fileref->dc->name = utf16;
2005     fileref->dc->name_uc = utf16uc;
2006 
2007     crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
2008 
2009     fileref->fcb->adsxattr = adsxattr;
2010     fileref->fcb->adshash = crc32;
2011     fileref->fcb->adsmaxlen = newmaxlen;
2012 
2013     fileref->fcb->created = true;
2014 
2015     mark_fcb_dirty(fileref->fcb);
2016 
2017     Status = STATUS_SUCCESS;
2018 
2019 end:
2020     if (sf)
2021         free_fileref(sf);
2022 
2023     return Status;
2024 }
2025 
2026 static NTSTATUS rename_file_to_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
2027                                       ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
2028     NTSTATUS Status;
2029     UNICODE_STRING fn;
2030     file_ref* sf = NULL;
2031     uint16_t newmaxlen;
2032     ULONG utf8len;
2033     ANSI_STRING utf8;
2034     UNICODE_STRING utf16, utf16uc;
2035     ANSI_STRING adsxattr, adsdata;
2036     uint32_t crc32;
2037     fcb* dummyfcb;
2038     file_ref* dummyfileref;
2039     dir_child* dc;
2040     LIST_ENTRY* le;
2041 
2042     static const WCHAR datasuf[] = L":$DATA";
2043     static const char xapref[] = "user.";
2044 
2045     if (!fileref) {
2046         ERR("fileref not set\n");
2047         return STATUS_INVALID_PARAMETER;
2048     }
2049 
2050     if (fri->FileNameLength < sizeof(WCHAR)) {
2051         WARN("filename too short\n");
2052         return STATUS_OBJECT_NAME_INVALID;
2053     }
2054 
2055     if (fri->FileName[0] != ':') {
2056         WARN("destination filename must begin with a colon\n");
2057         return STATUS_INVALID_PARAMETER;
2058     }
2059 
2060     if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
2061         WARN("insufficient permissions\n");
2062         return STATUS_ACCESS_DENIED;
2063     }
2064 
2065     if (fileref->fcb->type != BTRFS_TYPE_FILE)
2066         return STATUS_INVALID_PARAMETER;
2067 
2068     fn.Buffer = &fri->FileName[1];
2069     fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
2070 
2071     // remove :$DATA suffix
2072     if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
2073         RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
2074         fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
2075 
2076     if (fn.Length == 0) {
2077         WARN("not allowing overwriting file with itself\n");
2078         return STATUS_INVALID_PARAMETER;
2079     }
2080 
2081     if (!is_file_name_valid(&fn, false, true)) {
2082         WARN("invalid stream name %.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
2083         return STATUS_OBJECT_NAME_INVALID;
2084     }
2085 
2086     if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
2087         WARN("trying to rename stream on readonly file\n");
2088         return STATUS_ACCESS_DENIED;
2089     }
2090 
2091     Status = open_fileref_child(Vcb, fileref, &fn, fileref->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
2092     if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
2093         if (Status == STATUS_SUCCESS) {
2094             if (fileref == sf || sf->deleted) {
2095                 free_fileref(sf);
2096                 sf = NULL;
2097             } else {
2098                 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
2099                     Status = STATUS_OBJECT_NAME_COLLISION;
2100                     goto end;
2101                 }
2102 
2103                 // FIXME - POSIX overwrites of stream?
2104 
2105                 if (sf->open_count > 0) {
2106                     WARN("trying to overwrite open file\n");
2107                     Status = STATUS_ACCESS_DENIED;
2108                     goto end;
2109                 }
2110 
2111                 if (sf->fcb->adsdata.Length > 0) {
2112                     WARN("can only overwrite existing stream if it is zero-length\n");
2113                     Status = STATUS_INVALID_PARAMETER;
2114                     goto end;
2115                 }
2116 
2117                 Status = delete_fileref(sf, NULL, false, Irp, rollback);
2118                 if (!NT_SUCCESS(Status)) {
2119                     ERR("delete_fileref returned %08x\n", Status);
2120                     goto end;
2121                 }
2122             }
2123         } else {
2124             ERR("open_fileref_child returned %08x\n", Status);
2125             goto end;
2126         }
2127     }
2128 
2129     Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
2130     if (!NT_SUCCESS(Status))
2131         goto end;
2132 
2133     utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
2134     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2135     if (!utf8.Buffer) {
2136         ERR("out of memory\n");
2137         Status = STATUS_INSUFFICIENT_RESOURCES;
2138         goto end;
2139     }
2140 
2141     Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
2142     if (!NT_SUCCESS(Status)) {
2143         ExFreePool(utf8.Buffer);
2144         goto end;
2145     }
2146 
2147     adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
2148     adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
2149     if (!adsxattr.Buffer) {
2150         ERR("out of memory\n");
2151         Status = STATUS_INSUFFICIENT_RESOURCES;
2152         ExFreePool(utf8.Buffer);
2153         goto end;
2154     }
2155 
2156     RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
2157     RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
2158 
2159     // don't allow if it's one of our reserved names
2160 
2161     if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
2162         (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
2163         (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
2164         (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
2165         Status = STATUS_OBJECT_NAME_INVALID;
2166         ExFreePool(utf8.Buffer);
2167         ExFreePool(adsxattr.Buffer);
2168         goto end;
2169     }
2170 
2171     utf16.Length = utf16.MaximumLength = fn.Length;
2172     utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
2173     if (!utf16.Buffer) {
2174         ERR("out of memory\n");
2175         Status = STATUS_INSUFFICIENT_RESOURCES;
2176         ExFreePool(utf8.Buffer);
2177         ExFreePool(adsxattr.Buffer);
2178         goto end;
2179     }
2180 
2181     RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
2182 
2183     newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
2184                 offsetof(DIR_ITEM, name[0]);
2185 
2186     if (newmaxlen < adsxattr.Length) {
2187         WARN("cannot rename as data too long\n");
2188         Status = STATUS_INVALID_PARAMETER;
2189         ExFreePool(utf8.Buffer);
2190         ExFreePool(utf16.Buffer);
2191         ExFreePool(adsxattr.Buffer);
2192         goto end;
2193     }
2194 
2195     newmaxlen -= adsxattr.Length;
2196 
2197     if (newmaxlen < fileref->fcb->inode_item.st_size) {
2198         WARN("cannot rename as data too long\n");
2199         Status = STATUS_INVALID_PARAMETER;
2200         ExFreePool(utf8.Buffer);
2201         ExFreePool(utf16.Buffer);
2202         ExFreePool(adsxattr.Buffer);
2203         goto end;
2204     }
2205 
2206     Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
2207     if (!NT_SUCCESS(Status)) {
2208         ERR("RtlUpcaseUnicodeString returned %08x\n");
2209         ExFreePool(utf8.Buffer);
2210         ExFreePool(utf16.Buffer);
2211         ExFreePool(adsxattr.Buffer);
2212         goto end;
2213     }
2214 
2215     // read existing file data
2216 
2217     if (fileref->fcb->inode_item.st_size > 0) {
2218         ULONG bytes_read;
2219 
2220         adsdata.Length = adsdata.MaximumLength = (uint16_t)fileref->fcb->inode_item.st_size;
2221 
2222         adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, adsdata.MaximumLength, ALLOC_TAG);
2223         if (!adsdata.Buffer) {
2224             ERR("out of memory\n");
2225             Status = STATUS_INSUFFICIENT_RESOURCES;
2226             ExFreePool(utf8.Buffer);
2227             ExFreePool(utf16.Buffer);
2228             ExFreePool(utf16uc.Buffer);
2229             ExFreePool(adsxattr.Buffer);
2230             goto end;
2231         }
2232 
2233         Status = read_file(fileref->fcb, (uint8_t*)adsdata.Buffer, 0, adsdata.Length, &bytes_read, Irp);
2234         if (!NT_SUCCESS(Status)) {
2235             ERR("out of memory\n");
2236             Status = STATUS_INSUFFICIENT_RESOURCES;
2237             ExFreePool(utf8.Buffer);
2238             ExFreePool(utf16.Buffer);
2239             ExFreePool(utf16uc.Buffer);
2240             ExFreePool(adsxattr.Buffer);
2241             ExFreePool(adsdata.Buffer);
2242             goto end;
2243         }
2244 
2245         if (bytes_read < fileref->fcb->inode_item.st_size) {
2246             ERR("short read\n");
2247             Status = STATUS_INTERNAL_ERROR;
2248             ExFreePool(utf8.Buffer);
2249             ExFreePool(utf16.Buffer);
2250             ExFreePool(utf16uc.Buffer);
2251             ExFreePool(adsxattr.Buffer);
2252             ExFreePool(adsdata.Buffer);
2253             goto end;
2254         }
2255     } else
2256         adsdata.Buffer = NULL;
2257 
2258     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
2259     if (!dc) {
2260         ERR("short read\n");
2261         Status = STATUS_INSUFFICIENT_RESOURCES;;
2262         ExFreePool(utf8.Buffer);
2263         ExFreePool(utf16.Buffer);
2264         ExFreePool(utf16uc.Buffer);
2265         ExFreePool(adsxattr.Buffer);
2266 
2267         if (adsdata.Buffer)
2268             ExFreePool(adsdata.Buffer);
2269 
2270         goto end;
2271     }
2272 
2273     // add dummy deleted fcb with old name
2274 
2275     Status = duplicate_fcb(fileref->fcb, &dummyfcb);
2276     if (!NT_SUCCESS(Status)) {
2277         ERR("duplicate_fcb returned %08x\n", Status);
2278         ExFreePool(utf8.Buffer);
2279         ExFreePool(utf16.Buffer);
2280         ExFreePool(utf16uc.Buffer);
2281         ExFreePool(adsxattr.Buffer);
2282 
2283         if (adsdata.Buffer)
2284             ExFreePool(adsdata.Buffer);
2285 
2286         ExFreePool(dc);
2287 
2288         goto end;
2289     }
2290 
2291     dummyfileref = create_fileref(Vcb);
2292     if (!dummyfileref) {
2293         ERR("out of memory\n");
2294         Status = STATUS_INSUFFICIENT_RESOURCES;
2295         ExFreePool(utf8.Buffer);
2296         ExFreePool(utf16.Buffer);
2297         ExFreePool(utf16uc.Buffer);
2298         ExFreePool(adsxattr.Buffer);
2299 
2300         if (adsdata.Buffer)
2301             ExFreePool(adsdata.Buffer);
2302 
2303         ExFreePool(dc);
2304 
2305         reap_fcb(dummyfcb);
2306 
2307         goto end;
2308     }
2309 
2310     dummyfileref->fcb = dummyfcb;
2311 
2312     dummyfcb->Vcb = Vcb;
2313     dummyfcb->subvol = fileref->fcb->subvol;
2314     dummyfcb->inode = fileref->fcb->inode;
2315     dummyfcb->hash = fileref->fcb->hash;
2316 
2317     if (fileref->fcb->inode_item.st_size > 0) {
2318         Status = excise_extents(Vcb, dummyfcb, 0, sector_align(fileref->fcb->inode_item.st_size, Vcb->superblock.sector_size),
2319                                 Irp, rollback);
2320         if (!NT_SUCCESS(Status)) {
2321             ERR("excise_extents returned %08x\n", Status);
2322             ExFreePool(utf8.Buffer);
2323             ExFreePool(utf16.Buffer);
2324             ExFreePool(utf16uc.Buffer);
2325             ExFreePool(adsxattr.Buffer);
2326             ExFreePool(adsdata.Buffer);
2327             ExFreePool(dc);
2328 
2329             reap_fileref(Vcb, dummyfileref);
2330             reap_fcb(dummyfcb);
2331 
2332             goto end;
2333         }
2334 
2335         dummyfcb->inode_item.st_size = 0;
2336         dummyfcb->Header.AllocationSize.QuadPart = 0;
2337         dummyfcb->Header.FileSize.QuadPart = 0;
2338         dummyfcb->Header.ValidDataLength.QuadPart = 0;
2339     }
2340 
2341     dummyfcb->hash_ptrs = fileref->fcb->hash_ptrs;
2342     dummyfcb->hash_ptrs_uc = fileref->fcb->hash_ptrs_uc;
2343     dummyfcb->created = fileref->fcb->created;
2344 
2345     le = fileref->fcb->extents.Flink;
2346     while (le != &fileref->fcb->extents) {
2347         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2348 
2349         ext->ignore = true;
2350 
2351         le = le->Flink;
2352     }
2353 
2354     while (!IsListEmpty(&fileref->fcb->dir_children_index)) {
2355         InsertTailList(&dummyfcb->dir_children_index, RemoveHeadList(&fileref->fcb->dir_children_index));
2356     }
2357 
2358     while (!IsListEmpty(&fileref->fcb->dir_children_hash)) {
2359         InsertTailList(&dummyfcb->dir_children_hash, RemoveHeadList(&fileref->fcb->dir_children_hash));
2360     }
2361 
2362     while (!IsListEmpty(&fileref->fcb->dir_children_hash_uc)) {
2363         InsertTailList(&dummyfcb->dir_children_hash_uc, RemoveHeadList(&fileref->fcb->dir_children_hash_uc));
2364     }
2365 
2366     InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all);
2367 
2368     InsertHeadList(fileref->fcb->list_entry.Blink, &dummyfcb->list_entry);
2369 
2370     if (fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] == &fileref->fcb->list_entry)
2371         fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] = &dummyfcb->list_entry;
2372 
2373     RemoveEntryList(&fileref->fcb->list_entry);
2374     fileref->fcb->list_entry.Flink = fileref->fcb->list_entry.Blink = NULL;
2375 
2376     mark_fcb_dirty(dummyfcb);
2377 
2378     // create dummy fileref
2379 
2380     dummyfileref->oldutf8 = fileref->oldutf8;
2381     dummyfileref->oldindex = fileref->oldindex;
2382     dummyfileref->delete_on_close = fileref->delete_on_close;
2383     dummyfileref->posix_delete = fileref->posix_delete;
2384     dummyfileref->deleted = fileref->deleted;
2385     dummyfileref->created = fileref->created;
2386     dummyfileref->parent = fileref->parent;
2387 
2388     while (!IsListEmpty(&fileref->children)) {
2389         file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&fileref->children), file_ref, list_entry);
2390 
2391         free_fileref(fr->parent);
2392 
2393         fr->parent = dummyfileref;
2394         InterlockedIncrement(&dummyfileref->refcount);
2395 
2396         InsertTailList(&dummyfileref->children, &fr->list_entry);
2397     }
2398 
2399     InsertTailList(fileref->list_entry.Blink, &dummyfileref->list_entry);
2400 
2401     RemoveEntryList(&fileref->list_entry);
2402     InsertTailList(&dummyfileref->children, &fileref->list_entry);
2403 
2404     dummyfileref->dc = fileref->dc;
2405     dummyfileref->dc->fileref = dummyfileref;
2406 
2407     mark_fileref_dirty(dummyfileref);
2408 
2409     free_fileref(dummyfileref);
2410 
2411     // change fcb values
2412 
2413     fileref->fcb->hash_ptrs = NULL;
2414     fileref->fcb->hash_ptrs_uc = NULL;
2415 
2416     fileref->fcb->ads = true;
2417 
2418     fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
2419     fileref->oldutf8.Buffer = NULL;
2420 
2421     RtlZeroMemory(dc, sizeof(dir_child));
2422 
2423     dc->utf8 = utf8;
2424     dc->name = utf16;
2425     dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
2426     dc->name_uc = utf16uc;
2427     dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
2428     dc->fileref = fileref;
2429     InsertTailList(&dummyfcb->dir_children_index, &dc->list_entry_index);
2430 
2431     fileref->dc = dc;
2432     fileref->parent = dummyfileref;
2433 
2434     crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
2435 
2436     fileref->fcb->adsxattr = adsxattr;
2437     fileref->fcb->adshash = crc32;
2438     fileref->fcb->adsmaxlen = newmaxlen;
2439     fileref->fcb->adsdata = adsdata;
2440 
2441     fileref->fcb->created = true;
2442 
2443     mark_fcb_dirty(fileref->fcb);
2444 
2445     Status = STATUS_SUCCESS;
2446 
2447 end:
2448     if (sf)
2449         free_fileref(sf);
2450 
2451     return Status;
2452 }
2453 
2454 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
2455     FILE_RENAME_INFORMATION_EX* fri = Irp->AssociatedIrp.SystemBuffer;
2456     fcb* fcb = FileObject->FsContext;
2457     ccb* ccb = FileObject->FsContext2;
2458     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2459     WCHAR* fn;
2460     ULONG fnlen, utf8len, origutf8len;
2461     UNICODE_STRING fnus;
2462     ANSI_STRING utf8;
2463     NTSTATUS Status;
2464     LARGE_INTEGER time;
2465     BTRFS_TIME now;
2466     LIST_ENTRY rollback, *le;
2467     hardlink* hl;
2468     SECURITY_SUBJECT_CONTEXT subjcont;
2469     ACCESS_MASK access;
2470     ULONG flags;
2471 #ifdef __REACTOS__
2472     unsigned int i;
2473 #endif
2474 
2475     InitializeListHead(&rollback);
2476 
2477     if (ex)
2478         flags = fri->Flags;
2479     else
2480         flags = fri->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0;
2481 
2482     TRACE("tfo = %p\n", tfo);
2483     TRACE("Flags = %x\n", flags);
2484     TRACE("RootDirectory = %p\n", fri->RootDirectory);
2485     TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
2486 
2487     fn = fri->FileName;
2488     fnlen = fri->FileNameLength / sizeof(WCHAR);
2489 
2490     if (!tfo) {
2491         if (!fileref || !fileref->parent) {
2492             ERR("no fileref set and no directory given\n");
2493             return STATUS_INVALID_PARAMETER;
2494         }
2495     } else {
2496         LONG i;
2497 
2498         while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
2499             fnlen--;
2500 
2501         if (fnlen == 0)
2502             return STATUS_INVALID_PARAMETER;
2503 
2504         for (i = fnlen - 1; i >= 0; i--) {
2505             if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
2506                 fn = &fri->FileName[i+1];
2507                 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
2508                 break;
2509             }
2510         }
2511     }
2512 
2513     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2514     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2515     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2516 
2517     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
2518         WARN("not allowing \\$Root to be renamed\n");
2519         Status = STATUS_ACCESS_DENIED;
2520         goto end;
2521     }
2522 
2523     if (fcb->ads) {
2524         if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
2525             IO_STATUS_BLOCK iosb;
2526 
2527             CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2528             if (!NT_SUCCESS(iosb.Status)) {
2529                 ERR("CcFlushCache returned %08x\n", iosb.Status);
2530                 Status = iosb.Status;
2531                 goto end;
2532             }
2533         }
2534 
2535         Status = rename_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
2536         goto end;
2537     } else if (fnlen >= 1 && fn[0] == ':') {
2538         if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
2539             IO_STATUS_BLOCK iosb;
2540 
2541             CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2542             if (!NT_SUCCESS(iosb.Status)) {
2543                 ERR("CcFlushCache returned %08x\n", iosb.Status);
2544                 Status = iosb.Status;
2545                 goto end;
2546             }
2547         }
2548 
2549         Status = rename_file_to_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
2550         goto end;
2551     }
2552 
2553     fnus.Buffer = fn;
2554     fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR));
2555 
2556     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
2557 
2558 #ifndef __REACTOS__
2559     for (unsigned int i = 0 ; i < fnus.Length / sizeof(WCHAR); i++) {
2560 #else
2561     for (i = 0 ; i < fnus.Length / sizeof(WCHAR); i++) {
2562 #endif
2563         if (fnus.Buffer[i] == ':') {
2564             TRACE("colon in filename\n");
2565             Status = STATUS_OBJECT_NAME_INVALID;
2566             goto end;
2567         }
2568     }
2569 
2570     origutf8len = fileref->dc->utf8.Length;
2571 
2572     Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2573     if (!NT_SUCCESS(Status))
2574         goto end;
2575 
2576     utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
2577     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2578     if (!utf8.Buffer) {
2579         ERR("out of memory\n");
2580         Status = STATUS_INSUFFICIENT_RESOURCES;
2581         goto end;
2582     }
2583 
2584     Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2585     if (!NT_SUCCESS(Status))
2586         goto end;
2587 
2588     if (tfo && tfo->FsContext2) {
2589         struct _ccb* relatedccb = tfo->FsContext2;
2590 
2591         related = relatedccb->fileref;
2592         increase_fileref_refcount(related);
2593     } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') {
2594         related = fileref->parent;
2595         increase_fileref_refcount(related);
2596     }
2597 
2598     Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive,  Irp);
2599 
2600     if (NT_SUCCESS(Status)) {
2601         TRACE("destination file already exists\n");
2602 
2603         if (fileref != oldfileref && !oldfileref->deleted) {
2604             if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
2605                 Status = STATUS_OBJECT_NAME_COLLISION;
2606                 goto end;
2607             } else if (fileref == oldfileref) {
2608                 Status = STATUS_ACCESS_DENIED;
2609                 goto end;
2610             } else if (!(flags & FILE_RENAME_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
2611                 WARN("trying to overwrite open file\n");
2612                 Status = STATUS_ACCESS_DENIED;
2613                 goto end;
2614             } else if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
2615                 WARN("trying to overwrite readonly file\n");
2616                 Status = STATUS_ACCESS_DENIED;
2617                 goto end;
2618             } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2619                 WARN("trying to overwrite directory\n");
2620                 Status = STATUS_ACCESS_DENIED;
2621                 goto end;
2622             }
2623         }
2624 
2625         if (fileref == oldfileref || oldfileref->deleted) {
2626             free_fileref(oldfileref);
2627             oldfileref = NULL;
2628         }
2629     }
2630 
2631     if (!related) {
2632         Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2633 
2634         if (!NT_SUCCESS(Status)) {
2635             ERR("open_fileref returned %08x\n", Status);
2636             goto end;
2637         }
2638     }
2639 
2640     if (related->fcb == Vcb->dummy_fcb) {
2641         Status = STATUS_ACCESS_DENIED;
2642         goto end;
2643     }
2644 
2645     SeCaptureSubjectContext(&subjcont);
2646 
2647     if (!SeAccessCheck(related->fcb->sd, &subjcont, false, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
2648         IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2649         SeReleaseSubjectContext(&subjcont);
2650         TRACE("SeAccessCheck failed, returning %08x\n", Status);
2651         goto end;
2652     }
2653 
2654     SeReleaseSubjectContext(&subjcont);
2655 
2656     if (has_open_children(fileref)) {
2657         WARN("trying to rename file with open children\n");
2658         Status = STATUS_ACCESS_DENIED;
2659         goto end;
2660     }
2661 
2662     if (oldfileref) {
2663         SeCaptureSubjectContext(&subjcont);
2664 
2665         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL,
2666                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2667             SeReleaseSubjectContext(&subjcont);
2668             TRACE("SeAccessCheck failed, returning %08x\n", Status);
2669             goto end;
2670         }
2671 
2672         SeReleaseSubjectContext(&subjcont);
2673 
2674         if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
2675             oldfileref->delete_on_close = true;
2676             oldfileref->posix_delete = true;
2677         }
2678 
2679         Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
2680         if (!NT_SUCCESS(Status)) {
2681             ERR("delete_fileref returned %08x\n", Status);
2682             goto end;
2683         }
2684     }
2685 
2686     if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) {
2687         Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
2688         if (!NT_SUCCESS(Status)) {
2689             ERR("move_across_subvols returned %08x\n", Status);
2690         }
2691         goto end;
2692     }
2693 
2694     if (related == fileref->parent) { // keeping file in same directory
2695         UNICODE_STRING oldfn, newfn;
2696         USHORT name_offset;
2697         ULONG reqlen, oldutf8len;
2698 
2699         oldfn.Length = oldfn.MaximumLength = 0;
2700 
2701         Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
2702         if (Status != STATUS_BUFFER_OVERFLOW) {
2703             ERR("fileref_get_filename returned %08x\n", Status);
2704             goto end;
2705         }
2706 
2707         oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
2708         if (!oldfn.Buffer) {
2709             ERR("out of memory\n");
2710             Status = STATUS_INSUFFICIENT_RESOURCES;
2711             goto end;
2712         }
2713 
2714         oldfn.MaximumLength = (uint16_t)reqlen;
2715 
2716         Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
2717         if (!NT_SUCCESS(Status)) {
2718             ERR("fileref_get_filename returned %08x\n", Status);
2719             ExFreePool(oldfn.Buffer);
2720             goto end;
2721         }
2722 
2723         oldutf8len = fileref->dc->utf8.Length;
2724 
2725         if (!fileref->created && !fileref->oldutf8.Buffer) {
2726             fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
2727             if (!fileref->oldutf8.Buffer) {
2728                 ERR("out of memory\n");
2729                 Status = STATUS_INSUFFICIENT_RESOURCES;
2730                 goto end;
2731             }
2732 
2733             fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
2734             RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2735         }
2736 
2737         TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer);
2738 
2739         mark_fileref_dirty(fileref);
2740 
2741         if (fileref->dc) {
2742             ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2743 
2744             ExFreePool(fileref->dc->utf8.Buffer);
2745             ExFreePool(fileref->dc->name.Buffer);
2746             ExFreePool(fileref->dc->name_uc.Buffer);
2747 
2748             fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
2749             if (!fileref->dc->utf8.Buffer) {
2750                 ERR("out of memory\n");
2751                 Status = STATUS_INSUFFICIENT_RESOURCES;
2752                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2753                 ExFreePool(oldfn.Buffer);
2754                 goto end;
2755             }
2756 
2757             fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
2758             RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
2759 
2760             fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2761             if (!fileref->dc->name.Buffer) {
2762                 ERR("out of memory\n");
2763                 Status = STATUS_INSUFFICIENT_RESOURCES;
2764                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2765                 ExFreePool(oldfn.Buffer);
2766                 goto end;
2767             }
2768 
2769             fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
2770             RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
2771 
2772             Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
2773             if (!NT_SUCCESS(Status)) {
2774                 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
2775                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2776                 ExFreePool(oldfn.Buffer);
2777                 goto end;
2778             }
2779 
2780             remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2781 
2782             fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length);
2783             fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
2784 
2785             insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc);
2786 
2787             ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2788         }
2789 
2790         newfn.Length = newfn.MaximumLength = 0;
2791 
2792         Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
2793         if (Status != STATUS_BUFFER_OVERFLOW) {
2794             ERR("fileref_get_filename returned %08x\n", Status);
2795             ExFreePool(oldfn.Buffer);
2796             goto end;
2797         }
2798 
2799         newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
2800         if (!newfn.Buffer) {
2801             ERR("out of memory\n");
2802             Status = STATUS_INSUFFICIENT_RESOURCES;
2803             ExFreePool(oldfn.Buffer);
2804             goto end;
2805         }
2806 
2807         newfn.MaximumLength = (uint16_t)reqlen;
2808 
2809         Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
2810         if (!NT_SUCCESS(Status)) {
2811             ERR("fileref_get_filename returned %08x\n", Status);
2812             ExFreePool(oldfn.Buffer);
2813             ExFreePool(newfn.Buffer);
2814             goto end;
2815         }
2816 
2817         KeQuerySystemTime(&time);
2818         win_time_to_unix(time, &now);
2819 
2820         if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
2821             fcb->inode_item.transid = Vcb->superblock.generation;
2822             fcb->inode_item.sequence++;
2823 
2824             if (!ccb->user_set_change_time)
2825                 fcb->inode_item.st_ctime = now;
2826 
2827             fcb->inode_item_changed = true;
2828             mark_fcb_dirty(fcb);
2829         }
2830 
2831         // update parent's INODE_ITEM
2832 
2833         related->fcb->inode_item.transid = Vcb->superblock.generation;
2834         TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
2835         related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len);
2836         TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
2837         related->fcb->inode_item.sequence++;
2838         related->fcb->inode_item.st_ctime = now;
2839         related->fcb->inode_item.st_mtime = now;
2840 
2841         related->fcb->inode_item_changed = true;
2842         mark_fcb_dirty(related->fcb);
2843         send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2844 
2845         FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL,
2846                                       fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL);
2847         FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL,
2848                                       fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL);
2849 
2850         ExFreePool(oldfn.Buffer);
2851         ExFreePool(newfn.Buffer);
2852 
2853         Status = STATUS_SUCCESS;
2854         goto end;
2855     }
2856 
2857     // We move files by moving the existing fileref to the new directory, and
2858     // replacing it with a dummy fileref with the same original values, but marked as deleted.
2859 
2860     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
2861 
2862     fr2 = create_fileref(Vcb);
2863 
2864     fr2->fcb = fileref->fcb;
2865     fr2->fcb->refcount++;
2866 
2867     fr2->oldutf8 = fileref->oldutf8;
2868     fr2->oldindex = fileref->dc->index;
2869     fr2->delete_on_close = fileref->delete_on_close;
2870     fr2->deleted = true;
2871     fr2->created = fileref->created;
2872     fr2->parent = fileref->parent;
2873     fr2->dc = NULL;
2874 
2875     if (!fr2->oldutf8.Buffer) {
2876         fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
2877         if (!fr2->oldutf8.Buffer) {
2878             ERR("out of memory\n");
2879             Status = STATUS_INSUFFICIENT_RESOURCES;
2880             goto end;
2881         }
2882 
2883         RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2884 
2885         fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
2886     }
2887 
2888     if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
2889         fr2->fcb->fileref = fr2;
2890 
2891     if (fileref->fcb->inode == SUBVOL_ROOT_INODE)
2892         fileref->fcb->subvol->parent = related->fcb->subvol->id;
2893 
2894     fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
2895     fileref->oldutf8.Buffer = NULL;
2896     fileref->deleted = false;
2897     fileref->created = true;
2898     fileref->parent = related;
2899 
2900     ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2901     InsertHeadList(&fileref->list_entry, &fr2->list_entry);
2902     RemoveEntryList(&fileref->list_entry);
2903     ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2904 
2905     mark_fileref_dirty(fr2);
2906     mark_fileref_dirty(fileref);
2907 
2908     if (fileref->dc) {
2909         // remove from old parent
2910         ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, true);
2911         RemoveEntryList(&fileref->dc->list_entry_index);
2912         remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
2913         ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
2914 
2915         if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
2916             // handle changed name
2917 
2918             ExFreePool(fileref->dc->utf8.Buffer);
2919             ExFreePool(fileref->dc->name.Buffer);
2920             ExFreePool(fileref->dc->name_uc.Buffer);
2921 
2922             fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
2923             if (!fileref->dc->utf8.Buffer) {
2924                 ERR("out of memory\n");
2925                 Status = STATUS_INSUFFICIENT_RESOURCES;
2926                 goto end;
2927             }
2928 
2929             fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
2930             RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
2931 
2932             fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2933             if (!fileref->dc->name.Buffer) {
2934                 ERR("out of memory\n");
2935                 Status = STATUS_INSUFFICIENT_RESOURCES;
2936                 goto end;
2937             }
2938 
2939             fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
2940             RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
2941 
2942             Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
2943             if (!NT_SUCCESS(Status)) {
2944                 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
2945                 goto end;
2946             }
2947 
2948             fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length);
2949             fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
2950         }
2951 
2952         // add to new parent
2953         ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
2954 
2955         if (IsListEmpty(&related->fcb->dir_children_index))
2956             fileref->dc->index = 2;
2957         else {
2958             dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
2959 
2960             fileref->dc->index = max(2, dc2->index + 1);
2961         }
2962 
2963         InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
2964         insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
2965         ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
2966     }
2967 
2968     ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
2969     InsertTailList(&related->children, &fileref->list_entry);
2970     ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
2971 
2972     if (fcb->inode_item.st_nlink > 1) {
2973         // add new hardlink entry to fcb
2974 
2975         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2976         if (!hl) {
2977             ERR("out of memory\n");
2978             Status = STATUS_INSUFFICIENT_RESOURCES;
2979             goto end;
2980         }
2981 
2982         hl->parent = related->fcb->inode;
2983         hl->index = fileref->dc->index;
2984 
2985         hl->name.Length = hl->name.MaximumLength = fnus.Length;
2986         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
2987 
2988         if (!hl->name.Buffer) {
2989             ERR("out of memory\n");
2990             ExFreePool(hl);
2991             Status = STATUS_INSUFFICIENT_RESOURCES;
2992             goto end;
2993         }
2994 
2995         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2996 
2997         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
2998         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2999 
3000         if (!hl->utf8.Buffer) {
3001             ERR("out of memory\n");
3002             ExFreePool(hl->name.Buffer);
3003             ExFreePool(hl);
3004             Status = STATUS_INSUFFICIENT_RESOURCES;
3005             goto end;
3006         }
3007 
3008         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
3009 
3010         InsertTailList(&fcb->hardlinks, &hl->list_entry);
3011     }
3012 
3013     // delete old hardlink entry from fcb
3014 
3015     le = fcb->hardlinks.Flink;
3016     while (le != &fcb->hardlinks) {
3017         hl = CONTAINING_RECORD(le, hardlink, list_entry);
3018 
3019         if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
3020             RemoveEntryList(&hl->list_entry);
3021 
3022             if (hl->utf8.Buffer)
3023                 ExFreePool(hl->utf8.Buffer);
3024 
3025             if (hl->name.Buffer)
3026                 ExFreePool(hl->name.Buffer);
3027 
3028             ExFreePool(hl);
3029             break;
3030         }
3031 
3032         le = le->Flink;
3033     }
3034 
3035     // update inode's INODE_ITEM
3036 
3037     KeQuerySystemTime(&time);
3038     win_time_to_unix(time, &now);
3039 
3040     if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
3041         fcb->inode_item.transid = Vcb->superblock.generation;
3042         fcb->inode_item.sequence++;
3043 
3044         if (!ccb->user_set_change_time)
3045             fcb->inode_item.st_ctime = now;
3046 
3047         fcb->inode_item_changed = true;
3048         mark_fcb_dirty(fcb);
3049     }
3050 
3051     // update new parent's INODE_ITEM
3052 
3053     related->fcb->inode_item.transid = Vcb->superblock.generation;
3054     TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
3055     related->fcb->inode_item.st_size += 2 * utf8len;
3056     TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
3057     related->fcb->inode_item.sequence++;
3058     related->fcb->inode_item.st_ctime = now;
3059     related->fcb->inode_item.st_mtime = now;
3060 
3061     related->fcb->inode_item_changed = true;
3062     mark_fcb_dirty(related->fcb);
3063 
3064     // update old parent's INODE_ITEM
3065 
3066     fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
3067     TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
3068     fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
3069     TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
3070     fr2->parent->fcb->inode_item.sequence++;
3071     fr2->parent->fcb->inode_item.st_ctime = now;
3072     fr2->parent->fcb->inode_item.st_mtime = now;
3073 
3074     free_fileref(fr2);
3075 
3076     fr2->parent->fcb->inode_item_changed = true;
3077     mark_fcb_dirty(fr2->parent->fcb);
3078 
3079     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3080     send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3081     send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3082 
3083     Status = STATUS_SUCCESS;
3084 
3085 end:
3086     if (oldfileref)
3087         free_fileref(oldfileref);
3088 
3089     if (!NT_SUCCESS(Status) && related)
3090         free_fileref(related);
3091 
3092     if (!NT_SUCCESS(Status) && fr2)
3093         free_fileref(fr2);
3094 
3095     if (NT_SUCCESS(Status))
3096         clear_rollback(&rollback);
3097     else
3098         do_rollback(Vcb, &rollback);
3099 
3100     ExReleaseResourceLite(fcb->Header.Resource);
3101     ExReleaseResourceLite(&Vcb->fileref_lock);
3102     ExReleaseResourceLite(&Vcb->tree_lock);
3103 
3104     return Status;
3105 }
3106 
3107 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, uint16_t end, fcb* fcb, file_ref* fileref, bool advance_only) {
3108     LARGE_INTEGER time;
3109     BTRFS_TIME now;
3110 
3111     TRACE("setting new end to %I64x bytes (currently %x)\n", end, fcb->adsdata.Length);
3112 
3113     if (!fileref || !fileref->parent) {
3114         ERR("no fileref for stream\n");
3115         return STATUS_INTERNAL_ERROR;
3116     }
3117 
3118     if (end < fcb->adsdata.Length) {
3119         if (advance_only)
3120             return STATUS_SUCCESS;
3121 
3122         TRACE("truncating stream to %I64x bytes\n", end);
3123 
3124         fcb->adsdata.Length = end;
3125     } else if (end > fcb->adsdata.Length) {
3126         TRACE("extending stream to %I64x bytes\n", end);
3127 
3128         if (end > fcb->adsmaxlen) {
3129             ERR("error - xattr too long (%u > %u)\n", end, fcb->adsmaxlen);
3130             return STATUS_DISK_FULL;
3131         }
3132 
3133         if (end > fcb->adsdata.MaximumLength) {
3134             char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
3135             if (!data) {
3136                 ERR("out of memory\n");
3137                 ExFreePool(data);
3138                 return STATUS_INSUFFICIENT_RESOURCES;
3139             }
3140 
3141             if (fcb->adsdata.Buffer) {
3142                 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length);
3143                 ExFreePool(fcb->adsdata.Buffer);
3144             }
3145 
3146             fcb->adsdata.Buffer = data;
3147             fcb->adsdata.MaximumLength = end;
3148         }
3149 
3150         RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length);
3151 
3152         fcb->adsdata.Length = end;
3153     }
3154 
3155     mark_fcb_dirty(fcb);
3156 
3157     fcb->Header.AllocationSize.QuadPart = end;
3158     fcb->Header.FileSize.QuadPart = end;
3159     fcb->Header.ValidDataLength.QuadPart = end;
3160 
3161     KeQuerySystemTime(&time);
3162     win_time_to_unix(time, &now);
3163 
3164     fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
3165     fileref->parent->fcb->inode_item.sequence++;
3166     fileref->parent->fcb->inode_item.st_ctime = now;
3167 
3168     fileref->parent->fcb->inode_item_changed = true;
3169     mark_fcb_dirty(fileref->parent->fcb);
3170 
3171     fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3172     fileref->parent->fcb->subvol->root_item.ctime = now;
3173 
3174     return STATUS_SUCCESS;
3175 }
3176 
3177 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool advance_only, bool prealloc) {
3178     FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
3179     fcb* fcb = FileObject->FsContext;
3180     ccb* ccb = FileObject->FsContext2;
3181     file_ref* fileref = ccb ? ccb->fileref : NULL;
3182     NTSTATUS Status;
3183     LARGE_INTEGER time;
3184     CC_FILE_SIZES ccfs;
3185     LIST_ENTRY rollback;
3186     bool set_size = false;
3187     ULONG filter;
3188 
3189     if (!fileref) {
3190         ERR("fileref is NULL\n");
3191         return STATUS_INVALID_PARAMETER;
3192     }
3193 
3194     InitializeListHead(&rollback);
3195 
3196     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3197 
3198     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3199 
3200     if (fileref ? fileref->deleted : fcb->deleted) {
3201         Status = STATUS_FILE_CLOSED;
3202         goto end;
3203     }
3204 
3205     if (fcb->ads) {
3206         if (feofi->EndOfFile.QuadPart > 0xffff) {
3207             Status = STATUS_DISK_FULL;
3208             goto end;
3209         }
3210 
3211         if (feofi->EndOfFile.QuadPart < 0) {
3212             Status = STATUS_INVALID_PARAMETER;
3213             goto end;
3214         }
3215 
3216         Status = stream_set_end_of_file_information(Vcb, (uint16_t)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only);
3217 
3218         if (NT_SUCCESS(Status)) {
3219             ccfs.AllocationSize = fcb->Header.AllocationSize;
3220             ccfs.FileSize = fcb->Header.FileSize;
3221             ccfs.ValidDataLength = fcb->Header.ValidDataLength;
3222             set_size = true;
3223         }
3224 
3225         filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
3226 
3227         if (!ccb->user_set_write_time) {
3228             KeQuerySystemTime(&time);
3229             win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime);
3230             filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3231 
3232             fileref->parent->fcb->inode_item_changed = true;
3233             mark_fcb_dirty(fileref->parent->fcb);
3234         }
3235 
3236         queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
3237 
3238         goto end;
3239     }
3240 
3241     TRACE("file: %p\n", FileObject);
3242     TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "true" : "false");
3243     TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n",
3244         fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
3245 
3246     TRACE("setting new end to %I64x bytes (currently %I64x)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
3247 
3248     if ((uint64_t)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
3249         if (advance_only) {
3250             Status = STATUS_SUCCESS;
3251             goto end;
3252         }
3253 
3254         TRACE("truncating file to %I64x bytes\n", feofi->EndOfFile.QuadPart);
3255 
3256         if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
3257             Status = STATUS_USER_MAPPED_FILE;
3258             goto end;
3259         }
3260 
3261         Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
3262         if (!NT_SUCCESS(Status)) {
3263             ERR("error - truncate_file failed\n");
3264             goto end;
3265         }
3266     } else if ((uint64_t)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
3267         if (Irp->Flags & IRP_PAGING_IO) {
3268             TRACE("paging IO tried to extend file size\n");
3269             Status = STATUS_SUCCESS;
3270             goto end;
3271         }
3272 
3273         TRACE("extending file to %I64x bytes\n", feofi->EndOfFile.QuadPart);
3274 
3275         Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
3276         if (!NT_SUCCESS(Status)) {
3277             ERR("error - extend_file failed\n");
3278             goto end;
3279         }
3280     } else if ((uint64_t)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
3281         Status = STATUS_SUCCESS;
3282         goto end;
3283     }
3284 
3285     ccfs.AllocationSize = fcb->Header.AllocationSize;
3286     ccfs.FileSize = fcb->Header.FileSize;
3287     ccfs.ValidDataLength = fcb->Header.ValidDataLength;
3288     set_size = true;
3289 
3290     filter = FILE_NOTIFY_CHANGE_SIZE;
3291 
3292     if (!ccb->user_set_write_time) {
3293         KeQuerySystemTime(&time);
3294         win_time_to_unix(time, &fcb->inode_item.st_mtime);
3295         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3296     }
3297 
3298     fcb->inode_item_changed = true;
3299     mark_fcb_dirty(fcb);
3300     queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3301 
3302     Status = STATUS_SUCCESS;
3303 
3304 end:
3305     if (NT_SUCCESS(Status))
3306         clear_rollback(&rollback);
3307     else
3308         do_rollback(Vcb, &rollback);
3309 
3310     ExReleaseResourceLite(fcb->Header.Resource);
3311 
3312     if (set_size) {
3313         _SEH2_TRY {
3314             CcSetFileSizes(FileObject, &ccfs);
3315         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3316             Status = _SEH2_GetExceptionCode();
3317         } _SEH2_END;
3318 
3319         if (!NT_SUCCESS(Status))
3320             ERR("CcSetFileSizes threw exception %08x\n", Status);
3321     }
3322 
3323     ExReleaseResourceLite(&Vcb->tree_lock);
3324 
3325     return Status;
3326 }
3327 
3328 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
3329     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3330 
3331     TRACE("setting the position on %p to %I64x\n", FileObject, fpi->CurrentByteOffset.QuadPart);
3332 
3333     // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
3334 
3335     FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
3336 
3337     return STATUS_SUCCESS;
3338 }
3339 
3340 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
3341     FILE_LINK_INFORMATION_EX* fli = Irp->AssociatedIrp.SystemBuffer;
3342     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
3343     ccb* ccb = FileObject->FsContext2;
3344     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
3345     WCHAR* fn;
3346     ULONG fnlen, utf8len;
3347     UNICODE_STRING fnus;
3348     ANSI_STRING utf8;
3349     NTSTATUS Status;
3350     LARGE_INTEGER time;
3351     BTRFS_TIME now;
3352     LIST_ENTRY rollback;
3353     hardlink* hl;
3354     ACCESS_MASK access;
3355     SECURITY_SUBJECT_CONTEXT subjcont;
3356     dir_child* dc = NULL;
3357     ULONG flags;
3358 
3359     InitializeListHead(&rollback);
3360 
3361     // FIXME - check fli length
3362     // FIXME - don't ignore fli->RootDirectory
3363 
3364     if (ex)
3365         flags = fli->Flags;
3366     else
3367         flags = fli->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0;
3368 
3369     TRACE("flags = %x\n", flags);
3370     TRACE("RootDirectory = %p\n", fli->RootDirectory);
3371     TRACE("FileNameLength = %x\n", fli->FileNameLength);
3372     TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
3373 
3374     fn = fli->FileName;
3375     fnlen = fli->FileNameLength / sizeof(WCHAR);
3376 
3377     if (!tfo) {
3378         if (!fileref || !fileref->parent) {
3379             ERR("no fileref set and no directory given\n");
3380             return STATUS_INVALID_PARAMETER;
3381         }
3382 
3383         parfcb = fileref->parent->fcb;
3384         tfofcb = NULL;
3385     } else {
3386         LONG i;
3387 
3388         tfofcb = tfo->FsContext;
3389         parfcb = tfofcb;
3390 
3391         while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
3392             fnlen--;
3393 
3394         if (fnlen == 0)
3395             return STATUS_INVALID_PARAMETER;
3396 
3397         for (i = fnlen - 1; i >= 0; i--) {
3398             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
3399                 fn = &fli->FileName[i+1];
3400                 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
3401                 break;
3402             }
3403         }
3404     }
3405 
3406     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3407     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3408     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3409 
3410     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3411         WARN("tried to create hard link on directory\n");
3412         Status = STATUS_FILE_IS_A_DIRECTORY;
3413         goto end;
3414     }
3415 
3416     if (fcb->ads) {
3417         WARN("tried to create hard link on stream\n");
3418         Status = STATUS_INVALID_PARAMETER;
3419         goto end;
3420     }
3421 
3422     if (fcb->inode_item.st_nlink >= 65535) {
3423         Status = STATUS_TOO_MANY_LINKS;
3424         goto end;
3425     }
3426 
3427     fnus.Buffer = fn;
3428     fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR));
3429 
3430     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
3431 
3432     Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3433     if (!NT_SUCCESS(Status))
3434         goto end;
3435 
3436     utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
3437     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
3438     if (!utf8.Buffer) {
3439         ERR("out of memory\n");
3440         Status = STATUS_INSUFFICIENT_RESOURCES;
3441         goto end;
3442     }
3443 
3444     Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3445     if (!NT_SUCCESS(Status))
3446         goto end;
3447 
3448     if (tfo && tfo->FsContext2) {
3449         struct _ccb* relatedccb = tfo->FsContext2;
3450 
3451         related = relatedccb->fileref;
3452         increase_fileref_refcount(related);
3453     }
3454 
3455     Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3456 
3457     if (NT_SUCCESS(Status)) {
3458         if (!oldfileref->deleted) {
3459             WARN("destination file already exists\n");
3460 
3461             if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) {
3462                 Status = STATUS_OBJECT_NAME_COLLISION;
3463                 goto end;
3464             } else if (fileref == oldfileref) {
3465                 Status = STATUS_ACCESS_DENIED;
3466                 goto end;
3467             } else if (!(flags & FILE_LINK_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
3468                 WARN("trying to overwrite open file\n");
3469                 Status = STATUS_ACCESS_DENIED;
3470                 goto end;
3471             } else if (!(flags & FILE_LINK_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
3472                 WARN("trying to overwrite readonly file\n");
3473                 Status = STATUS_ACCESS_DENIED;
3474                 goto end;
3475             } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
3476                 WARN("trying to overwrite directory\n");
3477                 Status = STATUS_ACCESS_DENIED;
3478                 goto end;
3479             }
3480         } else {
3481             free_fileref(oldfileref);
3482             oldfileref = NULL;
3483         }
3484     }
3485 
3486     if (!related) {
3487         Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3488 
3489         if (!NT_SUCCESS(Status)) {
3490             ERR("open_fileref returned %08x\n", Status);
3491             goto end;
3492         }
3493     }
3494 
3495     SeCaptureSubjectContext(&subjcont);
3496 
3497     if (!SeAccessCheck(related->fcb->sd, &subjcont, false, FILE_ADD_FILE, 0, NULL,
3498                        IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3499         SeReleaseSubjectContext(&subjcont);
3500         TRACE("SeAccessCheck failed, returning %08x\n", Status);
3501         goto end;
3502     }
3503 
3504     SeReleaseSubjectContext(&subjcont);
3505 
3506     if (fcb->subvol != parfcb->subvol) {
3507         WARN("can't create hard link over subvolume boundary\n");
3508         Status = STATUS_INVALID_PARAMETER;
3509         goto end;
3510     }
3511 
3512     if (oldfileref) {
3513         SeCaptureSubjectContext(&subjcont);
3514 
3515         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL,
3516                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3517             SeReleaseSubjectContext(&subjcont);
3518             TRACE("SeAccessCheck failed, returning %08x\n", Status);
3519             goto end;
3520         }
3521 
3522         SeReleaseSubjectContext(&subjcont);
3523 
3524         if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
3525             oldfileref->delete_on_close = true;
3526             oldfileref->posix_delete = true;
3527         }
3528 
3529         Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
3530         if (!NT_SUCCESS(Status)) {
3531             ERR("delete_fileref returned %08x\n", Status);
3532             goto end;
3533         }
3534     }
3535 
3536     fr2 = create_fileref(Vcb);
3537 
3538     fr2->fcb = fcb;
3539     fcb->refcount++;
3540 
3541     fr2->created = true;
3542     fr2->parent = related;
3543 
3544     Status = add_dir_child(related->fcb, fcb->inode, false, &utf8, &fnus, fcb->type, &dc);
3545     if (!NT_SUCCESS(Status))
3546         WARN("add_dir_child returned %08x\n", Status);
3547 
3548     fr2->dc = dc;
3549     dc->fileref = fr2;
3550 
3551     ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
3552     InsertTailList(&related->children, &fr2->list_entry);
3553     ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
3554 
3555     // add hardlink for existing fileref, if it's not there already
3556     if (IsListEmpty(&fcb->hardlinks)) {
3557         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3558         if (!hl) {
3559             ERR("out of memory\n");
3560             Status = STATUS_INSUFFICIENT_RESOURCES;
3561             goto end;
3562         }
3563 
3564         hl->parent = fileref->parent->fcb->inode;
3565         hl->index = fileref->dc->index;
3566 
3567         hl->name.Length = hl->name.MaximumLength = fnus.Length;
3568         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
3569 
3570         if (!hl->name.Buffer) {
3571             ERR("out of memory\n");
3572             ExFreePool(hl);
3573             Status = STATUS_INSUFFICIENT_RESOURCES;
3574             goto end;
3575         }
3576 
3577         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3578 
3579         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
3580         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3581 
3582         if (!hl->utf8.Buffer) {
3583             ERR("out of memory\n");
3584             ExFreePool(hl->name.Buffer);
3585             ExFreePool(hl);
3586             Status = STATUS_INSUFFICIENT_RESOURCES;
3587             goto end;
3588         }
3589 
3590         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
3591 
3592         InsertTailList(&fcb->hardlinks, &hl->list_entry);
3593     }
3594 
3595     hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3596     if (!hl) {
3597         ERR("out of memory\n");
3598         Status = STATUS_INSUFFICIENT_RESOURCES;
3599         goto end;
3600     }
3601 
3602     hl->parent = related->fcb->inode;
3603     hl->index = dc->index;
3604 
3605     hl->name.Length = hl->name.MaximumLength = fnus.Length;
3606     hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
3607 
3608     if (!hl->name.Buffer) {
3609         ERR("out of memory\n");
3610         ExFreePool(hl);
3611         Status = STATUS_INSUFFICIENT_RESOURCES;
3612         goto end;
3613     }
3614 
3615     RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3616 
3617     hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
3618     hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3619 
3620     if (!hl->utf8.Buffer) {
3621         ERR("out of memory\n");
3622         ExFreePool(hl->name.Buffer);
3623         ExFreePool(hl);
3624         Status = STATUS_INSUFFICIENT_RESOURCES;
3625         goto end;
3626     }
3627 
3628     RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
3629     ExFreePool(utf8.Buffer);
3630 
3631     InsertTailList(&fcb->hardlinks, &hl->list_entry);
3632 
3633     mark_fileref_dirty(fr2);
3634     free_fileref(fr2);
3635 
3636     // update inode's INODE_ITEM
3637 
3638     KeQuerySystemTime(&time);
3639     win_time_to_unix(time, &now);
3640 
3641     fcb->inode_item.transid = Vcb->superblock.generation;
3642     fcb->inode_item.sequence++;
3643     fcb->inode_item.st_nlink++;
3644 
3645     if (!ccb->user_set_change_time)
3646         fcb->inode_item.st_ctime = now;
3647 
3648     fcb->inode_item_changed = true;
3649     mark_fcb_dirty(fcb);
3650 
3651     // update parent's INODE_ITEM
3652 
3653     parfcb->inode_item.transid = Vcb->superblock.generation;
3654     TRACE("parfcb->inode_item.st_size (inode %I64x) was %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3655     parfcb->inode_item.st_size += 2 * utf8len;
3656     TRACE("parfcb->inode_item.st_size (inode %I64x) now %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3657     parfcb->inode_item.sequence++;
3658     parfcb->inode_item.st_ctime = now;
3659 
3660     parfcb->inode_item_changed = true;
3661     mark_fcb_dirty(parfcb);
3662 
3663     send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3664 
3665     Status = STATUS_SUCCESS;
3666 
3667 end:
3668     if (oldfileref)
3669         free_fileref(oldfileref);
3670 
3671     if (!NT_SUCCESS(Status) && related)
3672         free_fileref(related);
3673 
3674     if (!NT_SUCCESS(Status) && fr2)
3675         free_fileref(fr2);
3676 
3677     if (NT_SUCCESS(Status))
3678         clear_rollback(&rollback);
3679     else
3680         do_rollback(Vcb, &rollback);
3681 
3682     ExReleaseResourceLite(fcb->Header.Resource);
3683     ExReleaseResourceLite(&Vcb->fileref_lock);
3684     ExReleaseResourceLite(&Vcb->tree_lock);
3685 
3686     return Status;
3687 }
3688 
3689 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
3690     FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
3691     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3692     fcb* fcb = FileObject->FsContext;
3693     ccb* ccb = FileObject->FsContext2;
3694     file_ref* fileref = ccb ? ccb->fileref : NULL;
3695     NTSTATUS Status;
3696     LARGE_INTEGER time;
3697     CC_FILE_SIZES ccfs;
3698     LIST_ENTRY rollback;
3699     bool set_size = false;
3700     ULONG filter;
3701 
3702     if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
3703         ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
3704         return STATUS_INVALID_PARAMETER;
3705     }
3706 
3707     if (!fileref) {
3708         ERR("fileref is NULL\n");
3709         return STATUS_INVALID_PARAMETER;
3710     }
3711 
3712     InitializeListHead(&rollback);
3713 
3714     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3715 
3716     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3717 
3718     if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
3719         Status = STATUS_INVALID_PARAMETER;
3720         goto end;
3721     }
3722 
3723     if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
3724         TRACE("invalid VDL of %I64u (current VDL = %I64u, file size = %I64u)\n", fvdli->ValidDataLength.QuadPart,
3725               fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
3726         Status = STATUS_INVALID_PARAMETER;
3727         goto end;
3728     }
3729 
3730     if (fileref ? fileref->deleted : fcb->deleted) {
3731         Status = STATUS_FILE_CLOSED;
3732         goto end;
3733     }
3734 
3735     // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
3736     // and we set it to the max anyway.
3737 
3738     ccfs.AllocationSize = fcb->Header.AllocationSize;
3739     ccfs.FileSize = fcb->Header.FileSize;
3740     ccfs.ValidDataLength = fvdli->ValidDataLength;
3741     set_size = true;
3742 
3743     filter = FILE_NOTIFY_CHANGE_SIZE;
3744 
3745     if (!ccb->user_set_write_time) {
3746         KeQuerySystemTime(&time);
3747         win_time_to_unix(time, &fcb->inode_item.st_mtime);
3748         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3749     }
3750 
3751     fcb->inode_item_changed = true;
3752     mark_fcb_dirty(fcb);
3753 
3754     queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3755 
3756     Status = STATUS_SUCCESS;
3757 
3758 end:
3759     if (NT_SUCCESS(Status))
3760         clear_rollback(&rollback);
3761     else
3762         do_rollback(Vcb, &rollback);
3763 
3764     ExReleaseResourceLite(fcb->Header.Resource);
3765 
3766     if (set_size) {
3767         _SEH2_TRY {
3768             CcSetFileSizes(FileObject, &ccfs);
3769         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3770             Status = _SEH2_GetExceptionCode();
3771         } _SEH2_END;
3772 
3773         if (!NT_SUCCESS(Status))
3774             ERR("CcSetFileSizes threw exception %08x\n", Status);
3775         else
3776             fcb->Header.AllocationSize = ccfs.AllocationSize;
3777     }
3778 
3779     ExReleaseResourceLite(&Vcb->tree_lock);
3780 
3781     return Status;
3782 }
3783 
3784 #ifndef __REACTOS__
3785 static NTSTATUS set_case_sensitive_information(PIRP Irp) {
3786     FILE_CASE_SENSITIVE_INFORMATION* fcsi = (FILE_CASE_SENSITIVE_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3787     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3788 
3789     if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_CASE_SENSITIVE_INFORMATION))
3790         return STATUS_INFO_LENGTH_MISMATCH;
3791 
3792     PFILE_OBJECT FileObject = IrpSp->FileObject;
3793 
3794     if (!FileObject)
3795         return STATUS_INVALID_PARAMETER;
3796 
3797     fcb* fcb = FileObject->FsContext;
3798 
3799     if (!fcb)
3800         return STATUS_INVALID_PARAMETER;
3801 
3802     if (!(fcb->atts & FILE_ATTRIBUTE_DIRECTORY)) {
3803         WARN("cannot set case-sensitive flag on anything other than directory\n");
3804         return STATUS_INVALID_PARAMETER;
3805     }
3806 
3807     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
3808 
3809     fcb->case_sensitive = fcsi->Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR;
3810     mark_fcb_dirty(fcb);
3811 
3812     ExReleaseResourceLite(&fcb->Vcb->tree_lock);
3813 
3814     return STATUS_SUCCESS;
3815 }
3816 #endif
3817 
3818 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
3819 _Function_class_(DRIVER_DISPATCH)
3820 NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3821     NTSTATUS Status;
3822     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3823     device_extension* Vcb = DeviceObject->DeviceExtension;
3824     fcb* fcb = IrpSp->FileObject->FsContext;
3825     ccb* ccb = IrpSp->FileObject->FsContext2;
3826     bool top_level;
3827 
3828     FsRtlEnterFileSystem();
3829 
3830     top_level = is_top_level(Irp);
3831 
3832     Irp->IoStatus.Information = 0;
3833 
3834     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3835         Status = vol_set_information(DeviceObject, Irp);
3836         goto end;
3837     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3838         Status = STATUS_INVALID_PARAMETER;
3839         goto end;
3840     }
3841 
3842     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
3843         Status = STATUS_ACCESS_DENIED;
3844         goto end;
3845     }
3846 
3847     if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
3848         Status = STATUS_MEDIA_WRITE_PROTECTED;
3849         goto end;
3850     }
3851 
3852     if (!fcb) {
3853         ERR("no fcb\n");
3854         Status = STATUS_INVALID_PARAMETER;
3855         goto end;
3856     }
3857 
3858     if (!ccb) {
3859         ERR("no ccb\n");
3860         Status = STATUS_INVALID_PARAMETER;
3861         goto end;
3862     }
3863 
3864     if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
3865 #ifndef __REACTOS__
3866         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformationEx))) {
3867 #else
3868         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
3869 #endif
3870         Status = STATUS_ACCESS_DENIED;
3871         goto end;
3872     }
3873 
3874     Status = STATUS_NOT_IMPLEMENTED;
3875 
3876     TRACE("set information\n");
3877 
3878     FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
3879 
3880     switch (IrpSp->Parameters.SetFile.FileInformationClass) {
3881         case FileAllocationInformation:
3882         {
3883             TRACE("FileAllocationInformation\n");
3884 
3885             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3886                 WARN("insufficient privileges\n");
3887                 Status = STATUS_ACCESS_DENIED;
3888                 break;
3889             }
3890 
3891             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, false, true);
3892             break;
3893         }
3894 
3895         case FileBasicInformation:
3896         {
3897             TRACE("FileBasicInformation\n");
3898 
3899             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
3900                 WARN("insufficient privileges\n");
3901                 Status = STATUS_ACCESS_DENIED;
3902                 break;
3903             }
3904 
3905             Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
3906 
3907             break;
3908         }
3909 
3910         case FileDispositionInformation:
3911         {
3912             TRACE("FileDispositionInformation\n");
3913 
3914             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
3915                 WARN("insufficient privileges\n");
3916                 Status = STATUS_ACCESS_DENIED;
3917                 break;
3918             }
3919 
3920             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, false);
3921 
3922             break;
3923         }
3924 
3925         case FileEndOfFileInformation:
3926         {
3927             TRACE("FileEndOfFileInformation\n");
3928 
3929             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3930                 WARN("insufficient privileges\n");
3931                 Status = STATUS_ACCESS_DENIED;
3932                 break;
3933             }
3934 
3935             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, false);
3936 
3937             break;
3938         }
3939 
3940         case FileLinkInformation:
3941             TRACE("FileLinkInformation\n");
3942             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
3943             break;
3944 
3945         case FilePositionInformation:
3946             TRACE("FilePositionInformation\n");
3947             Status = set_position_information(IrpSp->FileObject, Irp);
3948             break;
3949 
3950         case FileRenameInformation:
3951             TRACE("FileRenameInformation\n");
3952             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
3953             break;
3954 
3955         case FileValidDataLengthInformation:
3956         {
3957             TRACE("FileValidDataLengthInformation\n");
3958 
3959             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3960                 WARN("insufficient privileges\n");
3961                 Status = STATUS_ACCESS_DENIED;
3962                 break;
3963             }
3964 
3965             Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
3966 
3967             break;
3968         }
3969 
3970 #ifndef __REACTOS__
3971 #ifndef _MSC_VER
3972 #pragma GCC diagnostic push
3973 #pragma GCC diagnostic ignored "-Wswitch"
3974 #endif
3975         case FileDispositionInformationEx:
3976         {
3977             TRACE("FileDispositionInformationEx\n");
3978 
3979             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
3980                 WARN("insufficient privileges\n");
3981                 Status = STATUS_ACCESS_DENIED;
3982                 break;
3983             }
3984 
3985             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, true);
3986 
3987             break;
3988         }
3989 
3990         case FileRenameInformationEx:
3991             TRACE("FileRenameInformationEx\n");
3992             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
3993             break;
3994 
3995         case FileLinkInformationEx:
3996             TRACE("FileLinkInformationEx\n");
3997             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
3998             break;
3999 
4000         case FileCaseSensitiveInformation:
4001             TRACE("FileCaseSensitiveInformation\n");
4002 
4003             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
4004                 WARN("insufficient privileges\n");
4005                 Status = STATUS_ACCESS_DENIED;
4006                 break;
4007             }
4008 
4009             Status = set_case_sensitive_information(Irp);
4010             break;
4011 #ifndef _MSC_VER
4012 #pragma GCC diagnostic pop
4013 #endif
4014 #endif
4015 
4016         default:
4017             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
4018     }
4019 
4020 end:
4021     Irp->IoStatus.Status = Status;
4022 
4023     TRACE("returning %08x\n", Status);
4024 
4025     IoCompleteRequest(Irp, IO_NO_INCREMENT);
4026 
4027     if (top_level)
4028         IoSetTopLevelIrp(NULL);
4029 
4030     FsRtlExitFileSystem();
4031 
4032     return Status;
4033 }
4034 
4035 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
4036     RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
4037 
4038     *length -= sizeof(FILE_BASIC_INFORMATION);
4039 
4040     if (fcb == fcb->Vcb->dummy_fcb) {
4041         LARGE_INTEGER time;
4042 
4043         KeQuerySystemTime(&time);
4044         fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
4045     } else {
4046         fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4047         fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4048         fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4049         fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4050     }
4051 
4052     if (fcb->ads) {
4053         if (!fileref || !fileref->parent) {
4054             ERR("no fileref for stream\n");
4055             return STATUS_INTERNAL_ERROR;
4056         } else
4057             fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4058     } else
4059         fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4060 
4061     return STATUS_SUCCESS;
4062 }
4063 
4064 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
4065     INODE_ITEM* ii;
4066 
4067     if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
4068         WARN("overflow\n");
4069         return STATUS_BUFFER_OVERFLOW;
4070     }
4071 
4072     RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
4073 
4074     *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
4075 
4076     if (fcb->ads) {
4077         if (!fileref || !fileref->parent) {
4078             ERR("no fileref for stream\n");
4079             return STATUS_INTERNAL_ERROR;
4080         }
4081 
4082         ii = &fileref->parent->fcb->inode_item;
4083     } else
4084         ii = &fcb->inode_item;
4085 
4086     if (fcb == fcb->Vcb->dummy_fcb) {
4087         LARGE_INTEGER time;
4088 
4089         KeQuerySystemTime(&time);
4090         fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
4091     } else {
4092         fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4093         fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4094         fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4095         fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4096     }
4097 
4098     if (fcb->ads) {
4099         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
4100         fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4101     } else {
4102         fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4103         fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4104         fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4105     }
4106 
4107     return STATUS_SUCCESS;
4108 }
4109 
4110 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
4111     RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
4112 
4113     *length -= sizeof(FILE_STANDARD_INFORMATION);
4114 
4115     if (fcb->ads) {
4116         if (!fileref || !fileref->parent) {
4117             ERR("no fileref for stream\n");
4118             return STATUS_INTERNAL_ERROR;
4119         }
4120 
4121         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4122         fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
4123         fsi->Directory = false;
4124     } else {
4125         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4126         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4127         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4128         fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
4129     }
4130 
4131     TRACE("length = %I64u\n", fsi->EndOfFile.QuadPart);
4132 
4133     fsi->DeletePending = fileref ? fileref->delete_on_close : false;
4134 
4135     return STATUS_SUCCESS;
4136 }
4137 
4138 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
4139     *length -= sizeof(FILE_INTERNAL_INFORMATION);
4140 
4141     fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4142 
4143     return STATUS_SUCCESS;
4144 }
4145 
4146 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
4147     *length -= sizeof(FILE_EA_INFORMATION);
4148 
4149     /* This value appears to be the size of the structure NTFS stores on disk, and not,
4150      * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
4151      * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
4152      * item. */
4153 
4154     eai->EaSize = fcb->ealen;
4155 
4156     return STATUS_SUCCESS;
4157 }
4158 
4159 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
4160     RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
4161 
4162     *length -= sizeof(FILE_POSITION_INFORMATION);
4163 
4164     fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
4165 
4166     return STATUS_SUCCESS;
4167 }
4168 
4169 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
4170     file_ref* fr;
4171     NTSTATUS Status;
4172     ULONG reqlen = 0;
4173     USHORT offset;
4174     bool overflow = false;
4175 
4176     // FIXME - we need a lock on filerefs' filepart
4177 
4178     if (fileref == fileref->fcb->Vcb->root_fileref) {
4179         if (fn->MaximumLength >= sizeof(WCHAR)) {
4180             fn->Buffer[0] = '\\';
4181             fn->Length = sizeof(WCHAR);
4182 
4183             if (name_offset)
4184                 *name_offset = 0;
4185 
4186             return STATUS_SUCCESS;
4187         } else {
4188             if (preqlen)
4189                 *preqlen = sizeof(WCHAR);
4190             fn->Length = 0;
4191 
4192             return STATUS_BUFFER_OVERFLOW;
4193         }
4194     }
4195 
4196     fr = fileref;
4197     offset = 0;
4198 
4199     while (fr->parent) {
4200         USHORT movelen;
4201 
4202         if (!fr->dc)
4203             return STATUS_INTERNAL_ERROR;
4204 
4205         if (!overflow) {
4206             if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
4207                 overflow = true;
4208         }
4209 
4210         if (overflow)
4211             movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
4212         else
4213             movelen = fn->Length;
4214 
4215         if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
4216             RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
4217             offset += fr->dc->name.Length + sizeof(WCHAR);
4218         }
4219 
4220         if (fn->MaximumLength >= sizeof(WCHAR)) {
4221             fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
4222             fn->Length += sizeof(WCHAR);
4223 
4224             if (fn->MaximumLength > sizeof(WCHAR)) {
4225                 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
4226                 fn->Length += fr->dc->name.Length;
4227             }
4228 
4229             if (fn->Length > fn->MaximumLength) {
4230                 fn->Length = fn->MaximumLength;
4231                 overflow = true;
4232             }
4233         }
4234 
4235         reqlen += sizeof(WCHAR) + fr->dc->name.Length;
4236 
4237         fr = fr->parent;
4238     }
4239 
4240     offset += sizeof(WCHAR);
4241 
4242     if (overflow) {
4243         if (preqlen)
4244             *preqlen = reqlen;
4245         Status = STATUS_BUFFER_OVERFLOW;
4246     } else {
4247         if (name_offset)
4248             *name_offset = offset;
4249 
4250         Status = STATUS_SUCCESS;
4251     }
4252 
4253     return Status;
4254 }
4255 
4256 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
4257     ULONG reqlen;
4258     UNICODE_STRING fn;
4259     NTSTATUS Status;
4260     static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
4261     uint16_t datasuflen = sizeof(datasuf) - sizeof(WCHAR);
4262 
4263     if (!fileref) {
4264         ERR("called without fileref\n");
4265         return STATUS_INVALID_PARAMETER;
4266     }
4267 
4268     *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
4269 
4270     TRACE("maximum length is %u\n", *length);
4271     fni->FileNameLength = 0;
4272 
4273     fni->FileName[0] = 0;
4274 
4275     fn.Buffer = fni->FileName;
4276     fn.Length = 0;
4277     fn.MaximumLength = (uint16_t)*length;
4278 
4279     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
4280     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
4281         ERR("fileref_get_filename returned %08x\n", Status);
4282         return Status;
4283     }
4284 
4285     if (fcb->ads) {
4286         if (Status == STATUS_BUFFER_OVERFLOW)
4287             reqlen += datasuflen;
4288         else {
4289             if (fn.Length + datasuflen > fn.MaximumLength) {
4290                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
4291                 reqlen += datasuflen;
4292                 Status = STATUS_BUFFER_OVERFLOW;
4293             } else {
4294                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
4295                 fn.Length += datasuflen;
4296             }
4297         }
4298     }
4299 
4300     if (Status == STATUS_BUFFER_OVERFLOW) {
4301         *length = -1;
4302         fni->FileNameLength = reqlen;
4303         TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
4304     } else {
4305         *length -= fn.Length;
4306         fni->FileNameLength = fn.Length;
4307         TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
4308     }
4309 
4310     return Status;
4311 }
4312 
4313 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
4314     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
4315 
4316     if (fcb->ads) {
4317         if (!ccb->fileref || !ccb->fileref->parent) {
4318             ERR("no fileref for stream\n");
4319             return STATUS_INTERNAL_ERROR;
4320         }
4321 
4322         ati->FileAttributes = ccb->fileref->parent->fcb->atts;
4323     } else
4324         ati->FileAttributes = fcb->atts;
4325 
4326     if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4327         ati->ReparseTag = 0;
4328     else
4329         ati->ReparseTag = get_reparse_tag_fcb(fcb);
4330 
4331     return STATUS_SUCCESS;
4332 }
4333 
4334 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
4335     LONG reqsize;
4336     LIST_ENTRY* le;
4337     FILE_STREAM_INFORMATION *entry, *lastentry;
4338     NTSTATUS Status;
4339 
4340     static const WCHAR datasuf[] = L":$DATA";
4341     UNICODE_STRING suf;
4342 
4343     if (!fileref) {
4344         ERR("fileref was NULL\n");
4345         return STATUS_INVALID_PARAMETER;
4346     }
4347 
4348     suf.Buffer = (WCHAR*)datasuf;
4349     suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
4350 
4351     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
4352         reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
4353     else
4354         reqsize = 0;
4355 
4356     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, true);
4357 
4358     le = fileref->fcb->dir_children_index.Flink;
4359     while (le != &fileref->fcb->dir_children_index) {
4360         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4361 
4362         if (dc->index == 0) {
4363             reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
4364             reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
4365         } else
4366             break;
4367 
4368         le = le->Flink;
4369     }
4370 
4371     TRACE("length = %i, reqsize = %u\n", *length, reqsize);
4372 
4373     if (reqsize > *length) {
4374         Status = STATUS_BUFFER_OVERFLOW;
4375         goto end;
4376     }
4377 
4378     entry = fsi;
4379     lastentry = NULL;
4380 
4381     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
4382         ULONG off;
4383 
4384         entry->NextEntryOffset = 0;
4385         entry->StreamNameLength = suf.Length + sizeof(WCHAR);
4386         entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
4387         entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
4388 
4389         entry->StreamName[0] = ':';
4390         RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
4391 
4392         off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
4393 
4394         lastentry = entry;
4395         entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4396     }
4397 
4398     le = fileref->fcb->dir_children_index.Flink;
4399     while (le != &fileref->fcb->dir_children_index) {
4400         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4401 
4402         if (dc->index == 0) {
4403             ULONG off;
4404 
4405             entry->NextEntryOffset = 0;
4406             entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
4407 
4408             if (dc->fileref)
4409                 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
4410             else
4411                 entry->StreamSize.QuadPart = dc->size;
4412 
4413             entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
4414 
4415             entry->StreamName[0] = ':';
4416 
4417             RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
4418             RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
4419 
4420             if (lastentry)
4421                 lastentry->NextEntryOffset = (uint32_t)((uint8_t*)entry - (uint8_t*)lastentry);
4422 
4423             off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
4424 
4425             lastentry = entry;
4426             entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4427         } else
4428             break;
4429 
4430         le = le->Flink;
4431     }
4432 
4433     *length -= reqsize;
4434 
4435     Status = STATUS_SUCCESS;
4436 
4437 end:
4438     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
4439 
4440     return Status;
4441 }
4442 
4443 #ifndef __REACTOS__
4444 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
4445     TRACE("FileStandardLinkInformation\n");
4446 
4447     // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
4448 
4449     fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
4450     fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
4451     fsli->DeletePending = fileref ? fileref->delete_on_close : false;
4452     fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? true : false;
4453 
4454     *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
4455 
4456     return STATUS_SUCCESS;
4457 }
4458 
4459 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
4460     NTSTATUS Status;
4461     LIST_ENTRY* le;
4462     LONG bytes_needed;
4463     FILE_LINK_ENTRY_INFORMATION* feli;
4464     bool overflow = false;
4465     fcb* fcb = fileref->fcb;
4466     ULONG len;
4467 
4468     if (fcb->ads)
4469         return STATUS_INVALID_PARAMETER;
4470 
4471     if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
4472         return STATUS_INVALID_PARAMETER;
4473 
4474     RtlZeroMemory(fli, *length);
4475 
4476     bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
4477     len = bytes_needed;
4478     feli = NULL;
4479 
4480     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4481 
4482     if (fcb->inode == SUBVOL_ROOT_INODE) {
4483         ULONG namelen;
4484 
4485         if (fcb == fcb->Vcb->root_fileref->fcb)
4486             namelen = sizeof(WCHAR);
4487         else
4488             namelen = fileref->dc->name.Length;
4489 
4490         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
4491 
4492         if (bytes_needed > *length)
4493             overflow = true;
4494 
4495         if (!overflow) {
4496             feli = &fli->Entry;
4497 
4498             feli->NextEntryOffset = 0;
4499             feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
4500 
4501             if (fcb == fcb->Vcb->root_fileref->fcb) {
4502                 feli->FileNameLength = 1;
4503                 feli->FileName[0] = '.';
4504             } else {
4505                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4506                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4507             }
4508 
4509             fli->EntriesReturned++;
4510 
4511             len = bytes_needed;
4512         }
4513     } else {
4514         ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4515 
4516         if (IsListEmpty(&fcb->hardlinks)) {
4517             bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
4518 
4519             if (bytes_needed > *length)
4520                 overflow = true;
4521 
4522             if (!overflow) {
4523                 feli = &fli->Entry;
4524 
4525                 feli->NextEntryOffset = 0;
4526                 feli->ParentFileId = fileref->parent->fcb->inode;
4527                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4528                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4529 
4530                 fli->EntriesReturned++;
4531 
4532                 len = bytes_needed;
4533             }
4534         } else {
4535             le = fcb->hardlinks.Flink;
4536             while (le != &fcb->hardlinks) {
4537                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4538                 file_ref* parfr;
4539 
4540                 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
4541 
4542                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4543 
4544                 if (!NT_SUCCESS(Status)) {
4545                     ERR("open_fileref_by_inode returned %08x\n", Status);
4546                 } else if (!parfr->deleted) {
4547                     LIST_ENTRY* le2;
4548                     bool found = false, deleted = false;
4549                     UNICODE_STRING* fn = NULL;
4550 
4551                     le2 = parfr->children.Flink;
4552                     while (le2 != &parfr->children) {
4553                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4554 
4555                         if (fr2->dc->index == hl->index) {
4556                             found = true;
4557                             deleted = fr2->deleted;
4558 
4559                             if (!deleted)
4560                                 fn = &fr2->dc->name;
4561 
4562                             break;
4563                         }
4564 
4565                         le2 = le2->Flink;
4566                     }
4567 
4568                     if (!found)
4569                         fn = &hl->name;
4570 
4571                     if (!deleted) {
4572                         TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
4573 
4574                         if (feli)
4575                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
4576 
4577                         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
4578 
4579                         if (bytes_needed > *length)
4580                             overflow = true;
4581 
4582                         if (!overflow) {
4583                             if (feli) {
4584                                 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
4585                                 feli = (FILE_LINK_ENTRY_INFORMATION*)((uint8_t*)feli + feli->NextEntryOffset);
4586                             } else
4587                                 feli = &fli->Entry;
4588 
4589                             feli->NextEntryOffset = 0;
4590                             feli->ParentFileId = parfr->fcb->inode;
4591                             feli->FileNameLength = fn->Length / sizeof(WCHAR);
4592                             RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
4593 
4594                             fli->EntriesReturned++;
4595 
4596                             len = bytes_needed;
4597                         }
4598                     }
4599 
4600                     free_fileref(parfr);
4601                 }
4602 
4603                 le = le->Flink;
4604             }
4605         }
4606 
4607         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4608     }
4609 
4610     fli->BytesNeeded = bytes_needed;
4611 
4612     *length -= len;
4613 
4614     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4615 
4616     ExReleaseResourceLite(fcb->Header.Resource);
4617 
4618     return Status;
4619 }
4620 
4621 static NTSTATUS fill_in_hard_link_full_id_information(FILE_LINKS_FULL_ID_INFORMATION* flfii, file_ref* fileref, PIRP Irp, LONG* length) {
4622     NTSTATUS Status;
4623     LIST_ENTRY* le;
4624     LONG bytes_needed;
4625     FILE_LINK_ENTRY_FULL_ID_INFORMATION* flefii;
4626     bool overflow = false;
4627     fcb* fcb = fileref->fcb;
4628     ULONG len;
4629 
4630     if (fcb->ads)
4631         return STATUS_INVALID_PARAMETER;
4632 
4633     if (*length < (LONG)offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry))
4634         return STATUS_INVALID_PARAMETER;
4635 
4636     RtlZeroMemory(flfii, *length);
4637 
4638     bytes_needed = offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry);
4639     len = bytes_needed;
4640     flefii = NULL;
4641 
4642     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4643 
4644     if (fcb->inode == SUBVOL_ROOT_INODE) {
4645         ULONG namelen;
4646 
4647         if (fcb == fcb->Vcb->root_fileref->fcb)
4648             namelen = sizeof(WCHAR);
4649         else
4650             namelen = fileref->dc->name.Length;
4651 
4652         bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + namelen;
4653 
4654         if (bytes_needed > *length)
4655             overflow = true;
4656 
4657         if (!overflow) {
4658             flefii = &flfii->Entry;
4659 
4660             flefii->NextEntryOffset = 0;
4661 
4662             if (fcb == fcb->Vcb->root_fileref->fcb) {
4663                 RtlZeroMemory(&flefii->ParentFileId.Identifier[0], sizeof(FILE_ID_128));
4664                 flefii->FileNameLength = 1;
4665                 flefii->FileName[0] = '.';
4666             } else {
4667                 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4668                 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4669 
4670                 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4671                 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4672             }
4673 
4674             flfii->EntriesReturned++;
4675 
4676             len = bytes_needed;
4677         }
4678     } else {
4679         ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4680 
4681         if (IsListEmpty(&fcb->hardlinks)) {
4682             bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fileref->dc->name.Length;
4683 
4684             if (bytes_needed > *length)
4685                 overflow = true;
4686 
4687             if (!overflow) {
4688                 flefii = &flfii->Entry;
4689 
4690                 flefii->NextEntryOffset = 0;
4691 
4692                 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4693                 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4694 
4695                 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4696                 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4697 
4698                 flfii->EntriesReturned++;
4699 
4700                 len = bytes_needed;
4701             }
4702         } else {
4703             le = fcb->hardlinks.Flink;
4704             while (le != &fcb->hardlinks) {
4705                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4706                 file_ref* parfr;
4707 
4708                 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
4709 
4710                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4711 
4712                 if (!NT_SUCCESS(Status)) {
4713                     ERR("open_fileref_by_inode returned %08x\n", Status);
4714                 } else if (!parfr->deleted) {
4715                     LIST_ENTRY* le2;
4716                     bool found = false, deleted = false;
4717                     UNICODE_STRING* fn = NULL;
4718 
4719                     le2 = parfr->children.Flink;
4720                     while (le2 != &parfr->children) {
4721                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4722 
4723                         if (fr2->dc->index == hl->index) {
4724                             found = true;
4725                             deleted = fr2->deleted;
4726 
4727                             if (!deleted)
4728                                 fn = &fr2->dc->name;
4729 
4730                             break;
4731                         }
4732 
4733                         le2 = le2->Flink;
4734                     }
4735 
4736                     if (!found)
4737                         fn = &hl->name;
4738 
4739                     if (!deleted) {
4740                         TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
4741 
4742                         if (flefii)
4743                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
4744 
4745                         bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fn->Length;
4746 
4747                         if (bytes_needed > *length)
4748                             overflow = true;
4749 
4750                         if (!overflow) {
4751                             if (flefii) {
4752                                 flefii->NextEntryOffset = (ULONG)sector_align(offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + (flefii->FileNameLength * sizeof(WCHAR)), 8);
4753                                 flefii = (FILE_LINK_ENTRY_FULL_ID_INFORMATION*)((uint8_t*)flefii + flefii->NextEntryOffset);
4754                             } else
4755                                 flefii = &flfii->Entry;
4756 
4757                             flefii->NextEntryOffset = 0;
4758 
4759                             RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &parfr->fcb->inode, sizeof(uint64_t));
4760                             RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &parfr->fcb->subvol->id, sizeof(uint64_t));
4761 
4762                             flefii->FileNameLength = fn->Length / sizeof(WCHAR);
4763                             RtlCopyMemory(flefii->FileName, fn->Buffer, fn->Length);
4764 
4765                             flfii->EntriesReturned++;
4766 
4767                             len = bytes_needed;
4768                         }
4769                     }
4770 
4771                     free_fileref(parfr);
4772                 }
4773 
4774                 le = le->Flink;
4775             }
4776         }
4777 
4778         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4779     }
4780 
4781     flfii->BytesNeeded = bytes_needed;
4782 
4783     *length -= len;
4784 
4785     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4786 
4787     ExReleaseResourceLite(fcb->Header.Resource);
4788 
4789     return Status;
4790 }
4791 
4792 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
4793     RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(uint64_t));
4794     RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(uint64_t));
4795     RtlCopyMemory(&fii->FileId.Identifier[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
4796 
4797     *length -= sizeof(FILE_ID_INFORMATION);
4798 
4799     return STATUS_SUCCESS;
4800 }
4801 
4802 static NTSTATUS fill_in_file_stat_information(FILE_STAT_INFORMATION* fsi, fcb* fcb, ccb* ccb, LONG* length) {
4803     INODE_ITEM* ii;
4804 
4805     fsi->FileId.LowPart = (uint32_t)fcb->inode;
4806     fsi->FileId.HighPart = (uint32_t)fcb->subvol->id;
4807 
4808     if (fcb->ads)
4809         ii = &ccb->fileref->parent->fcb->inode_item;
4810     else
4811         ii = &fcb->inode_item;
4812 
4813     if (fcb == fcb->Vcb->dummy_fcb) {
4814         LARGE_INTEGER time;
4815 
4816         KeQuerySystemTime(&time);
4817         fsi->CreationTime = fsi->LastAccessTime = fsi->LastWriteTime = fsi->ChangeTime = time;
4818     } else {
4819         fsi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4820         fsi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4821         fsi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4822         fsi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4823     }
4824 
4825     if (fcb->ads) {
4826         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4827         fsi->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4828     } else {
4829         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4830         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4831         fsi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4832     }
4833 
4834     if (fcb->type == BTRFS_TYPE_SOCKET)
4835         fsi->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
4836     else if (fcb->type == BTRFS_TYPE_FIFO)
4837         fsi->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
4838     else if (fcb->type == BTRFS_TYPE_CHARDEV)
4839         fsi->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
4840     else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4841         fsi->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
4842     else if (!(fsi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4843         fsi->ReparseTag = 0;
4844     else
4845         fsi->ReparseTag = get_reparse_tag_fcb(fcb);
4846 
4847     if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4848         fsi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4849 
4850     if (fcb->ads)
4851         fsi->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4852     else
4853         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4854 
4855     fsi->EffectiveAccess = ccb->access;
4856 
4857     *length -= sizeof(FILE_STAT_INFORMATION);
4858 
4859     return STATUS_SUCCESS;
4860 }
4861 
4862 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
4863     INODE_ITEM* ii;
4864 
4865     fsli->FileId.LowPart = (uint32_t)fcb->inode;
4866     fsli->FileId.HighPart = (uint32_t)fcb->subvol->id;
4867 
4868     if (fcb->ads)
4869         ii = &ccb->fileref->parent->fcb->inode_item;
4870     else
4871         ii = &fcb->inode_item;
4872 
4873     if (fcb == fcb->Vcb->dummy_fcb) {
4874         LARGE_INTEGER time;
4875 
4876         KeQuerySystemTime(&time);
4877         fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
4878     } else {
4879         fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4880         fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4881         fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4882         fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4883     }
4884 
4885     if (fcb->ads) {
4886         fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
4887         fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4888     } else {
4889         fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4890         fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4891         fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4892     }
4893 
4894     if (fcb->type == BTRFS_TYPE_SOCKET)
4895         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
4896     else if (fcb->type == BTRFS_TYPE_FIFO)
4897         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
4898     else if (fcb->type == BTRFS_TYPE_CHARDEV)
4899         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
4900     else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4901         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
4902     else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4903         fsli->ReparseTag = 0;
4904     else
4905         fsli->ReparseTag = get_reparse_tag_fcb(fcb);
4906 
4907     if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4908         fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4909 
4910     if (fcb->ads)
4911         fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4912     else
4913         fsli->NumberOfLinks = fcb->inode_item.st_nlink;
4914 
4915     fsli->EffectiveAccess = ccb->access;
4916     fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID;
4917 
4918     if (fcb->case_sensitive)
4919         fsli->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR;
4920 
4921     fsli->LxUid = ii->st_uid;
4922     fsli->LxGid = ii->st_gid;
4923     fsli->LxMode = ii->st_mode;
4924 
4925     if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
4926         fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
4927         fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
4928     } else {
4929         fsli->LxDeviceIdMajor = 0;
4930         fsli->LxDeviceIdMinor = 0;
4931     }
4932 
4933     *length -= sizeof(FILE_STAT_LX_INFORMATION);
4934 
4935     return STATUS_SUCCESS;
4936 }
4937 
4938 static NTSTATUS fill_in_file_case_sensitive_information(FILE_CASE_SENSITIVE_INFORMATION* fcsi, fcb* fcb, LONG* length) {
4939     fcsi->Flags = fcb->case_sensitive ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
4940 
4941     *length -= sizeof(FILE_CASE_SENSITIVE_INFORMATION);
4942 
4943     return STATUS_SUCCESS;
4944 }
4945 #endif
4946 
4947 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
4948     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4949     LONG length = IrpSp->Parameters.QueryFile.Length;
4950     fcb* fcb = FileObject->FsContext;
4951     ccb* ccb = FileObject->FsContext2;
4952     file_ref* fileref = ccb ? ccb->fileref : NULL;
4953     NTSTATUS Status;
4954 
4955     TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
4956     TRACE("fcb = %p\n", fcb);
4957 
4958     if (fcb == Vcb->volume_fcb)
4959         return STATUS_INVALID_PARAMETER;
4960 
4961     if (!ccb) {
4962         ERR("ccb is NULL\n");
4963         return STATUS_INVALID_PARAMETER;
4964     }
4965 
4966     switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
4967         case FileAllInformation:
4968         {
4969             FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
4970             INODE_ITEM* ii;
4971 
4972             TRACE("FileAllInformation\n");
4973 
4974             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
4975                 WARN("insufficient privileges\n");
4976                 Status = STATUS_ACCESS_DENIED;
4977                 goto exit;
4978             }
4979 
4980             if (fcb->ads) {
4981                 if (!fileref || !fileref->parent) {
4982                     ERR("no fileref for stream\n");
4983                     Status = STATUS_INTERNAL_ERROR;
4984                     goto exit;
4985                 }
4986 
4987                 ii = &fileref->parent->fcb->inode_item;
4988             } else
4989                 ii = &fcb->inode_item;
4990 
4991             // Access, mode, and alignment are all filled in by the kernel
4992 
4993             if (length > 0)
4994                 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
4995 
4996             if (length > 0)
4997                 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
4998 
4999             if (length > 0)
5000                 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
5001 
5002             if (length > 0)
5003                 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
5004 
5005             length -= sizeof(FILE_ACCESS_INFORMATION);
5006 
5007             if (length > 0)
5008                 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
5009 
5010             length -= sizeof(FILE_MODE_INFORMATION);
5011 
5012             length -= sizeof(FILE_ALIGNMENT_INFORMATION);
5013 
5014             if (length > 0)
5015                 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
5016 
5017             Status = STATUS_SUCCESS;
5018 
5019             break;
5020         }
5021 
5022         case FileAttributeTagInformation:
5023         {
5024             FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
5025 
5026             TRACE("FileAttributeTagInformation\n");
5027 
5028             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5029                 WARN("insufficient privileges\n");
5030                 Status = STATUS_ACCESS_DENIED;
5031                 goto exit;
5032             }
5033 
5034             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5035             Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
5036             ExReleaseResourceLite(&Vcb->tree_lock);
5037 
5038             break;
5039         }
5040 
5041         case FileBasicInformation:
5042         {
5043             FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
5044             INODE_ITEM* ii;
5045 
5046             TRACE("FileBasicInformation\n");
5047 
5048             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5049                 WARN("insufficient privileges\n");
5050                 Status = STATUS_ACCESS_DENIED;
5051                 goto exit;
5052             }
5053 
5054             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
5055                 WARN("overflow\n");
5056                 Status = STATUS_BUFFER_OVERFLOW;
5057                 goto exit;
5058             }
5059 
5060             if (fcb->ads) {
5061                 if (!fileref || !fileref->parent) {
5062                     ERR("no fileref for stream\n");
5063                     Status = STATUS_INTERNAL_ERROR;
5064                     goto exit;
5065                 }
5066 
5067                 ii = &fileref->parent->fcb->inode_item;
5068             } else
5069                 ii = &fcb->inode_item;
5070 
5071             Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
5072             break;
5073         }
5074 
5075         case FileCompressionInformation:
5076             FIXME("STUB: FileCompressionInformation\n");
5077             Status = STATUS_INVALID_PARAMETER;
5078             goto exit;
5079 
5080         case FileEaInformation:
5081         {
5082             FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
5083 
5084             TRACE("FileEaInformation\n");
5085 
5086             Status = fill_in_file_ea_information(eai, fcb, &length);
5087 
5088             break;
5089         }
5090 
5091         case FileInternalInformation:
5092         {
5093             FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5094 
5095             TRACE("FileInternalInformation\n");
5096 
5097             Status = fill_in_file_internal_information(fii, fcb, &length);
5098 
5099             break;
5100         }
5101 
5102         case FileNameInformation:
5103         {
5104             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5105 
5106             TRACE("FileNameInformation\n");
5107 
5108             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5109 
5110             break;
5111         }
5112 
5113         case FileNetworkOpenInformation:
5114         {
5115             FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
5116 
5117             TRACE("FileNetworkOpenInformation\n");
5118 
5119             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5120                 WARN("insufficient privileges\n");
5121                 Status = STATUS_ACCESS_DENIED;
5122                 goto exit;
5123             }
5124 
5125             Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
5126 
5127             break;
5128         }
5129 
5130         case FilePositionInformation:
5131         {
5132             FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
5133 
5134             TRACE("FilePositionInformation\n");
5135 
5136             Status = fill_in_file_position_information(fpi, FileObject, &length);
5137 
5138             break;
5139         }
5140 
5141         case FileStandardInformation:
5142         {
5143             FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5144 
5145             TRACE("FileStandardInformation\n");
5146 
5147             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
5148                 WARN("overflow\n");
5149                 Status = STATUS_BUFFER_OVERFLOW;
5150                 goto exit;
5151             }
5152 
5153             Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
5154 
5155             break;
5156         }
5157 
5158         case FileStreamInformation:
5159         {
5160             FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5161 
5162             TRACE("FileStreamInformation\n");
5163 
5164             Status = fill_in_file_stream_information(fsi, fileref, &length);
5165 
5166             break;
5167         }
5168 
5169 #if (NTDDI_VERSION >= NTDDI_VISTA)
5170         case FileHardLinkInformation:
5171         {
5172             FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
5173 
5174             TRACE("FileHardLinkInformation\n");
5175 
5176             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5177             Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
5178             ExReleaseResourceLite(&Vcb->tree_lock);
5179 
5180             break;
5181         }
5182 
5183         case FileNormalizedNameInformation:
5184         {
5185             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5186 
5187             TRACE("FileNormalizedNameInformation\n");
5188 
5189             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5190 
5191             break;
5192         }
5193 
5194         case FileStandardLinkInformation:
5195         {
5196             FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5197 
5198             TRACE("FileStandardLinkInformation\n");
5199 
5200             Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
5201 
5202             break;
5203         }
5204 
5205         case FileRemoteProtocolInformation:
5206             TRACE("FileRemoteProtocolInformation\n");
5207             Status = STATUS_INVALID_PARAMETER;
5208             goto exit;
5209 
5210 #ifndef _MSC_VER
5211 #pragma GCC diagnostic push
5212 #pragma GCC diagnostic ignored "-Wswitch"
5213 #endif
5214         case FileIdInformation:
5215         {
5216             FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5217 
5218             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
5219                 WARN("overflow\n");
5220                 Status = STATUS_BUFFER_OVERFLOW;
5221                 goto exit;
5222             }
5223 
5224             TRACE("FileIdInformation\n");
5225 
5226             Status = fill_in_file_id_information(fii, fcb, &length);
5227 
5228             break;
5229         }
5230 
5231         case FileStatInformation:
5232         {
5233             FILE_STAT_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5234 
5235             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
5236                 WARN("overflow\n");
5237                 Status = STATUS_BUFFER_OVERFLOW;
5238                 goto exit;
5239             }
5240 
5241             TRACE("FileStatInformation\n");
5242 
5243             Status = fill_in_file_stat_information(fsi, fcb, ccb, &length);
5244 
5245             break;
5246         }
5247 
5248         case FileStatLxInformation:
5249         {
5250             FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5251 
5252             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
5253                 WARN("overflow\n");
5254                 Status = STATUS_BUFFER_OVERFLOW;
5255                 goto exit;
5256             }
5257 
5258             TRACE("FileStatLxInformation\n");
5259 
5260             Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
5261 
5262             break;
5263         }
5264 
5265         case FileCaseSensitiveInformation:
5266         {
5267             FILE_CASE_SENSITIVE_INFORMATION* fcsi = Irp->AssociatedIrp.SystemBuffer;
5268 
5269             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) {
5270                 WARN("overflow\n");
5271                 Status = STATUS_BUFFER_OVERFLOW;
5272                 goto exit;
5273             }
5274 
5275             TRACE("FileCaseSensitiveInformation\n");
5276 
5277             Status = fill_in_file_case_sensitive_information(fcsi, fcb, &length);
5278 
5279             break;
5280         }
5281 
5282         case FileHardLinkFullIdInformation:
5283         {
5284             FILE_LINKS_FULL_ID_INFORMATION* flfii = Irp->AssociatedIrp.SystemBuffer;
5285 
5286             TRACE("FileHardLinkFullIdInformation\n");
5287 
5288             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5289             Status = fill_in_hard_link_full_id_information(flfii, fileref, Irp, &length);
5290             ExReleaseResourceLite(&Vcb->tree_lock);
5291 
5292             break;
5293         }
5294 #ifndef _MSC_VER
5295 #pragma GCC diagnostic pop
5296 #endif
5297 #endif
5298 
5299         default:
5300             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
5301             Status = STATUS_INVALID_PARAMETER;
5302             goto exit;
5303     }
5304 
5305     if (length < 0) {
5306         length = 0;
5307         Status = STATUS_BUFFER_OVERFLOW;
5308     }
5309 
5310     Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
5311 
5312 exit:
5313     TRACE("query_info returning %08x\n", Status);
5314 
5315     return Status;
5316 }
5317 
5318 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
5319 _Function_class_(DRIVER_DISPATCH)
5320 NTSTATUS __stdcall drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5321     PIO_STACK_LOCATION IrpSp;
5322     NTSTATUS Status;
5323     fcb* fcb;
5324     device_extension* Vcb = DeviceObject->DeviceExtension;
5325     bool top_level;
5326 
5327     FsRtlEnterFileSystem();
5328 
5329     top_level = is_top_level(Irp);
5330 
5331     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5332         Status = vol_query_information(DeviceObject, Irp);
5333         goto end;
5334     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5335         Status = STATUS_INVALID_PARAMETER;
5336         goto end;
5337     }
5338 
5339     Irp->IoStatus.Information = 0;
5340 
5341     TRACE("query information\n");
5342 
5343     IrpSp = IoGetCurrentIrpStackLocation(Irp);
5344 
5345     fcb = IrpSp->FileObject->FsContext;
5346     TRACE("fcb = %p\n", fcb);
5347     TRACE("fcb->subvol = %p\n", fcb->subvol);
5348 
5349     Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
5350 
5351 end:
5352     TRACE("returning %08x\n", Status);
5353 
5354     Irp->IoStatus.Status = Status;
5355 
5356     IoCompleteRequest( Irp, IO_NO_INCREMENT );
5357 
5358     if (top_level)
5359         IoSetTopLevelIrp(NULL);
5360 
5361     FsRtlExitFileSystem();
5362 
5363     return Status;
5364 }
5365 
5366 _Dispatch_type_(IRP_MJ_QUERY_EA)
5367 _Function_class_(DRIVER_DISPATCH)
5368 NTSTATUS __stdcall drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5369     NTSTATUS Status;
5370     bool top_level;
5371     device_extension* Vcb = DeviceObject->DeviceExtension;
5372     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5373     PFILE_OBJECT FileObject = IrpSp->FileObject;
5374     fcb* fcb;
5375     ccb* ccb;
5376     FILE_FULL_EA_INFORMATION* ffei;
5377     ULONG retlen = 0;
5378 
5379     FsRtlEnterFileSystem();
5380 
5381     TRACE("(%p, %p)\n", DeviceObject, Irp);
5382 
5383     top_level = is_top_level(Irp);
5384 
5385     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5386         Status = vol_query_ea(DeviceObject, Irp);
5387         goto end;
5388     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5389         Status = STATUS_INVALID_PARAMETER;
5390         goto end;
5391     }
5392 
5393     ffei = map_user_buffer(Irp, NormalPagePriority);
5394     if (!ffei) {
5395         ERR("could not get output buffer\n");
5396         Status = STATUS_INVALID_PARAMETER;
5397         goto end;
5398     }
5399 
5400     if (!FileObject) {
5401         ERR("no file object\n");
5402         Status = STATUS_INVALID_PARAMETER;
5403         goto end;
5404     }
5405 
5406     fcb = FileObject->FsContext;
5407 
5408     if (!fcb) {
5409         ERR("no fcb\n");
5410         Status = STATUS_INVALID_PARAMETER;
5411         goto end;
5412     }
5413 
5414     ccb = FileObject->FsContext2;
5415 
5416     if (!ccb) {
5417         ERR("no ccb\n");
5418         Status = STATUS_INVALID_PARAMETER;
5419         goto end;
5420     }
5421 
5422     if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
5423         WARN("insufficient privileges\n");
5424         Status = STATUS_ACCESS_DENIED;
5425         goto end;
5426     }
5427 
5428     if (fcb->ads)
5429         fcb = ccb->fileref->parent->fcb;
5430 
5431     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
5432 
5433     Status = STATUS_SUCCESS;
5434 
5435     if (fcb->ea_xattr.Length == 0)
5436         goto end2;
5437 
5438     if (IrpSp->Parameters.QueryEa.EaList) {
5439         FILE_FULL_EA_INFORMATION *ea, *out;
5440         FILE_GET_EA_INFORMATION* in;
5441 
5442         in = IrpSp->Parameters.QueryEa.EaList;
5443         do {
5444             STRING s;
5445 
5446             s.Length = s.MaximumLength = in->EaNameLength;
5447             s.Buffer = in->EaName;
5448 
5449             RtlUpperString(&s, &s);
5450 
5451             if (in->NextEntryOffset == 0)
5452                 break;
5453 
5454             in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5455         } while (true);
5456 
5457         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5458         out = NULL;
5459 
5460         do {
5461             bool found = false;
5462 
5463             in = IrpSp->Parameters.QueryEa.EaList;
5464             do {
5465                 if (in->EaNameLength == ea->EaNameLength &&
5466                     RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
5467                     found = true;
5468                     break;
5469                 }
5470 
5471                 if (in->NextEntryOffset == 0)
5472                     break;
5473 
5474                 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5475             } while (true);
5476 
5477             if (found) {
5478                 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5479 
5480                 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5481                     Status = STATUS_BUFFER_OVERFLOW;
5482                     retlen = 0;
5483                     goto end2;
5484                 }
5485 
5486                 retlen += padding;
5487 
5488                 if (out) {
5489                     out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5490                     out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5491                 } else
5492                     out = ffei;
5493 
5494                 out->NextEntryOffset = 0;
5495                 out->Flags = ea->Flags;
5496                 out->EaNameLength = ea->EaNameLength;
5497                 out->EaValueLength = ea->EaValueLength;
5498                 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5499 
5500                 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5501 
5502                 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5503                     break;
5504             }
5505 
5506             if (ea->NextEntryOffset == 0)
5507                 break;
5508 
5509             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5510         } while (true);
5511     } else {
5512         FILE_FULL_EA_INFORMATION *ea, *out;
5513         ULONG index;
5514 
5515         if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
5516             // The index is 1-based
5517             if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
5518                 Status = STATUS_NONEXISTENT_EA_ENTRY;
5519                 goto end2;
5520             } else
5521                 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
5522         } else if (IrpSp->Flags & SL_RESTART_SCAN)
5523             index = ccb->ea_index = 0;
5524         else
5525             index = ccb->ea_index;
5526 
5527         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5528 
5529         if (index > 0) {
5530             ULONG i;
5531 
5532             for (i = 0; i < index; i++) {
5533                 if (ea->NextEntryOffset == 0) // last item
5534                     goto end2;
5535 
5536                 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5537             }
5538         }
5539 
5540         out = NULL;
5541 
5542         do {
5543             uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5544 
5545             if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5546                 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
5547                 goto end2;
5548             }
5549 
5550             retlen += padding;
5551 
5552             if (out) {
5553                 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5554                 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5555             } else
5556                 out = ffei;
5557 
5558             out->NextEntryOffset = 0;
5559             out->Flags = ea->Flags;
5560             out->EaNameLength = ea->EaNameLength;
5561             out->EaValueLength = ea->EaValueLength;
5562             RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5563 
5564             retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5565 
5566             if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
5567                 ccb->ea_index++;
5568 
5569             if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5570                 break;
5571 
5572             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5573         } while (true);
5574     }
5575 
5576 end2:
5577     ExReleaseResourceLite(fcb->Header.Resource);
5578 
5579 end:
5580     TRACE("returning %08x\n", Status);
5581 
5582     Irp->IoStatus.Status = Status;
5583     Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
5584 
5585     IoCompleteRequest( Irp, IO_NO_INCREMENT );
5586 
5587     if (top_level)
5588         IoSetTopLevelIrp(NULL);
5589 
5590     FsRtlExitFileSystem();
5591 
5592     return Status;
5593 }
5594 
5595 _Dispatch_type_(IRP_MJ_SET_EA)
5596 _Function_class_(DRIVER_DISPATCH)
5597 NTSTATUS __stdcall drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5598     device_extension* Vcb = DeviceObject->DeviceExtension;
5599     NTSTATUS Status;
5600     bool top_level;
5601     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5602     PFILE_OBJECT FileObject = IrpSp->FileObject;
5603     fcb* fcb;
5604     ccb* ccb;
5605     file_ref* fileref;
5606     FILE_FULL_EA_INFORMATION* ffei;
5607     ULONG offset;
5608     LIST_ENTRY ealist;
5609     ea_item* item;
5610     FILE_FULL_EA_INFORMATION* ea;
5611     LIST_ENTRY* le;
5612     LARGE_INTEGER time;
5613     BTRFS_TIME now;
5614 
5615     FsRtlEnterFileSystem();
5616 
5617     TRACE("(%p, %p)\n", DeviceObject, Irp);
5618 
5619     top_level = is_top_level(Irp);
5620 
5621     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5622         Status = vol_set_ea(DeviceObject, Irp);
5623         goto end;
5624     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5625         Status = STATUS_INVALID_PARAMETER;
5626         goto end;
5627     }
5628 
5629     if (Vcb->readonly) {
5630         Status = STATUS_MEDIA_WRITE_PROTECTED;
5631         goto end;
5632     }
5633 
5634     ffei = map_user_buffer(Irp, NormalPagePriority);
5635     if (!ffei) {
5636         ERR("could not get output buffer\n");
5637         Status = STATUS_INVALID_PARAMETER;
5638         goto end;
5639     }
5640 
5641     Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
5642     if (!NT_SUCCESS(Status)) {
5643         ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
5644         goto end;
5645     }
5646 
5647     if (!FileObject) {
5648         ERR("no file object\n");
5649         Status = STATUS_INVALID_PARAMETER;
5650         goto end;
5651     }
5652 
5653     fcb = FileObject->FsContext;
5654 
5655     if (!fcb) {
5656         ERR("no fcb\n");
5657         Status = STATUS_INVALID_PARAMETER;
5658         goto end;
5659     }
5660 
5661     ccb = FileObject->FsContext2;
5662 
5663     if (!ccb) {
5664         ERR("no ccb\n");
5665         Status = STATUS_INVALID_PARAMETER;
5666         goto end;
5667     }
5668 
5669     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
5670         WARN("insufficient privileges\n");
5671         Status = STATUS_ACCESS_DENIED;
5672         goto end;
5673     }
5674 
5675     if (fcb->ads) {
5676         fileref = ccb->fileref->parent;
5677         fcb = fileref->fcb;
5678     } else
5679         fileref = ccb->fileref;
5680 
5681     InitializeListHead(&ealist);
5682 
5683     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
5684 
5685     if (fcb->ea_xattr.Length > 0) {
5686         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5687 
5688         do {
5689             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5690             if (!item) {
5691                 ERR("out of memory\n");
5692                 Status = STATUS_INSUFFICIENT_RESOURCES;
5693                 goto end2;
5694             }
5695 
5696             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5697             item->name.Buffer = ea->EaName;
5698 
5699             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5700             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5701 
5702             item->flags = ea->Flags;
5703 
5704             InsertTailList(&ealist, &item->list_entry);
5705 
5706             if (ea->NextEntryOffset == 0)
5707                 break;
5708 
5709             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5710         } while (true);
5711     }
5712 
5713     ea = ffei;
5714 
5715     do {
5716         STRING s;
5717         bool found = false;
5718 
5719         s.Length = s.MaximumLength = ea->EaNameLength;
5720         s.Buffer = ea->EaName;
5721 
5722         RtlUpperString(&s, &s);
5723 
5724         le = ealist.Flink;
5725         while (le != &ealist) {
5726             item = CONTAINING_RECORD(le, ea_item, list_entry);
5727 
5728             if (item->name.Length == s.Length &&
5729                 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
5730                 item->flags = ea->Flags;
5731                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5732                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5733                 found = true;
5734                 break;
5735             }
5736 
5737             le = le->Flink;
5738         }
5739 
5740         if (!found) {
5741             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5742             if (!item) {
5743                 ERR("out of memory\n");
5744                 Status = STATUS_INSUFFICIENT_RESOURCES;
5745                 goto end2;
5746             }
5747 
5748             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5749             item->name.Buffer = ea->EaName;
5750 
5751             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5752             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5753 
5754             item->flags = ea->Flags;
5755 
5756             InsertTailList(&ealist, &item->list_entry);
5757         }
5758 
5759         if (ea->NextEntryOffset == 0)
5760             break;
5761 
5762         ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5763     } while (true);
5764 
5765     // remove entries with zero-length value
5766     le = ealist.Flink;
5767     while (le != &ealist) {
5768         LIST_ENTRY* le2 = le->Flink;
5769 
5770         item = CONTAINING_RECORD(le, ea_item, list_entry);
5771 
5772         if (item->value.Length == 0) {
5773             RemoveEntryList(&item->list_entry);
5774             ExFreePool(item);
5775         }
5776 
5777         le = le2;
5778     }
5779 
5780     // handle LXSS values
5781     le = ealist.Flink;
5782     while (le != &ealist) {
5783         LIST_ENTRY* le2 = le->Flink;
5784 
5785         item = CONTAINING_RECORD(le, ea_item, list_entry);
5786 
5787         if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
5788             if (item->value.Length < sizeof(uint32_t)) {
5789                 ERR("uid value was shorter than expected\n");
5790                 Status = STATUS_INVALID_PARAMETER;
5791                 goto end2;
5792             }
5793 
5794             if (Irp->RequestorMode == KernelMode) {
5795                 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t));
5796                 fcb->sd_dirty = true;
5797                 fcb->sd_deleted = false;
5798             }
5799 
5800             RemoveEntryList(&item->list_entry);
5801             ExFreePool(item);
5802         } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
5803             if (item->value.Length < sizeof(uint32_t)) {
5804                 ERR("gid value was shorter than expected\n");
5805                 Status = STATUS_INVALID_PARAMETER;
5806                 goto end2;
5807             }
5808 
5809             if (Irp->RequestorMode == KernelMode)
5810                 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
5811 
5812             RemoveEntryList(&item->list_entry);
5813             ExFreePool(item);
5814         } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
5815             if (item->value.Length < sizeof(uint32_t)) {
5816                 ERR("mode value was shorter than expected\n");
5817                 Status = STATUS_INVALID_PARAMETER;
5818                 goto end2;
5819             }
5820 
5821             if (Irp->RequestorMode == KernelMode) {
5822                 uint32_t 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;
5823                 uint32_t val;
5824 
5825                 RtlCopyMemory(&val, item->value.Buffer, sizeof(uint32_t));
5826 
5827                 fcb->inode_item.st_mode &= ~allowed;
5828                 fcb->inode_item.st_mode |= val & allowed;
5829             }
5830 
5831             RemoveEntryList(&item->list_entry);
5832             ExFreePool(item);
5833         }
5834 
5835         le = le2;
5836     }
5837 
5838     if (IsListEmpty(&ealist)) {
5839         fcb->ealen = 0;
5840 
5841         if (fcb->ea_xattr.Buffer)
5842             ExFreePool(fcb->ea_xattr.Buffer);
5843 
5844         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
5845         fcb->ea_xattr.Buffer = NULL;
5846     } else {
5847         uint16_t size = 0;
5848         char *buf, *oldbuf;
5849 
5850         le = ealist.Flink;
5851         while (le != &ealist) {
5852             item = CONTAINING_RECORD(le, ea_item, list_entry);
5853 
5854             if (size % 4 > 0)
5855                 size += 4 - (size % 4);
5856 
5857             size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
5858 
5859             le = le->Flink;
5860         }
5861 
5862         buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
5863         if (!buf) {
5864             ERR("out of memory\n");
5865             Status = STATUS_INSUFFICIENT_RESOURCES;
5866             goto end2;
5867         }
5868 
5869         oldbuf = fcb->ea_xattr.Buffer;
5870 
5871         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
5872         fcb->ea_xattr.Buffer = buf;
5873 
5874         fcb->ealen = 4;
5875         ea = NULL;
5876 
5877         le = ealist.Flink;
5878         while (le != &ealist) {
5879             item = CONTAINING_RECORD(le, ea_item, list_entry);
5880 
5881             if (ea) {
5882                 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
5883 
5884                 if (ea->NextEntryOffset % 4 > 0)
5885                     ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
5886 
5887                 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5888             } else
5889                 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5890 
5891             ea->NextEntryOffset = 0;
5892             ea->Flags = item->flags;
5893             ea->EaNameLength = (UCHAR)item->name.Length;
5894             ea->EaValueLength = item->value.Length;
5895 
5896             RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
5897             ea->EaName[item->name.Length] = 0;
5898             RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
5899 
5900             fcb->ealen += 5 + item->name.Length + item->value.Length;
5901 
5902             le = le->Flink;
5903         }
5904 
5905         if (oldbuf)
5906             ExFreePool(oldbuf);
5907     }
5908 
5909     fcb->ea_changed = true;
5910 
5911     KeQuerySystemTime(&time);
5912     win_time_to_unix(time, &now);
5913 
5914     fcb->inode_item.transid = Vcb->superblock.generation;
5915     fcb->inode_item.sequence++;
5916 
5917     if (!ccb->user_set_change_time)
5918         fcb->inode_item.st_ctime = now;
5919 
5920     fcb->inode_item_changed = true;
5921     mark_fcb_dirty(fcb);
5922 
5923     send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
5924 
5925     Status = STATUS_SUCCESS;
5926 
5927 end2:
5928     ExReleaseResourceLite(fcb->Header.Resource);
5929 
5930     while (!IsListEmpty(&ealist)) {
5931         le = RemoveHeadList(&ealist);
5932 
5933         item = CONTAINING_RECORD(le, ea_item, list_entry);
5934 
5935         ExFreePool(item);
5936     }
5937 
5938 end:
5939     TRACE("returning %08x\n", Status);
5940 
5941     Irp->IoStatus.Status = Status;
5942     Irp->IoStatus.Information = 0;
5943 
5944     IoCompleteRequest(Irp, IO_NO_INCREMENT);
5945 
5946     if (top_level)
5947         IoSetTopLevelIrp(NULL);
5948 
5949     FsRtlExitFileSystem();
5950 
5951     return Status;
5952 }
5953