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 #include "crc32c.h"
20 
21 #if (NTDDI_VERSION >= NTDDI_WIN10)
22 // not currently in mingw - introduced with Windows 10
23 #ifndef _MSC_VER
24 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
25 #define FileHardLinkFullIdInformation (enum _FILE_INFORMATION_CLASS)62
26 #define FileDispositionInformationEx (enum _FILE_INFORMATION_CLASS)64
27 #define FileRenameInformationEx (enum _FILE_INFORMATION_CLASS)65
28 #define FileStatInformation (enum _FILE_INFORMATION_CLASS)68
29 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
30 #define FileCaseSensitiveInformation (enum _FILE_INFORMATION_CLASS)71
31 #define FileLinkInformationEx (enum _FILE_INFORMATION_CLASS)72
32 #define FileStorageReserveIdInformation (enum _FILE_INFORMATION_CLASS)74
33 
34 typedef struct _FILE_ID_INFORMATION {
35     ULONGLONG VolumeSerialNumber;
36     FILE_ID_128 FileId;
37 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
38 
39 typedef struct _FILE_STAT_INFORMATION {
40     LARGE_INTEGER FileId;
41     LARGE_INTEGER CreationTime;
42     LARGE_INTEGER LastAccessTime;
43     LARGE_INTEGER LastWriteTime;
44     LARGE_INTEGER ChangeTime;
45     LARGE_INTEGER AllocationSize;
46     LARGE_INTEGER EndOfFile;
47     ULONG FileAttributes;
48     ULONG ReparseTag;
49     ULONG NumberOfLinks;
50     ACCESS_MASK EffectiveAccess;
51 } FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION;
52 
53 typedef struct _FILE_STAT_LX_INFORMATION {
54     LARGE_INTEGER FileId;
55     LARGE_INTEGER CreationTime;
56     LARGE_INTEGER LastAccessTime;
57     LARGE_INTEGER LastWriteTime;
58     LARGE_INTEGER ChangeTime;
59     LARGE_INTEGER AllocationSize;
60     LARGE_INTEGER EndOfFile;
61     ULONG         FileAttributes;
62     ULONG         ReparseTag;
63     ULONG         NumberOfLinks;
64     ACCESS_MASK   EffectiveAccess;
65     ULONG         LxFlags;
66     ULONG         LxUid;
67     ULONG         LxGid;
68     ULONG         LxMode;
69     ULONG         LxDeviceIdMajor;
70     ULONG         LxDeviceIdMinor;
71 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
72 
73 #define LX_FILE_METADATA_HAS_UID        0x01
74 #define LX_FILE_METADATA_HAS_GID        0x02
75 #define LX_FILE_METADATA_HAS_MODE       0x04
76 #define LX_FILE_METADATA_HAS_DEVICE_ID  0x08
77 #define LX_FILE_CASE_SENSITIVE_DIR      0x10
78 
79 typedef struct _FILE_RENAME_INFORMATION_EX {
80     union {
81         BOOLEAN ReplaceIfExists;
82         ULONG Flags;
83     };
84     HANDLE RootDirectory;
85     ULONG FileNameLength;
86     WCHAR FileName[1];
87 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
88 
89 typedef struct _FILE_DISPOSITION_INFORMATION_EX {
90     ULONG Flags;
91 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
92 
93 typedef struct _FILE_LINK_INFORMATION_EX {
94     union {
95         BOOLEAN ReplaceIfExists;
96         ULONG Flags;
97     };
98     HANDLE RootDirectory;
99     ULONG FileNameLength;
100     WCHAR FileName[1];
101 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
102 
103 typedef struct _FILE_CASE_SENSITIVE_INFORMATION {
104     ULONG Flags;
105 } FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION;
106 
107 typedef struct _FILE_LINK_ENTRY_FULL_ID_INFORMATION {
108     ULONG NextEntryOffset;
109     FILE_ID_128 ParentFileId;
110     ULONG FileNameLength;
111     WCHAR FileName[1];
112 } FILE_LINK_ENTRY_FULL_ID_INFORMATION, *PFILE_LINK_ENTRY_FULL_ID_INFORMATION;
113 
114 typedef struct _FILE_LINKS_FULL_ID_INFORMATION {
115     ULONG BytesNeeded;
116     ULONG EntriesReturned;
117     FILE_LINK_ENTRY_FULL_ID_INFORMATION Entry;
118 } FILE_LINKS_FULL_ID_INFORMATION, *PFILE_LINKS_FULL_ID_INFORMATION;
119 
120 #define FILE_RENAME_REPLACE_IF_EXISTS                       0x001
121 #define FILE_RENAME_POSIX_SEMANTICS                         0x002
122 #define FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE          0x004
123 #define FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
124 #define FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE             0x010
125 #define FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE             0x020
126 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE               0x040
127 #define FILE_RENAME_FORCE_RESIZE_TARGET_SR                  0x080
128 #define FILE_RENAME_FORCE_RESIZE_SOURCE_SR                  0x100
129 
130 #define FILE_DISPOSITION_DELETE                         0x1
131 #define FILE_DISPOSITION_POSIX_SEMANTICS                0x2
132 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK      0x4
133 #define FILE_DISPOSITION_ON_CLOSE                       0x8
134 
135 #define FILE_LINK_REPLACE_IF_EXISTS                       0x001
136 #define FILE_LINK_POSIX_SEMANTICS                         0x002
137 #define FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
138 #define FILE_LINK_NO_INCREASE_AVAILABLE_SPACE             0x010
139 #define FILE_LINK_NO_DECREASE_AVAILABLE_SPACE             0x020
140 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE               0x040
141 #define FILE_LINK_FORCE_RESIZE_TARGET_SR                  0x080
142 #define FILE_LINK_FORCE_RESIZE_SOURCE_SR                  0x100
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 = %lx\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 = %lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 returned %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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", (int)(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 %08lx\n", Status);
1881                     goto end;
1882                 }
1883             }
1884         } else {
1885             ERR("open_fileref_child returned %08lx\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 %08lx\n", Status);
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", (int)(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 %08lx\n", Status);
2120                     goto end;
2121                 }
2122             }
2123         } else {
2124             ERR("open_fileref_child returned %08lx\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 %08lx\n", Status);
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 %08lx\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 %08lx\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 = %lx\n", flags);
2484     TRACE("RootDirectory = %p\n", fri->RootDirectory);
2485     TRACE("FileName = %.*S\n", (int)(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 %08lx\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 %08lx\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", (int)(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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer, (int)(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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %x 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 %x bytes\n", end);
3123 
3124         fcb->adsdata.Length = end;
3125     } else if (end > fcb->adsdata.Length) {
3126         TRACE("extending stream to %x bytes\n", end);
3127 
3128         if (end > fcb->adsmaxlen) {
3129             ERR("error - xattr too long (%u > %lu)\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         TRACE("extending file to %I64x bytes\n", feofi->EndOfFile.QuadPart);
3268 
3269         Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
3270         if (!NT_SUCCESS(Status)) {
3271             ERR("error - extend_file failed\n");
3272             goto end;
3273         }
3274     } else if ((uint64_t)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
3275         Status = STATUS_SUCCESS;
3276         goto end;
3277     }
3278 
3279     ccfs.AllocationSize = fcb->Header.AllocationSize;
3280     ccfs.FileSize = fcb->Header.FileSize;
3281     ccfs.ValidDataLength = fcb->Header.ValidDataLength;
3282     set_size = true;
3283 
3284     filter = FILE_NOTIFY_CHANGE_SIZE;
3285 
3286     if (!ccb->user_set_write_time) {
3287         KeQuerySystemTime(&time);
3288         win_time_to_unix(time, &fcb->inode_item.st_mtime);
3289         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3290     }
3291 
3292     fcb->inode_item_changed = true;
3293     mark_fcb_dirty(fcb);
3294     queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3295 
3296     Status = STATUS_SUCCESS;
3297 
3298 end:
3299     if (NT_SUCCESS(Status))
3300         clear_rollback(&rollback);
3301     else
3302         do_rollback(Vcb, &rollback);
3303 
3304     ExReleaseResourceLite(fcb->Header.Resource);
3305 
3306     if (set_size) {
3307         _SEH2_TRY {
3308             CcSetFileSizes(FileObject, &ccfs);
3309         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3310             Status = _SEH2_GetExceptionCode();
3311         } _SEH2_END;
3312 
3313         if (!NT_SUCCESS(Status))
3314             ERR("CcSetFileSizes threw exception %08lx\n", Status);
3315     }
3316 
3317     ExReleaseResourceLite(&Vcb->tree_lock);
3318 
3319     return Status;
3320 }
3321 
3322 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
3323     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3324 
3325     TRACE("setting the position on %p to %I64x\n", FileObject, fpi->CurrentByteOffset.QuadPart);
3326 
3327     // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
3328 
3329     FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
3330 
3331     return STATUS_SUCCESS;
3332 }
3333 
3334 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
3335     FILE_LINK_INFORMATION_EX* fli = Irp->AssociatedIrp.SystemBuffer;
3336     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
3337     ccb* ccb = FileObject->FsContext2;
3338     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
3339     WCHAR* fn;
3340     ULONG fnlen, utf8len;
3341     UNICODE_STRING fnus;
3342     ANSI_STRING utf8;
3343     NTSTATUS Status;
3344     LARGE_INTEGER time;
3345     BTRFS_TIME now;
3346     LIST_ENTRY rollback;
3347     hardlink* hl;
3348     ACCESS_MASK access;
3349     SECURITY_SUBJECT_CONTEXT subjcont;
3350     dir_child* dc = NULL;
3351     ULONG flags;
3352 
3353     InitializeListHead(&rollback);
3354 
3355     // FIXME - check fli length
3356     // FIXME - don't ignore fli->RootDirectory
3357 
3358     if (ex)
3359         flags = fli->Flags;
3360     else
3361         flags = fli->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0;
3362 
3363     TRACE("flags = %lx\n", flags);
3364     TRACE("RootDirectory = %p\n", fli->RootDirectory);
3365     TRACE("FileNameLength = %lx\n", fli->FileNameLength);
3366     TRACE("FileName = %.*S\n", (int)(fli->FileNameLength / sizeof(WCHAR)), fli->FileName);
3367 
3368     fn = fli->FileName;
3369     fnlen = fli->FileNameLength / sizeof(WCHAR);
3370 
3371     if (!tfo) {
3372         if (!fileref || !fileref->parent) {
3373             ERR("no fileref set and no directory given\n");
3374             return STATUS_INVALID_PARAMETER;
3375         }
3376 
3377         parfcb = fileref->parent->fcb;
3378         tfofcb = NULL;
3379     } else {
3380         LONG i;
3381 
3382         tfofcb = tfo->FsContext;
3383         parfcb = tfofcb;
3384 
3385         while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
3386             fnlen--;
3387 
3388         if (fnlen == 0)
3389             return STATUS_INVALID_PARAMETER;
3390 
3391         for (i = fnlen - 1; i >= 0; i--) {
3392             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
3393                 fn = &fli->FileName[i+1];
3394                 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
3395                 break;
3396             }
3397         }
3398     }
3399 
3400     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3401     ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3402     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3403 
3404     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3405         WARN("tried to create hard link on directory\n");
3406         Status = STATUS_FILE_IS_A_DIRECTORY;
3407         goto end;
3408     }
3409 
3410     if (fcb->ads) {
3411         WARN("tried to create hard link on stream\n");
3412         Status = STATUS_INVALID_PARAMETER;
3413         goto end;
3414     }
3415 
3416     if (fcb->inode_item.st_nlink >= 65535) {
3417         Status = STATUS_TOO_MANY_LINKS;
3418         goto end;
3419     }
3420 
3421     fnus.Buffer = fn;
3422     fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR));
3423 
3424     TRACE("fnus = %.*S\n", (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer);
3425 
3426     Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3427     if (!NT_SUCCESS(Status))
3428         goto end;
3429 
3430     utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
3431     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
3432     if (!utf8.Buffer) {
3433         ERR("out of memory\n");
3434         Status = STATUS_INSUFFICIENT_RESOURCES;
3435         goto end;
3436     }
3437 
3438     Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3439     if (!NT_SUCCESS(Status))
3440         goto end;
3441 
3442     if (tfo && tfo->FsContext2) {
3443         struct _ccb* relatedccb = tfo->FsContext2;
3444 
3445         related = relatedccb->fileref;
3446         increase_fileref_refcount(related);
3447     }
3448 
3449     Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3450 
3451     if (NT_SUCCESS(Status)) {
3452         if (!oldfileref->deleted) {
3453             WARN("destination file already exists\n");
3454 
3455             if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) {
3456                 Status = STATUS_OBJECT_NAME_COLLISION;
3457                 goto end;
3458             } else if (fileref == oldfileref) {
3459                 Status = STATUS_ACCESS_DENIED;
3460                 goto end;
3461             } else if (!(flags & FILE_LINK_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
3462                 WARN("trying to overwrite open file\n");
3463                 Status = STATUS_ACCESS_DENIED;
3464                 goto end;
3465             } else if (!(flags & FILE_LINK_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
3466                 WARN("trying to overwrite readonly file\n");
3467                 Status = STATUS_ACCESS_DENIED;
3468                 goto end;
3469             } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
3470                 WARN("trying to overwrite directory\n");
3471                 Status = STATUS_ACCESS_DENIED;
3472                 goto end;
3473             }
3474         } else {
3475             free_fileref(oldfileref);
3476             oldfileref = NULL;
3477         }
3478     }
3479 
3480     if (!related) {
3481         Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3482 
3483         if (!NT_SUCCESS(Status)) {
3484             ERR("open_fileref returned %08lx\n", Status);
3485             goto end;
3486         }
3487     }
3488 
3489     SeCaptureSubjectContext(&subjcont);
3490 
3491     if (!SeAccessCheck(related->fcb->sd, &subjcont, false, FILE_ADD_FILE, 0, NULL,
3492                        IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3493         SeReleaseSubjectContext(&subjcont);
3494         TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3495         goto end;
3496     }
3497 
3498     SeReleaseSubjectContext(&subjcont);
3499 
3500     if (fcb->subvol != parfcb->subvol) {
3501         WARN("can't create hard link over subvolume boundary\n");
3502         Status = STATUS_INVALID_PARAMETER;
3503         goto end;
3504     }
3505 
3506     if (oldfileref) {
3507         SeCaptureSubjectContext(&subjcont);
3508 
3509         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL,
3510                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3511             SeReleaseSubjectContext(&subjcont);
3512             TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3513             goto end;
3514         }
3515 
3516         SeReleaseSubjectContext(&subjcont);
3517 
3518         if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
3519             oldfileref->delete_on_close = true;
3520             oldfileref->posix_delete = true;
3521         }
3522 
3523         Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
3524         if (!NT_SUCCESS(Status)) {
3525             ERR("delete_fileref returned %08lx\n", Status);
3526             goto end;
3527         }
3528     }
3529 
3530     fr2 = create_fileref(Vcb);
3531 
3532     fr2->fcb = fcb;
3533     fcb->refcount++;
3534 
3535     fr2->created = true;
3536     fr2->parent = related;
3537 
3538     Status = add_dir_child(related->fcb, fcb->inode, false, &utf8, &fnus, fcb->type, &dc);
3539     if (!NT_SUCCESS(Status))
3540         WARN("add_dir_child returned %08lx\n", Status);
3541 
3542     fr2->dc = dc;
3543     dc->fileref = fr2;
3544 
3545     ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
3546     InsertTailList(&related->children, &fr2->list_entry);
3547     ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
3548 
3549     // add hardlink for existing fileref, if it's not there already
3550     if (IsListEmpty(&fcb->hardlinks)) {
3551         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3552         if (!hl) {
3553             ERR("out of memory\n");
3554             Status = STATUS_INSUFFICIENT_RESOURCES;
3555             goto end;
3556         }
3557 
3558         hl->parent = fileref->parent->fcb->inode;
3559         hl->index = fileref->dc->index;
3560 
3561         hl->name.Length = hl->name.MaximumLength = fnus.Length;
3562         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
3563 
3564         if (!hl->name.Buffer) {
3565             ERR("out of memory\n");
3566             ExFreePool(hl);
3567             Status = STATUS_INSUFFICIENT_RESOURCES;
3568             goto end;
3569         }
3570 
3571         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3572 
3573         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
3574         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3575 
3576         if (!hl->utf8.Buffer) {
3577             ERR("out of memory\n");
3578             ExFreePool(hl->name.Buffer);
3579             ExFreePool(hl);
3580             Status = STATUS_INSUFFICIENT_RESOURCES;
3581             goto end;
3582         }
3583 
3584         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
3585 
3586         InsertTailList(&fcb->hardlinks, &hl->list_entry);
3587     }
3588 
3589     hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3590     if (!hl) {
3591         ERR("out of memory\n");
3592         Status = STATUS_INSUFFICIENT_RESOURCES;
3593         goto end;
3594     }
3595 
3596     hl->parent = related->fcb->inode;
3597     hl->index = dc->index;
3598 
3599     hl->name.Length = hl->name.MaximumLength = fnus.Length;
3600     hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
3601 
3602     if (!hl->name.Buffer) {
3603         ERR("out of memory\n");
3604         ExFreePool(hl);
3605         Status = STATUS_INSUFFICIENT_RESOURCES;
3606         goto end;
3607     }
3608 
3609     RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3610 
3611     hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
3612     hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3613 
3614     if (!hl->utf8.Buffer) {
3615         ERR("out of memory\n");
3616         ExFreePool(hl->name.Buffer);
3617         ExFreePool(hl);
3618         Status = STATUS_INSUFFICIENT_RESOURCES;
3619         goto end;
3620     }
3621 
3622     RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
3623     ExFreePool(utf8.Buffer);
3624 
3625     InsertTailList(&fcb->hardlinks, &hl->list_entry);
3626 
3627     mark_fileref_dirty(fr2);
3628     free_fileref(fr2);
3629 
3630     // update inode's INODE_ITEM
3631 
3632     KeQuerySystemTime(&time);
3633     win_time_to_unix(time, &now);
3634 
3635     fcb->inode_item.transid = Vcb->superblock.generation;
3636     fcb->inode_item.sequence++;
3637     fcb->inode_item.st_nlink++;
3638 
3639     if (!ccb->user_set_change_time)
3640         fcb->inode_item.st_ctime = now;
3641 
3642     fcb->inode_item_changed = true;
3643     mark_fcb_dirty(fcb);
3644 
3645     // update parent's INODE_ITEM
3646 
3647     parfcb->inode_item.transid = Vcb->superblock.generation;
3648     TRACE("parfcb->inode_item.st_size (inode %I64x) was %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3649     parfcb->inode_item.st_size += 2 * utf8len;
3650     TRACE("parfcb->inode_item.st_size (inode %I64x) now %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3651     parfcb->inode_item.sequence++;
3652     parfcb->inode_item.st_ctime = now;
3653 
3654     parfcb->inode_item_changed = true;
3655     mark_fcb_dirty(parfcb);
3656 
3657     send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3658 
3659     Status = STATUS_SUCCESS;
3660 
3661 end:
3662     if (oldfileref)
3663         free_fileref(oldfileref);
3664 
3665     if (!NT_SUCCESS(Status) && related)
3666         free_fileref(related);
3667 
3668     if (!NT_SUCCESS(Status) && fr2)
3669         free_fileref(fr2);
3670 
3671     if (NT_SUCCESS(Status))
3672         clear_rollback(&rollback);
3673     else
3674         do_rollback(Vcb, &rollback);
3675 
3676     ExReleaseResourceLite(fcb->Header.Resource);
3677     ExReleaseResourceLite(&Vcb->fileref_lock);
3678     ExReleaseResourceLite(&Vcb->tree_lock);
3679 
3680     return Status;
3681 }
3682 
3683 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
3684     FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
3685     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3686     fcb* fcb = FileObject->FsContext;
3687     ccb* ccb = FileObject->FsContext2;
3688     file_ref* fileref = ccb ? ccb->fileref : NULL;
3689     NTSTATUS Status;
3690     LARGE_INTEGER time;
3691     CC_FILE_SIZES ccfs;
3692     LIST_ENTRY rollback;
3693     bool set_size = false;
3694     ULONG filter;
3695 
3696     if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
3697         ERR("input buffer length was %lu, expected %Iu\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
3698         return STATUS_INVALID_PARAMETER;
3699     }
3700 
3701     if (!fileref) {
3702         ERR("fileref is NULL\n");
3703         return STATUS_INVALID_PARAMETER;
3704     }
3705 
3706     InitializeListHead(&rollback);
3707 
3708     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3709 
3710     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3711 
3712     if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
3713         Status = STATUS_INVALID_PARAMETER;
3714         goto end;
3715     }
3716 
3717     if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
3718         TRACE("invalid VDL of %I64u (current VDL = %I64u, file size = %I64u)\n", fvdli->ValidDataLength.QuadPart,
3719               fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
3720         Status = STATUS_INVALID_PARAMETER;
3721         goto end;
3722     }
3723 
3724     if (fileref ? fileref->deleted : fcb->deleted) {
3725         Status = STATUS_FILE_CLOSED;
3726         goto end;
3727     }
3728 
3729     // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
3730     // and we set it to the max anyway.
3731 
3732     ccfs.AllocationSize = fcb->Header.AllocationSize;
3733     ccfs.FileSize = fcb->Header.FileSize;
3734     ccfs.ValidDataLength = fvdli->ValidDataLength;
3735     set_size = true;
3736 
3737     filter = FILE_NOTIFY_CHANGE_SIZE;
3738 
3739     if (!ccb->user_set_write_time) {
3740         KeQuerySystemTime(&time);
3741         win_time_to_unix(time, &fcb->inode_item.st_mtime);
3742         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3743     }
3744 
3745     fcb->inode_item_changed = true;
3746     mark_fcb_dirty(fcb);
3747 
3748     queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3749 
3750     Status = STATUS_SUCCESS;
3751 
3752 end:
3753     if (NT_SUCCESS(Status))
3754         clear_rollback(&rollback);
3755     else
3756         do_rollback(Vcb, &rollback);
3757 
3758     ExReleaseResourceLite(fcb->Header.Resource);
3759 
3760     if (set_size) {
3761         _SEH2_TRY {
3762             CcSetFileSizes(FileObject, &ccfs);
3763         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3764             Status = _SEH2_GetExceptionCode();
3765         } _SEH2_END;
3766 
3767         if (!NT_SUCCESS(Status))
3768             ERR("CcSetFileSizes threw exception %08lx\n", Status);
3769         else
3770             fcb->Header.AllocationSize = ccfs.AllocationSize;
3771     }
3772 
3773     ExReleaseResourceLite(&Vcb->tree_lock);
3774 
3775     return Status;
3776 }
3777 
3778 #ifndef __REACTOS__
3779 static NTSTATUS set_case_sensitive_information(PIRP Irp) {
3780     FILE_CASE_SENSITIVE_INFORMATION* fcsi = (FILE_CASE_SENSITIVE_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3781     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3782 
3783     if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_CASE_SENSITIVE_INFORMATION))
3784         return STATUS_INFO_LENGTH_MISMATCH;
3785 
3786     PFILE_OBJECT FileObject = IrpSp->FileObject;
3787 
3788     if (!FileObject)
3789         return STATUS_INVALID_PARAMETER;
3790 
3791     fcb* fcb = FileObject->FsContext;
3792 
3793     if (!fcb)
3794         return STATUS_INVALID_PARAMETER;
3795 
3796     if (!(fcb->atts & FILE_ATTRIBUTE_DIRECTORY)) {
3797         WARN("cannot set case-sensitive flag on anything other than directory\n");
3798         return STATUS_INVALID_PARAMETER;
3799     }
3800 
3801     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
3802 
3803     fcb->case_sensitive = fcsi->Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR;
3804     mark_fcb_dirty(fcb);
3805 
3806     ExReleaseResourceLite(&fcb->Vcb->tree_lock);
3807 
3808     return STATUS_SUCCESS;
3809 }
3810 #endif
3811 
3812 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
3813 _Function_class_(DRIVER_DISPATCH)
3814 NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3815     NTSTATUS Status;
3816     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3817     device_extension* Vcb = DeviceObject->DeviceExtension;
3818     fcb* fcb = IrpSp->FileObject->FsContext;
3819     ccb* ccb = IrpSp->FileObject->FsContext2;
3820     bool top_level;
3821 
3822     FsRtlEnterFileSystem();
3823 
3824     top_level = is_top_level(Irp);
3825 
3826     Irp->IoStatus.Information = 0;
3827 
3828     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3829         Status = vol_set_information(DeviceObject, Irp);
3830         goto end;
3831     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3832         Status = STATUS_INVALID_PARAMETER;
3833         goto end;
3834     }
3835 
3836     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
3837         Status = STATUS_ACCESS_DENIED;
3838         goto end;
3839     }
3840 
3841     if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
3842         Status = STATUS_MEDIA_WRITE_PROTECTED;
3843         goto end;
3844     }
3845 
3846     if (!fcb) {
3847         ERR("no fcb\n");
3848         Status = STATUS_INVALID_PARAMETER;
3849         goto end;
3850     }
3851 
3852     if (!ccb) {
3853         ERR("no ccb\n");
3854         Status = STATUS_INVALID_PARAMETER;
3855         goto end;
3856     }
3857 
3858     if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
3859 #ifndef __REACTOS__
3860         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformationEx))) {
3861 #else
3862         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
3863 #endif
3864         Status = STATUS_ACCESS_DENIED;
3865         goto end;
3866     }
3867 
3868     Status = STATUS_NOT_IMPLEMENTED;
3869 
3870     TRACE("set information\n");
3871 
3872     FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
3873 
3874     switch (IrpSp->Parameters.SetFile.FileInformationClass) {
3875         case FileAllocationInformation:
3876         {
3877             TRACE("FileAllocationInformation\n");
3878 
3879             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3880                 WARN("insufficient privileges\n");
3881                 Status = STATUS_ACCESS_DENIED;
3882                 break;
3883             }
3884 
3885             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, false, true);
3886             break;
3887         }
3888 
3889         case FileBasicInformation:
3890         {
3891             TRACE("FileBasicInformation\n");
3892 
3893             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
3894                 WARN("insufficient privileges\n");
3895                 Status = STATUS_ACCESS_DENIED;
3896                 break;
3897             }
3898 
3899             Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
3900 
3901             break;
3902         }
3903 
3904         case FileDispositionInformation:
3905         {
3906             TRACE("FileDispositionInformation\n");
3907 
3908             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
3909                 WARN("insufficient privileges\n");
3910                 Status = STATUS_ACCESS_DENIED;
3911                 break;
3912             }
3913 
3914             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, false);
3915 
3916             break;
3917         }
3918 
3919         case FileEndOfFileInformation:
3920         {
3921             TRACE("FileEndOfFileInformation\n");
3922 
3923             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3924                 WARN("insufficient privileges\n");
3925                 Status = STATUS_ACCESS_DENIED;
3926                 break;
3927             }
3928 
3929             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, false);
3930 
3931             break;
3932         }
3933 
3934         case FileLinkInformation:
3935             TRACE("FileLinkInformation\n");
3936             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
3937             break;
3938 
3939         case FilePositionInformation:
3940             TRACE("FilePositionInformation\n");
3941             Status = set_position_information(IrpSp->FileObject, Irp);
3942             break;
3943 
3944         case FileRenameInformation:
3945             TRACE("FileRenameInformation\n");
3946             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
3947             break;
3948 
3949         case FileValidDataLengthInformation:
3950         {
3951             TRACE("FileValidDataLengthInformation\n");
3952 
3953             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3954                 WARN("insufficient privileges\n");
3955                 Status = STATUS_ACCESS_DENIED;
3956                 break;
3957             }
3958 
3959             Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
3960 
3961             break;
3962         }
3963 
3964 #ifndef __REACTOS__
3965 #ifndef _MSC_VER
3966 #pragma GCC diagnostic push
3967 #pragma GCC diagnostic ignored "-Wswitch"
3968 #endif
3969         case FileDispositionInformationEx:
3970         {
3971             TRACE("FileDispositionInformationEx\n");
3972 
3973             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
3974                 WARN("insufficient privileges\n");
3975                 Status = STATUS_ACCESS_DENIED;
3976                 break;
3977             }
3978 
3979             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, true);
3980 
3981             break;
3982         }
3983 
3984         case FileRenameInformationEx:
3985             TRACE("FileRenameInformationEx\n");
3986             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
3987             break;
3988 
3989         case FileLinkInformationEx:
3990             TRACE("FileLinkInformationEx\n");
3991             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
3992             break;
3993 
3994         case FileCaseSensitiveInformation:
3995             TRACE("FileCaseSensitiveInformation\n");
3996 
3997             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
3998                 WARN("insufficient privileges\n");
3999                 Status = STATUS_ACCESS_DENIED;
4000                 break;
4001             }
4002 
4003             Status = set_case_sensitive_information(Irp);
4004             break;
4005 
4006         case FileStorageReserveIdInformation:
4007             WARN("unimplemented FileInformationClass FileStorageReserveIdInformation\n");
4008             break;
4009 
4010 #ifndef _MSC_VER
4011 #pragma GCC diagnostic pop
4012 #endif
4013 #endif
4014 
4015         default:
4016             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
4017     }
4018 
4019 end:
4020     Irp->IoStatus.Status = Status;
4021 
4022     TRACE("returning %08lx\n", Status);
4023 
4024     IoCompleteRequest(Irp, IO_NO_INCREMENT);
4025 
4026     if (top_level)
4027         IoSetTopLevelIrp(NULL);
4028 
4029     FsRtlExitFileSystem();
4030 
4031     return Status;
4032 }
4033 
4034 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
4035     RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
4036 
4037     *length -= sizeof(FILE_BASIC_INFORMATION);
4038 
4039     if (fcb == fcb->Vcb->dummy_fcb) {
4040         LARGE_INTEGER time;
4041 
4042         KeQuerySystemTime(&time);
4043         fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
4044     } else {
4045         fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4046         fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4047         fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4048         fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4049     }
4050 
4051     if (fcb->ads) {
4052         if (!fileref || !fileref->parent) {
4053             ERR("no fileref for stream\n");
4054             return STATUS_INTERNAL_ERROR;
4055         } else
4056             fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4057     } else
4058         fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4059 
4060     return STATUS_SUCCESS;
4061 }
4062 
4063 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
4064     INODE_ITEM* ii;
4065 
4066     if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
4067         WARN("overflow\n");
4068         return STATUS_BUFFER_OVERFLOW;
4069     }
4070 
4071     RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
4072 
4073     *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
4074 
4075     if (fcb->ads) {
4076         if (!fileref || !fileref->parent) {
4077             ERR("no fileref for stream\n");
4078             return STATUS_INTERNAL_ERROR;
4079         }
4080 
4081         ii = &fileref->parent->fcb->inode_item;
4082     } else
4083         ii = &fcb->inode_item;
4084 
4085     if (fcb == fcb->Vcb->dummy_fcb) {
4086         LARGE_INTEGER time;
4087 
4088         KeQuerySystemTime(&time);
4089         fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
4090     } else {
4091         fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4092         fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4093         fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4094         fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4095     }
4096 
4097     if (fcb->ads) {
4098         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
4099         fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4100     } else {
4101         fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4102         fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4103         fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4104     }
4105 
4106     return STATUS_SUCCESS;
4107 }
4108 
4109 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
4110     RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
4111 
4112     *length -= sizeof(FILE_STANDARD_INFORMATION);
4113 
4114     if (fcb->ads) {
4115         if (!fileref || !fileref->parent) {
4116             ERR("no fileref for stream\n");
4117             return STATUS_INTERNAL_ERROR;
4118         }
4119 
4120         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4121         fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
4122         fsi->Directory = false;
4123     } else {
4124         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4125         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4126         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4127         fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
4128     }
4129 
4130     TRACE("length = %I64u\n", fsi->EndOfFile.QuadPart);
4131 
4132     fsi->DeletePending = fileref ? fileref->delete_on_close : false;
4133 
4134     return STATUS_SUCCESS;
4135 }
4136 
4137 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
4138     *length -= sizeof(FILE_INTERNAL_INFORMATION);
4139 
4140     fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4141 
4142     return STATUS_SUCCESS;
4143 }
4144 
4145 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
4146     *length -= sizeof(FILE_EA_INFORMATION);
4147 
4148     /* This value appears to be the size of the structure NTFS stores on disk, and not,
4149      * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
4150      * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
4151      * item. */
4152 
4153     eai->EaSize = fcb->ealen;
4154 
4155     return STATUS_SUCCESS;
4156 }
4157 
4158 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
4159     RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
4160 
4161     *length -= sizeof(FILE_POSITION_INFORMATION);
4162 
4163     fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
4164 
4165     return STATUS_SUCCESS;
4166 }
4167 
4168 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
4169     file_ref* fr;
4170     NTSTATUS Status;
4171     ULONG reqlen = 0;
4172     USHORT offset;
4173     bool overflow = false;
4174 
4175     // FIXME - we need a lock on filerefs' filepart
4176 
4177     if (fileref == fileref->fcb->Vcb->root_fileref) {
4178         if (fn->MaximumLength >= sizeof(WCHAR)) {
4179             fn->Buffer[0] = '\\';
4180             fn->Length = sizeof(WCHAR);
4181 
4182             if (name_offset)
4183                 *name_offset = 0;
4184 
4185             return STATUS_SUCCESS;
4186         } else {
4187             if (preqlen)
4188                 *preqlen = sizeof(WCHAR);
4189             fn->Length = 0;
4190 
4191             return STATUS_BUFFER_OVERFLOW;
4192         }
4193     }
4194 
4195     fr = fileref;
4196     offset = 0;
4197 
4198     while (fr->parent) {
4199         USHORT movelen;
4200 
4201         if (!fr->dc)
4202             return STATUS_INTERNAL_ERROR;
4203 
4204         if (!overflow) {
4205             if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
4206                 overflow = true;
4207         }
4208 
4209         if (overflow)
4210             movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
4211         else
4212             movelen = fn->Length;
4213 
4214         if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
4215             RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
4216             offset += fr->dc->name.Length + sizeof(WCHAR);
4217         }
4218 
4219         if (fn->MaximumLength >= sizeof(WCHAR)) {
4220             fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
4221             fn->Length += sizeof(WCHAR);
4222 
4223             if (fn->MaximumLength > sizeof(WCHAR)) {
4224                 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
4225                 fn->Length += fr->dc->name.Length;
4226             }
4227 
4228             if (fn->Length > fn->MaximumLength) {
4229                 fn->Length = fn->MaximumLength;
4230                 overflow = true;
4231             }
4232         }
4233 
4234         reqlen += sizeof(WCHAR) + fr->dc->name.Length;
4235 
4236         fr = fr->parent;
4237     }
4238 
4239     offset += sizeof(WCHAR);
4240 
4241     if (overflow) {
4242         if (preqlen)
4243             *preqlen = reqlen;
4244         Status = STATUS_BUFFER_OVERFLOW;
4245     } else {
4246         if (name_offset)
4247             *name_offset = offset;
4248 
4249         Status = STATUS_SUCCESS;
4250     }
4251 
4252     return Status;
4253 }
4254 
4255 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
4256     ULONG reqlen;
4257     UNICODE_STRING fn;
4258     NTSTATUS Status;
4259     static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
4260     uint16_t datasuflen = sizeof(datasuf) - sizeof(WCHAR);
4261 
4262     if (!fileref) {
4263         ERR("called without fileref\n");
4264         return STATUS_INVALID_PARAMETER;
4265     }
4266 
4267     *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
4268 
4269     TRACE("maximum length is %li\n", *length);
4270     fni->FileNameLength = 0;
4271 
4272     fni->FileName[0] = 0;
4273 
4274     fn.Buffer = fni->FileName;
4275     fn.Length = 0;
4276     fn.MaximumLength = (uint16_t)*length;
4277 
4278     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
4279     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
4280         ERR("fileref_get_filename returned %08lx\n", Status);
4281         return Status;
4282     }
4283 
4284     if (fcb->ads) {
4285         if (Status == STATUS_BUFFER_OVERFLOW)
4286             reqlen += datasuflen;
4287         else {
4288             if (fn.Length + datasuflen > fn.MaximumLength) {
4289                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
4290                 reqlen += datasuflen;
4291                 Status = STATUS_BUFFER_OVERFLOW;
4292             } else {
4293                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
4294                 fn.Length += datasuflen;
4295             }
4296         }
4297     }
4298 
4299     if (Status == STATUS_BUFFER_OVERFLOW) {
4300         *length = -1;
4301         fni->FileNameLength = reqlen;
4302         TRACE("%.*S (truncated)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4303     } else {
4304         *length -= fn.Length;
4305         fni->FileNameLength = fn.Length;
4306         TRACE("%.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4307     }
4308 
4309     return Status;
4310 }
4311 
4312 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
4313     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
4314 
4315     if (fcb->ads) {
4316         if (!ccb->fileref || !ccb->fileref->parent) {
4317             ERR("no fileref for stream\n");
4318             return STATUS_INTERNAL_ERROR;
4319         }
4320 
4321         ati->FileAttributes = ccb->fileref->parent->fcb->atts;
4322     } else
4323         ati->FileAttributes = fcb->atts;
4324 
4325     if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4326         ati->ReparseTag = 0;
4327     else
4328         ati->ReparseTag = get_reparse_tag_fcb(fcb);
4329 
4330     return STATUS_SUCCESS;
4331 }
4332 
4333 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
4334     LONG reqsize;
4335     LIST_ENTRY* le;
4336     FILE_STREAM_INFORMATION *entry, *lastentry;
4337     NTSTATUS Status;
4338 
4339     static const WCHAR datasuf[] = L":$DATA";
4340     UNICODE_STRING suf;
4341 
4342     if (!fileref) {
4343         ERR("fileref was NULL\n");
4344         return STATUS_INVALID_PARAMETER;
4345     }
4346 
4347     suf.Buffer = (WCHAR*)datasuf;
4348     suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
4349 
4350     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
4351         reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
4352     else
4353         reqsize = 0;
4354 
4355     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, true);
4356 
4357     le = fileref->fcb->dir_children_index.Flink;
4358     while (le != &fileref->fcb->dir_children_index) {
4359         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4360 
4361         if (dc->index == 0) {
4362             reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
4363             reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
4364         } else
4365             break;
4366 
4367         le = le->Flink;
4368     }
4369 
4370     TRACE("length = %li, reqsize = %lu\n", *length, reqsize);
4371 
4372     if (reqsize > *length) {
4373         Status = STATUS_BUFFER_OVERFLOW;
4374         goto end;
4375     }
4376 
4377     entry = fsi;
4378     lastentry = NULL;
4379 
4380     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
4381         ULONG off;
4382 
4383         entry->NextEntryOffset = 0;
4384         entry->StreamNameLength = suf.Length + sizeof(WCHAR);
4385         entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
4386         entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
4387 
4388         entry->StreamName[0] = ':';
4389         RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
4390 
4391         off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
4392 
4393         lastentry = entry;
4394         entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4395     }
4396 
4397     le = fileref->fcb->dir_children_index.Flink;
4398     while (le != &fileref->fcb->dir_children_index) {
4399         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4400 
4401         if (dc->index == 0) {
4402             ULONG off;
4403 
4404             entry->NextEntryOffset = 0;
4405             entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
4406 
4407             if (dc->fileref)
4408                 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
4409             else
4410                 entry->StreamSize.QuadPart = dc->size;
4411 
4412             entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
4413 
4414             entry->StreamName[0] = ':';
4415 
4416             RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
4417             RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
4418 
4419             if (lastentry)
4420                 lastentry->NextEntryOffset = (uint32_t)((uint8_t*)entry - (uint8_t*)lastentry);
4421 
4422             off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
4423 
4424             lastentry = entry;
4425             entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4426         } else
4427             break;
4428 
4429         le = le->Flink;
4430     }
4431 
4432     *length -= reqsize;
4433 
4434     Status = STATUS_SUCCESS;
4435 
4436 end:
4437     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
4438 
4439     return Status;
4440 }
4441 
4442 #ifndef __REACTOS__
4443 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
4444     TRACE("FileStandardLinkInformation\n");
4445 
4446     // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
4447 
4448     fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
4449     fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
4450     fsli->DeletePending = fileref ? fileref->delete_on_close : false;
4451     fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? true : false;
4452 
4453     *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
4454 
4455     return STATUS_SUCCESS;
4456 }
4457 
4458 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
4459     NTSTATUS Status;
4460     LIST_ENTRY* le;
4461     LONG bytes_needed;
4462     FILE_LINK_ENTRY_INFORMATION* feli;
4463     bool overflow = false;
4464     fcb* fcb = fileref->fcb;
4465     ULONG len;
4466 
4467     if (fcb->ads)
4468         return STATUS_INVALID_PARAMETER;
4469 
4470     if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
4471         return STATUS_INVALID_PARAMETER;
4472 
4473     RtlZeroMemory(fli, *length);
4474 
4475     bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
4476     len = bytes_needed;
4477     feli = NULL;
4478 
4479     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4480 
4481     if (fcb->inode == SUBVOL_ROOT_INODE) {
4482         ULONG namelen;
4483 
4484         if (fcb == fcb->Vcb->root_fileref->fcb)
4485             namelen = sizeof(WCHAR);
4486         else
4487             namelen = fileref->dc->name.Length;
4488 
4489         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
4490 
4491         if (bytes_needed > *length)
4492             overflow = true;
4493 
4494         if (!overflow) {
4495             feli = &fli->Entry;
4496 
4497             feli->NextEntryOffset = 0;
4498             feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
4499 
4500             if (fcb == fcb->Vcb->root_fileref->fcb) {
4501                 feli->FileNameLength = 1;
4502                 feli->FileName[0] = '.';
4503             } else {
4504                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4505                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4506             }
4507 
4508             fli->EntriesReturned++;
4509 
4510             len = bytes_needed;
4511         }
4512     } else {
4513         ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4514 
4515         if (IsListEmpty(&fcb->hardlinks)) {
4516             bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
4517 
4518             if (bytes_needed > *length)
4519                 overflow = true;
4520 
4521             if (!overflow) {
4522                 feli = &fli->Entry;
4523 
4524                 feli->NextEntryOffset = 0;
4525                 feli->ParentFileId = fileref->parent->fcb->inode;
4526                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4527                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4528 
4529                 fli->EntriesReturned++;
4530 
4531                 len = bytes_needed;
4532             }
4533         } else {
4534             le = fcb->hardlinks.Flink;
4535             while (le != &fcb->hardlinks) {
4536                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4537                 file_ref* parfr;
4538 
4539                 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer);
4540 
4541                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4542 
4543                 if (!NT_SUCCESS(Status)) {
4544                     ERR("open_fileref_by_inode returned %08lx\n", Status);
4545                 } else if (!parfr->deleted) {
4546                     LIST_ENTRY* le2;
4547                     bool found = false, deleted = false;
4548                     UNICODE_STRING* fn = NULL;
4549 
4550                     le2 = parfr->children.Flink;
4551                     while (le2 != &parfr->children) {
4552                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4553 
4554                         if (fr2->dc->index == hl->index) {
4555                             found = true;
4556                             deleted = fr2->deleted;
4557 
4558                             if (!deleted)
4559                                 fn = &fr2->dc->name;
4560 
4561                             break;
4562                         }
4563 
4564                         le2 = le2->Flink;
4565                     }
4566 
4567                     if (!found)
4568                         fn = &hl->name;
4569 
4570                     if (!deleted) {
4571                         TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found);
4572 
4573                         if (feli)
4574                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
4575 
4576                         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
4577 
4578                         if (bytes_needed > *length)
4579                             overflow = true;
4580 
4581                         if (!overflow) {
4582                             if (feli) {
4583                                 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
4584                                 feli = (FILE_LINK_ENTRY_INFORMATION*)((uint8_t*)feli + feli->NextEntryOffset);
4585                             } else
4586                                 feli = &fli->Entry;
4587 
4588                             feli->NextEntryOffset = 0;
4589                             feli->ParentFileId = parfr->fcb->inode;
4590                             feli->FileNameLength = fn->Length / sizeof(WCHAR);
4591                             RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
4592 
4593                             fli->EntriesReturned++;
4594 
4595                             len = bytes_needed;
4596                         }
4597                     }
4598 
4599                     free_fileref(parfr);
4600                 }
4601 
4602                 le = le->Flink;
4603             }
4604         }
4605 
4606         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4607     }
4608 
4609     fli->BytesNeeded = bytes_needed;
4610 
4611     *length -= len;
4612 
4613     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4614 
4615     ExReleaseResourceLite(fcb->Header.Resource);
4616 
4617     return Status;
4618 }
4619 
4620 static NTSTATUS fill_in_hard_link_full_id_information(FILE_LINKS_FULL_ID_INFORMATION* flfii, file_ref* fileref, PIRP Irp, LONG* length) {
4621     NTSTATUS Status;
4622     LIST_ENTRY* le;
4623     LONG bytes_needed;
4624     FILE_LINK_ENTRY_FULL_ID_INFORMATION* flefii;
4625     bool overflow = false;
4626     fcb* fcb = fileref->fcb;
4627     ULONG len;
4628 
4629     if (fcb->ads)
4630         return STATUS_INVALID_PARAMETER;
4631 
4632     if (*length < (LONG)offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry))
4633         return STATUS_INVALID_PARAMETER;
4634 
4635     RtlZeroMemory(flfii, *length);
4636 
4637     bytes_needed = offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry);
4638     len = bytes_needed;
4639     flefii = NULL;
4640 
4641     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4642 
4643     if (fcb->inode == SUBVOL_ROOT_INODE) {
4644         ULONG namelen;
4645 
4646         if (fcb == fcb->Vcb->root_fileref->fcb)
4647             namelen = sizeof(WCHAR);
4648         else
4649             namelen = fileref->dc->name.Length;
4650 
4651         bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + namelen;
4652 
4653         if (bytes_needed > *length)
4654             overflow = true;
4655 
4656         if (!overflow) {
4657             flefii = &flfii->Entry;
4658 
4659             flefii->NextEntryOffset = 0;
4660 
4661             if (fcb == fcb->Vcb->root_fileref->fcb) {
4662                 RtlZeroMemory(&flefii->ParentFileId.Identifier[0], sizeof(FILE_ID_128));
4663                 flefii->FileNameLength = 1;
4664                 flefii->FileName[0] = '.';
4665             } else {
4666                 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4667                 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4668 
4669                 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4670                 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4671             }
4672 
4673             flfii->EntriesReturned++;
4674 
4675             len = bytes_needed;
4676         }
4677     } else {
4678         ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4679 
4680         if (IsListEmpty(&fcb->hardlinks)) {
4681             bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fileref->dc->name.Length;
4682 
4683             if (bytes_needed > *length)
4684                 overflow = true;
4685 
4686             if (!overflow) {
4687                 flefii = &flfii->Entry;
4688 
4689                 flefii->NextEntryOffset = 0;
4690 
4691                 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4692                 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4693 
4694                 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4695                 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4696 
4697                 flfii->EntriesReturned++;
4698 
4699                 len = bytes_needed;
4700             }
4701         } else {
4702             le = fcb->hardlinks.Flink;
4703             while (le != &fcb->hardlinks) {
4704                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4705                 file_ref* parfr;
4706 
4707                 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer);
4708 
4709                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4710 
4711                 if (!NT_SUCCESS(Status)) {
4712                     ERR("open_fileref_by_inode returned %08lx\n", Status);
4713                 } else if (!parfr->deleted) {
4714                     LIST_ENTRY* le2;
4715                     bool found = false, deleted = false;
4716                     UNICODE_STRING* fn = NULL;
4717 
4718                     le2 = parfr->children.Flink;
4719                     while (le2 != &parfr->children) {
4720                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4721 
4722                         if (fr2->dc->index == hl->index) {
4723                             found = true;
4724                             deleted = fr2->deleted;
4725 
4726                             if (!deleted)
4727                                 fn = &fr2->dc->name;
4728 
4729                             break;
4730                         }
4731 
4732                         le2 = le2->Flink;
4733                     }
4734 
4735                     if (!found)
4736                         fn = &hl->name;
4737 
4738                     if (!deleted) {
4739                         TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found);
4740 
4741                         if (flefii)
4742                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
4743 
4744                         bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fn->Length;
4745 
4746                         if (bytes_needed > *length)
4747                             overflow = true;
4748 
4749                         if (!overflow) {
4750                             if (flefii) {
4751                                 flefii->NextEntryOffset = (ULONG)sector_align(offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + (flefii->FileNameLength * sizeof(WCHAR)), 8);
4752                                 flefii = (FILE_LINK_ENTRY_FULL_ID_INFORMATION*)((uint8_t*)flefii + flefii->NextEntryOffset);
4753                             } else
4754                                 flefii = &flfii->Entry;
4755 
4756                             flefii->NextEntryOffset = 0;
4757 
4758                             RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &parfr->fcb->inode, sizeof(uint64_t));
4759                             RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &parfr->fcb->subvol->id, sizeof(uint64_t));
4760 
4761                             flefii->FileNameLength = fn->Length / sizeof(WCHAR);
4762                             RtlCopyMemory(flefii->FileName, fn->Buffer, fn->Length);
4763 
4764                             flfii->EntriesReturned++;
4765 
4766                             len = bytes_needed;
4767                         }
4768                     }
4769 
4770                     free_fileref(parfr);
4771                 }
4772 
4773                 le = le->Flink;
4774             }
4775         }
4776 
4777         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4778     }
4779 
4780     flfii->BytesNeeded = bytes_needed;
4781 
4782     *length -= len;
4783 
4784     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4785 
4786     ExReleaseResourceLite(fcb->Header.Resource);
4787 
4788     return Status;
4789 }
4790 
4791 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
4792     RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(uint64_t));
4793     RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(uint64_t));
4794     RtlCopyMemory(&fii->FileId.Identifier[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
4795 
4796     *length -= sizeof(FILE_ID_INFORMATION);
4797 
4798     return STATUS_SUCCESS;
4799 }
4800 
4801 static NTSTATUS fill_in_file_stat_information(FILE_STAT_INFORMATION* fsi, fcb* fcb, ccb* ccb, LONG* length) {
4802     INODE_ITEM* ii;
4803 
4804     fsi->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4805 
4806     if (fcb->ads)
4807         ii = &ccb->fileref->parent->fcb->inode_item;
4808     else
4809         ii = &fcb->inode_item;
4810 
4811     if (fcb == fcb->Vcb->dummy_fcb) {
4812         LARGE_INTEGER time;
4813 
4814         KeQuerySystemTime(&time);
4815         fsi->CreationTime = fsi->LastAccessTime = fsi->LastWriteTime = fsi->ChangeTime = time;
4816     } else {
4817         fsi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4818         fsi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4819         fsi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4820         fsi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4821     }
4822 
4823     if (fcb->ads) {
4824         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4825         fsi->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4826     } else {
4827         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4828         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4829         fsi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4830     }
4831 
4832     if (fcb->type == BTRFS_TYPE_SOCKET)
4833         fsi->ReparseTag = IO_REPARSE_TAG_AF_UNIX;
4834     else if (fcb->type == BTRFS_TYPE_FIFO)
4835         fsi->ReparseTag = IO_REPARSE_TAG_LX_FIFO;
4836     else if (fcb->type == BTRFS_TYPE_CHARDEV)
4837         fsi->ReparseTag = IO_REPARSE_TAG_LX_CHR;
4838     else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4839         fsi->ReparseTag = IO_REPARSE_TAG_LX_BLK;
4840     else if (!(fsi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4841         fsi->ReparseTag = 0;
4842     else
4843         fsi->ReparseTag = get_reparse_tag_fcb(fcb);
4844 
4845     if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4846         fsi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4847 
4848     if (fcb->ads)
4849         fsi->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4850     else
4851         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4852 
4853     fsi->EffectiveAccess = ccb->access;
4854 
4855     *length -= sizeof(FILE_STAT_INFORMATION);
4856 
4857     return STATUS_SUCCESS;
4858 }
4859 
4860 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
4861     INODE_ITEM* ii;
4862 
4863     fsli->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4864 
4865     if (fcb->ads)
4866         ii = &ccb->fileref->parent->fcb->inode_item;
4867     else
4868         ii = &fcb->inode_item;
4869 
4870     if (fcb == fcb->Vcb->dummy_fcb) {
4871         LARGE_INTEGER time;
4872 
4873         KeQuerySystemTime(&time);
4874         fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
4875     } else {
4876         fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4877         fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4878         fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4879         fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4880     }
4881 
4882     if (fcb->ads) {
4883         fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
4884         fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4885     } else {
4886         fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4887         fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4888         fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4889     }
4890 
4891     if (fcb->type == BTRFS_TYPE_SOCKET)
4892         fsli->ReparseTag = IO_REPARSE_TAG_AF_UNIX;
4893     else if (fcb->type == BTRFS_TYPE_FIFO)
4894         fsli->ReparseTag = IO_REPARSE_TAG_LX_FIFO;
4895     else if (fcb->type == BTRFS_TYPE_CHARDEV)
4896         fsli->ReparseTag = IO_REPARSE_TAG_LX_CHR;
4897     else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4898         fsli->ReparseTag = IO_REPARSE_TAG_LX_BLK;
4899     else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4900         fsli->ReparseTag = 0;
4901     else
4902         fsli->ReparseTag = get_reparse_tag_fcb(fcb);
4903 
4904     if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4905         fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4906 
4907     if (fcb->ads)
4908         fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4909     else
4910         fsli->NumberOfLinks = fcb->inode_item.st_nlink;
4911 
4912     fsli->EffectiveAccess = ccb->access;
4913     fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID;
4914 
4915     if (fcb->case_sensitive)
4916         fsli->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR;
4917 
4918     fsli->LxUid = ii->st_uid;
4919     fsli->LxGid = ii->st_gid;
4920     fsli->LxMode = ii->st_mode;
4921 
4922     if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
4923         fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
4924         fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
4925     } else {
4926         fsli->LxDeviceIdMajor = 0;
4927         fsli->LxDeviceIdMinor = 0;
4928     }
4929 
4930     *length -= sizeof(FILE_STAT_LX_INFORMATION);
4931 
4932     return STATUS_SUCCESS;
4933 }
4934 
4935 static NTSTATUS fill_in_file_case_sensitive_information(FILE_CASE_SENSITIVE_INFORMATION* fcsi, fcb* fcb, LONG* length) {
4936     fcsi->Flags = fcb->case_sensitive ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
4937 
4938     *length -= sizeof(FILE_CASE_SENSITIVE_INFORMATION);
4939 
4940     return STATUS_SUCCESS;
4941 }
4942 
4943 #endif // __REACTOS__
4944 
4945 static NTSTATUS fill_in_file_compression_information(FILE_COMPRESSION_INFORMATION* fci, LONG* length, fcb* fcb) {
4946     *length -= sizeof(FILE_COMPRESSION_INFORMATION);
4947 
4948     memset(fci, 0, sizeof(FILE_COMPRESSION_INFORMATION));
4949 
4950     if (fcb->ads)
4951         fci->CompressedFileSize.QuadPart = fcb->adsdata.Length;
4952     else if (!S_ISDIR(fcb->inode_item.st_mode))
4953         fci->CompressedFileSize.QuadPart = fcb->inode_item.st_size;
4954 
4955     return STATUS_SUCCESS;
4956 }
4957 
4958 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
4959     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4960     LONG length = IrpSp->Parameters.QueryFile.Length;
4961     fcb* fcb = FileObject->FsContext;
4962     ccb* ccb = FileObject->FsContext2;
4963     file_ref* fileref = ccb ? ccb->fileref : NULL;
4964     NTSTATUS Status;
4965 
4966     TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
4967     TRACE("fcb = %p\n", fcb);
4968 
4969     if (fcb == Vcb->volume_fcb)
4970         return STATUS_INVALID_PARAMETER;
4971 
4972     if (!ccb) {
4973         ERR("ccb is NULL\n");
4974         return STATUS_INVALID_PARAMETER;
4975     }
4976 
4977     switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
4978         case FileAllInformation:
4979         {
4980             FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
4981             INODE_ITEM* ii;
4982 
4983             TRACE("FileAllInformation\n");
4984 
4985             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
4986                 WARN("insufficient privileges\n");
4987                 Status = STATUS_ACCESS_DENIED;
4988                 goto exit;
4989             }
4990 
4991             if (fcb->ads) {
4992                 if (!fileref || !fileref->parent) {
4993                     ERR("no fileref for stream\n");
4994                     Status = STATUS_INTERNAL_ERROR;
4995                     goto exit;
4996                 }
4997 
4998                 ii = &fileref->parent->fcb->inode_item;
4999             } else
5000                 ii = &fcb->inode_item;
5001 
5002             // Access, mode, and alignment are all filled in by the kernel
5003 
5004             if (length > 0)
5005                 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
5006 
5007             if (length > 0)
5008                 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
5009 
5010             if (length > 0)
5011                 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
5012 
5013             if (length > 0)
5014                 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
5015 
5016             length -= sizeof(FILE_ACCESS_INFORMATION);
5017 
5018             if (length > 0)
5019                 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
5020 
5021             length -= sizeof(FILE_MODE_INFORMATION);
5022 
5023             length -= sizeof(FILE_ALIGNMENT_INFORMATION);
5024 
5025             if (length > 0)
5026                 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
5027 
5028             Status = STATUS_SUCCESS;
5029 
5030             break;
5031         }
5032 
5033         case FileAttributeTagInformation:
5034         {
5035             FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
5036 
5037             TRACE("FileAttributeTagInformation\n");
5038 
5039             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5040                 WARN("insufficient privileges\n");
5041                 Status = STATUS_ACCESS_DENIED;
5042                 goto exit;
5043             }
5044 
5045             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5046             Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
5047             ExReleaseResourceLite(&Vcb->tree_lock);
5048 
5049             break;
5050         }
5051 
5052         case FileBasicInformation:
5053         {
5054             FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
5055             INODE_ITEM* ii;
5056 
5057             TRACE("FileBasicInformation\n");
5058 
5059             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5060                 WARN("insufficient privileges\n");
5061                 Status = STATUS_ACCESS_DENIED;
5062                 goto exit;
5063             }
5064 
5065             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
5066                 WARN("overflow\n");
5067                 Status = STATUS_BUFFER_OVERFLOW;
5068                 goto exit;
5069             }
5070 
5071             if (fcb->ads) {
5072                 if (!fileref || !fileref->parent) {
5073                     ERR("no fileref for stream\n");
5074                     Status = STATUS_INTERNAL_ERROR;
5075                     goto exit;
5076                 }
5077 
5078                 ii = &fileref->parent->fcb->inode_item;
5079             } else
5080                 ii = &fcb->inode_item;
5081 
5082             Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
5083             break;
5084         }
5085 
5086         case FileCompressionInformation:
5087         {
5088             FILE_COMPRESSION_INFORMATION* fci = Irp->AssociatedIrp.SystemBuffer;
5089 
5090             TRACE("FileCompressionInformation\n");
5091 
5092             Status = fill_in_file_compression_information(fci, &length, fcb);
5093             break;
5094         }
5095 
5096         case FileEaInformation:
5097         {
5098             FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
5099 
5100             TRACE("FileEaInformation\n");
5101 
5102             Status = fill_in_file_ea_information(eai, fcb, &length);
5103 
5104             break;
5105         }
5106 
5107         case FileInternalInformation:
5108         {
5109             FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5110 
5111             TRACE("FileInternalInformation\n");
5112 
5113             Status = fill_in_file_internal_information(fii, fcb, &length);
5114 
5115             break;
5116         }
5117 
5118         case FileNameInformation:
5119         {
5120             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5121 
5122             TRACE("FileNameInformation\n");
5123 
5124             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5125 
5126             break;
5127         }
5128 
5129         case FileNetworkOpenInformation:
5130         {
5131             FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
5132 
5133             TRACE("FileNetworkOpenInformation\n");
5134 
5135             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5136                 WARN("insufficient privileges\n");
5137                 Status = STATUS_ACCESS_DENIED;
5138                 goto exit;
5139             }
5140 
5141             Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
5142 
5143             break;
5144         }
5145 
5146         case FilePositionInformation:
5147         {
5148             FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
5149 
5150             TRACE("FilePositionInformation\n");
5151 
5152             Status = fill_in_file_position_information(fpi, FileObject, &length);
5153 
5154             break;
5155         }
5156 
5157         case FileStandardInformation:
5158         {
5159             FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5160 
5161             TRACE("FileStandardInformation\n");
5162 
5163             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
5164                 WARN("overflow\n");
5165                 Status = STATUS_BUFFER_OVERFLOW;
5166                 goto exit;
5167             }
5168 
5169             Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
5170 
5171             break;
5172         }
5173 
5174         case FileStreamInformation:
5175         {
5176             FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5177 
5178             TRACE("FileStreamInformation\n");
5179 
5180             Status = fill_in_file_stream_information(fsi, fileref, &length);
5181 
5182             break;
5183         }
5184 
5185 #if (NTDDI_VERSION >= NTDDI_VISTA)
5186         case FileHardLinkInformation:
5187         {
5188             FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
5189 
5190             TRACE("FileHardLinkInformation\n");
5191 
5192             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5193             Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
5194             ExReleaseResourceLite(&Vcb->tree_lock);
5195 
5196             break;
5197         }
5198 
5199         case FileNormalizedNameInformation:
5200         {
5201             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5202 
5203             TRACE("FileNormalizedNameInformation\n");
5204 
5205             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5206 
5207             break;
5208         }
5209 
5210         case FileStandardLinkInformation:
5211         {
5212             FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5213 
5214             TRACE("FileStandardLinkInformation\n");
5215 
5216             Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
5217 
5218             break;
5219         }
5220 
5221         case FileRemoteProtocolInformation:
5222             TRACE("FileRemoteProtocolInformation\n");
5223             Status = STATUS_INVALID_PARAMETER;
5224             goto exit;
5225 
5226 #ifndef _MSC_VER
5227 #pragma GCC diagnostic push
5228 #pragma GCC diagnostic ignored "-Wswitch"
5229 #endif
5230         case FileIdInformation:
5231         {
5232             FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5233 
5234             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
5235                 WARN("overflow\n");
5236                 Status = STATUS_BUFFER_OVERFLOW;
5237                 goto exit;
5238             }
5239 
5240             TRACE("FileIdInformation\n");
5241 
5242             Status = fill_in_file_id_information(fii, fcb, &length);
5243 
5244             break;
5245         }
5246 
5247         case FileStatInformation:
5248         {
5249             FILE_STAT_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5250 
5251             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
5252                 WARN("overflow\n");
5253                 Status = STATUS_BUFFER_OVERFLOW;
5254                 goto exit;
5255             }
5256 
5257             TRACE("FileStatInformation\n");
5258 
5259             Status = fill_in_file_stat_information(fsi, fcb, ccb, &length);
5260 
5261             break;
5262         }
5263 
5264         case FileStatLxInformation:
5265         {
5266             FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5267 
5268             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
5269                 WARN("overflow\n");
5270                 Status = STATUS_BUFFER_OVERFLOW;
5271                 goto exit;
5272             }
5273 
5274             TRACE("FileStatLxInformation\n");
5275 
5276             Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
5277 
5278             break;
5279         }
5280 
5281         case FileCaseSensitiveInformation:
5282         {
5283             FILE_CASE_SENSITIVE_INFORMATION* fcsi = Irp->AssociatedIrp.SystemBuffer;
5284 
5285             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) {
5286                 WARN("overflow\n");
5287                 Status = STATUS_BUFFER_OVERFLOW;
5288                 goto exit;
5289             }
5290 
5291             TRACE("FileCaseSensitiveInformation\n");
5292 
5293             Status = fill_in_file_case_sensitive_information(fcsi, fcb, &length);
5294 
5295             break;
5296         }
5297 
5298         case FileHardLinkFullIdInformation:
5299         {
5300             FILE_LINKS_FULL_ID_INFORMATION* flfii = Irp->AssociatedIrp.SystemBuffer;
5301 
5302             TRACE("FileHardLinkFullIdInformation\n");
5303 
5304             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5305             Status = fill_in_hard_link_full_id_information(flfii, fileref, Irp, &length);
5306             ExReleaseResourceLite(&Vcb->tree_lock);
5307 
5308             break;
5309         }
5310 #ifndef _MSC_VER
5311 #pragma GCC diagnostic pop
5312 #endif
5313 #endif
5314 
5315         default:
5316             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
5317             Status = STATUS_INVALID_PARAMETER;
5318             goto exit;
5319     }
5320 
5321     if (length < 0) {
5322         length = 0;
5323         Status = STATUS_BUFFER_OVERFLOW;
5324     }
5325 
5326     Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
5327 
5328 exit:
5329     TRACE("query_info returning %08lx\n", Status);
5330 
5331     return Status;
5332 }
5333 
5334 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
5335 _Function_class_(DRIVER_DISPATCH)
5336 NTSTATUS __stdcall drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5337     PIO_STACK_LOCATION IrpSp;
5338     NTSTATUS Status;
5339     fcb* fcb;
5340     device_extension* Vcb = DeviceObject->DeviceExtension;
5341     bool top_level;
5342 
5343     FsRtlEnterFileSystem();
5344 
5345     top_level = is_top_level(Irp);
5346 
5347     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5348         Status = vol_query_information(DeviceObject, Irp);
5349         goto end;
5350     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5351         Status = STATUS_INVALID_PARAMETER;
5352         goto end;
5353     }
5354 
5355     Irp->IoStatus.Information = 0;
5356 
5357     TRACE("query information\n");
5358 
5359     IrpSp = IoGetCurrentIrpStackLocation(Irp);
5360 
5361     fcb = IrpSp->FileObject->FsContext;
5362     TRACE("fcb = %p\n", fcb);
5363     TRACE("fcb->subvol = %p\n", fcb->subvol);
5364 
5365     Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
5366 
5367 end:
5368     TRACE("returning %08lx\n", Status);
5369 
5370     Irp->IoStatus.Status = Status;
5371 
5372     IoCompleteRequest( Irp, IO_NO_INCREMENT );
5373 
5374     if (top_level)
5375         IoSetTopLevelIrp(NULL);
5376 
5377     FsRtlExitFileSystem();
5378 
5379     return Status;
5380 }
5381 
5382 _Dispatch_type_(IRP_MJ_QUERY_EA)
5383 _Function_class_(DRIVER_DISPATCH)
5384 NTSTATUS __stdcall drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5385     NTSTATUS Status;
5386     bool top_level;
5387     device_extension* Vcb = DeviceObject->DeviceExtension;
5388     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5389     PFILE_OBJECT FileObject = IrpSp->FileObject;
5390     fcb* fcb;
5391     ccb* ccb;
5392     FILE_FULL_EA_INFORMATION* ffei;
5393     ULONG retlen = 0;
5394 
5395     FsRtlEnterFileSystem();
5396 
5397     TRACE("(%p, %p)\n", DeviceObject, Irp);
5398 
5399     top_level = is_top_level(Irp);
5400 
5401     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5402         Status = vol_query_ea(DeviceObject, Irp);
5403         goto end;
5404     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5405         Status = STATUS_INVALID_PARAMETER;
5406         goto end;
5407     }
5408 
5409     ffei = map_user_buffer(Irp, NormalPagePriority);
5410     if (!ffei) {
5411         ERR("could not get output buffer\n");
5412         Status = STATUS_INVALID_PARAMETER;
5413         goto end;
5414     }
5415 
5416     if (!FileObject) {
5417         ERR("no file object\n");
5418         Status = STATUS_INVALID_PARAMETER;
5419         goto end;
5420     }
5421 
5422     fcb = FileObject->FsContext;
5423 
5424     if (!fcb) {
5425         ERR("no fcb\n");
5426         Status = STATUS_INVALID_PARAMETER;
5427         goto end;
5428     }
5429 
5430     ccb = FileObject->FsContext2;
5431 
5432     if (!ccb) {
5433         ERR("no ccb\n");
5434         Status = STATUS_INVALID_PARAMETER;
5435         goto end;
5436     }
5437 
5438     if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
5439         WARN("insufficient privileges\n");
5440         Status = STATUS_ACCESS_DENIED;
5441         goto end;
5442     }
5443 
5444     if (fcb->ads)
5445         fcb = ccb->fileref->parent->fcb;
5446 
5447     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
5448 
5449     Status = STATUS_SUCCESS;
5450 
5451     if (fcb->ea_xattr.Length == 0)
5452         goto end2;
5453 
5454     if (IrpSp->Parameters.QueryEa.EaList) {
5455         FILE_FULL_EA_INFORMATION *ea, *out;
5456         FILE_GET_EA_INFORMATION* in;
5457 
5458         in = IrpSp->Parameters.QueryEa.EaList;
5459         do {
5460             STRING s;
5461 
5462             s.Length = s.MaximumLength = in->EaNameLength;
5463             s.Buffer = in->EaName;
5464 
5465             RtlUpperString(&s, &s);
5466 
5467             if (in->NextEntryOffset == 0)
5468                 break;
5469 
5470             in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5471         } while (true);
5472 
5473         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5474         out = NULL;
5475 
5476         do {
5477             bool found = false;
5478 
5479             in = IrpSp->Parameters.QueryEa.EaList;
5480             do {
5481                 if (in->EaNameLength == ea->EaNameLength &&
5482                     RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
5483                     found = true;
5484                     break;
5485                 }
5486 
5487                 if (in->NextEntryOffset == 0)
5488                     break;
5489 
5490                 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5491             } while (true);
5492 
5493             if (found) {
5494                 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5495 
5496                 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5497                     Status = STATUS_BUFFER_OVERFLOW;
5498                     retlen = 0;
5499                     goto end2;
5500                 }
5501 
5502                 retlen += padding;
5503 
5504                 if (out) {
5505                     out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5506                     out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5507                 } else
5508                     out = ffei;
5509 
5510                 out->NextEntryOffset = 0;
5511                 out->Flags = ea->Flags;
5512                 out->EaNameLength = ea->EaNameLength;
5513                 out->EaValueLength = ea->EaValueLength;
5514                 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5515 
5516                 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5517 
5518                 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5519                     break;
5520             }
5521 
5522             if (ea->NextEntryOffset == 0)
5523                 break;
5524 
5525             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5526         } while (true);
5527     } else {
5528         FILE_FULL_EA_INFORMATION *ea, *out;
5529         ULONG index;
5530 
5531         if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
5532             // The index is 1-based
5533             if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
5534                 Status = STATUS_NONEXISTENT_EA_ENTRY;
5535                 goto end2;
5536             } else
5537                 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
5538         } else if (IrpSp->Flags & SL_RESTART_SCAN)
5539             index = ccb->ea_index = 0;
5540         else
5541             index = ccb->ea_index;
5542 
5543         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5544 
5545         if (index > 0) {
5546             ULONG i;
5547 
5548             for (i = 0; i < index; i++) {
5549                 if (ea->NextEntryOffset == 0) // last item
5550                     goto end2;
5551 
5552                 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5553             }
5554         }
5555 
5556         out = NULL;
5557 
5558         do {
5559             uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5560 
5561             if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5562                 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
5563                 goto end2;
5564             }
5565 
5566             retlen += padding;
5567 
5568             if (out) {
5569                 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5570                 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5571             } else
5572                 out = ffei;
5573 
5574             out->NextEntryOffset = 0;
5575             out->Flags = ea->Flags;
5576             out->EaNameLength = ea->EaNameLength;
5577             out->EaValueLength = ea->EaValueLength;
5578             RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5579 
5580             retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5581 
5582             if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
5583                 ccb->ea_index++;
5584 
5585             if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5586                 break;
5587 
5588             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5589         } while (true);
5590     }
5591 
5592 end2:
5593     ExReleaseResourceLite(fcb->Header.Resource);
5594 
5595 end:
5596     TRACE("returning %08lx\n", Status);
5597 
5598     Irp->IoStatus.Status = Status;
5599     Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
5600 
5601     IoCompleteRequest( Irp, IO_NO_INCREMENT );
5602 
5603     if (top_level)
5604         IoSetTopLevelIrp(NULL);
5605 
5606     FsRtlExitFileSystem();
5607 
5608     return Status;
5609 }
5610 
5611 _Dispatch_type_(IRP_MJ_SET_EA)
5612 _Function_class_(DRIVER_DISPATCH)
5613 NTSTATUS __stdcall drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5614     device_extension* Vcb = DeviceObject->DeviceExtension;
5615     NTSTATUS Status;
5616     bool top_level;
5617     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5618     PFILE_OBJECT FileObject = IrpSp->FileObject;
5619     fcb* fcb;
5620     ccb* ccb;
5621     file_ref* fileref;
5622     FILE_FULL_EA_INFORMATION* ffei;
5623     ULONG offset;
5624     LIST_ENTRY ealist;
5625     ea_item* item;
5626     FILE_FULL_EA_INFORMATION* ea;
5627     LIST_ENTRY* le;
5628     LARGE_INTEGER time;
5629     BTRFS_TIME now;
5630 
5631     FsRtlEnterFileSystem();
5632 
5633     TRACE("(%p, %p)\n", DeviceObject, Irp);
5634 
5635     top_level = is_top_level(Irp);
5636 
5637     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5638         Status = vol_set_ea(DeviceObject, Irp);
5639         goto end;
5640     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5641         Status = STATUS_INVALID_PARAMETER;
5642         goto end;
5643     }
5644 
5645     if (Vcb->readonly) {
5646         Status = STATUS_MEDIA_WRITE_PROTECTED;
5647         goto end;
5648     }
5649 
5650     ffei = map_user_buffer(Irp, NormalPagePriority);
5651     if (!ffei) {
5652         ERR("could not get output buffer\n");
5653         Status = STATUS_INVALID_PARAMETER;
5654         goto end;
5655     }
5656 
5657     Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
5658     if (!NT_SUCCESS(Status)) {
5659         ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
5660         goto end;
5661     }
5662 
5663     if (!FileObject) {
5664         ERR("no file object\n");
5665         Status = STATUS_INVALID_PARAMETER;
5666         goto end;
5667     }
5668 
5669     fcb = FileObject->FsContext;
5670 
5671     if (!fcb) {
5672         ERR("no fcb\n");
5673         Status = STATUS_INVALID_PARAMETER;
5674         goto end;
5675     }
5676 
5677     ccb = FileObject->FsContext2;
5678 
5679     if (!ccb) {
5680         ERR("no ccb\n");
5681         Status = STATUS_INVALID_PARAMETER;
5682         goto end;
5683     }
5684 
5685     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
5686         WARN("insufficient privileges\n");
5687         Status = STATUS_ACCESS_DENIED;
5688         goto end;
5689     }
5690 
5691     if (fcb->ads) {
5692         fileref = ccb->fileref->parent;
5693         fcb = fileref->fcb;
5694     } else
5695         fileref = ccb->fileref;
5696 
5697     InitializeListHead(&ealist);
5698 
5699     ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
5700 
5701     if (fcb->ea_xattr.Length > 0) {
5702         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5703 
5704         do {
5705             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5706             if (!item) {
5707                 ERR("out of memory\n");
5708                 Status = STATUS_INSUFFICIENT_RESOURCES;
5709                 goto end2;
5710             }
5711 
5712             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5713             item->name.Buffer = ea->EaName;
5714 
5715             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5716             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5717 
5718             item->flags = ea->Flags;
5719 
5720             InsertTailList(&ealist, &item->list_entry);
5721 
5722             if (ea->NextEntryOffset == 0)
5723                 break;
5724 
5725             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5726         } while (true);
5727     }
5728 
5729     ea = ffei;
5730 
5731     do {
5732         STRING s;
5733         bool found = false;
5734 
5735         s.Length = s.MaximumLength = ea->EaNameLength;
5736         s.Buffer = ea->EaName;
5737 
5738         RtlUpperString(&s, &s);
5739 
5740         le = ealist.Flink;
5741         while (le != &ealist) {
5742             item = CONTAINING_RECORD(le, ea_item, list_entry);
5743 
5744             if (item->name.Length == s.Length &&
5745                 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
5746                 item->flags = ea->Flags;
5747                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5748                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5749                 found = true;
5750                 break;
5751             }
5752 
5753             le = le->Flink;
5754         }
5755 
5756         if (!found) {
5757             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5758             if (!item) {
5759                 ERR("out of memory\n");
5760                 Status = STATUS_INSUFFICIENT_RESOURCES;
5761                 goto end2;
5762             }
5763 
5764             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5765             item->name.Buffer = ea->EaName;
5766 
5767             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5768             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5769 
5770             item->flags = ea->Flags;
5771 
5772             InsertTailList(&ealist, &item->list_entry);
5773         }
5774 
5775         if (ea->NextEntryOffset == 0)
5776             break;
5777 
5778         ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5779     } while (true);
5780 
5781     // remove entries with zero-length value
5782     le = ealist.Flink;
5783     while (le != &ealist) {
5784         LIST_ENTRY* le2 = le->Flink;
5785 
5786         item = CONTAINING_RECORD(le, ea_item, list_entry);
5787 
5788         if (item->value.Length == 0) {
5789             RemoveEntryList(&item->list_entry);
5790             ExFreePool(item);
5791         }
5792 
5793         le = le2;
5794     }
5795 
5796     // handle LXSS values
5797     le = ealist.Flink;
5798     while (le != &ealist) {
5799         LIST_ENTRY* le2 = le->Flink;
5800 
5801         item = CONTAINING_RECORD(le, ea_item, list_entry);
5802 
5803         if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
5804             if (item->value.Length < sizeof(uint32_t)) {
5805                 ERR("uid value was shorter than expected\n");
5806                 Status = STATUS_INVALID_PARAMETER;
5807                 goto end2;
5808             }
5809 
5810             if (Irp->RequestorMode == KernelMode) {
5811                 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t));
5812                 fcb->sd_dirty = true;
5813                 fcb->sd_deleted = false;
5814             }
5815 
5816             RemoveEntryList(&item->list_entry);
5817             ExFreePool(item);
5818         } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
5819             if (item->value.Length < sizeof(uint32_t)) {
5820                 ERR("gid value was shorter than expected\n");
5821                 Status = STATUS_INVALID_PARAMETER;
5822                 goto end2;
5823             }
5824 
5825             if (Irp->RequestorMode == KernelMode)
5826                 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
5827 
5828             RemoveEntryList(&item->list_entry);
5829             ExFreePool(item);
5830         } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
5831             if (item->value.Length < sizeof(uint32_t)) {
5832                 ERR("mode value was shorter than expected\n");
5833                 Status = STATUS_INVALID_PARAMETER;
5834                 goto end2;
5835             }
5836 
5837             if (Irp->RequestorMode == KernelMode) {
5838                 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;
5839                 uint32_t val;
5840 
5841                 RtlCopyMemory(&val, item->value.Buffer, sizeof(uint32_t));
5842 
5843                 fcb->inode_item.st_mode &= ~allowed;
5844                 fcb->inode_item.st_mode |= val & allowed;
5845             }
5846 
5847             RemoveEntryList(&item->list_entry);
5848             ExFreePool(item);
5849         }
5850 
5851         le = le2;
5852     }
5853 
5854     if (IsListEmpty(&ealist)) {
5855         fcb->ealen = 0;
5856 
5857         if (fcb->ea_xattr.Buffer)
5858             ExFreePool(fcb->ea_xattr.Buffer);
5859 
5860         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
5861         fcb->ea_xattr.Buffer = NULL;
5862     } else {
5863         uint16_t size = 0;
5864         char *buf, *oldbuf;
5865 
5866         le = ealist.Flink;
5867         while (le != &ealist) {
5868             item = CONTAINING_RECORD(le, ea_item, list_entry);
5869 
5870             if (size % 4 > 0)
5871                 size += 4 - (size % 4);
5872 
5873             size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
5874 
5875             le = le->Flink;
5876         }
5877 
5878         buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
5879         if (!buf) {
5880             ERR("out of memory\n");
5881             Status = STATUS_INSUFFICIENT_RESOURCES;
5882             goto end2;
5883         }
5884 
5885         oldbuf = fcb->ea_xattr.Buffer;
5886 
5887         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
5888         fcb->ea_xattr.Buffer = buf;
5889 
5890         fcb->ealen = 4;
5891         ea = NULL;
5892 
5893         le = ealist.Flink;
5894         while (le != &ealist) {
5895             item = CONTAINING_RECORD(le, ea_item, list_entry);
5896 
5897             if (ea) {
5898                 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
5899 
5900                 if (ea->NextEntryOffset % 4 > 0)
5901                     ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
5902 
5903                 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5904             } else
5905                 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5906 
5907             ea->NextEntryOffset = 0;
5908             ea->Flags = item->flags;
5909             ea->EaNameLength = (UCHAR)item->name.Length;
5910             ea->EaValueLength = item->value.Length;
5911 
5912             RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
5913             ea->EaName[item->name.Length] = 0;
5914             RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
5915 
5916             fcb->ealen += 5 + item->name.Length + item->value.Length;
5917 
5918             le = le->Flink;
5919         }
5920 
5921         if (oldbuf)
5922             ExFreePool(oldbuf);
5923     }
5924 
5925     fcb->ea_changed = true;
5926 
5927     KeQuerySystemTime(&time);
5928     win_time_to_unix(time, &now);
5929 
5930     fcb->inode_item.transid = Vcb->superblock.generation;
5931     fcb->inode_item.sequence++;
5932 
5933     if (!ccb->user_set_change_time)
5934         fcb->inode_item.st_ctime = now;
5935 
5936     fcb->inode_item_changed = true;
5937     mark_fcb_dirty(fcb);
5938 
5939     send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
5940 
5941     Status = STATUS_SUCCESS;
5942 
5943 end2:
5944     ExReleaseResourceLite(fcb->Header.Resource);
5945 
5946     while (!IsListEmpty(&ealist)) {
5947         le = RemoveHeadList(&ealist);
5948 
5949         item = CONTAINING_RECORD(le, ea_item, list_entry);
5950 
5951         ExFreePool(item);
5952     }
5953 
5954 end:
5955     TRACE("returning %08lx\n", Status);
5956 
5957     Irp->IoStatus.Status = Status;
5958     Irp->IoStatus.Information = 0;
5959 
5960     IoCompleteRequest(Irp, IO_NO_INCREMENT);
5961 
5962     if (top_level)
5963         IoSetTopLevelIrp(NULL);
5964 
5965     FsRtlExitFileSystem();
5966 
5967     return Status;
5968 }
5969