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