1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "btrfs_drv.h"
19 
20 #if (NTDDI_VERSION >= NTDDI_WIN10)
21 // not currently in mingw - introduced with Windows 10
22 #ifndef FileIdInformation
23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
24 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
25 
26 typedef struct _FILE_STAT_LX_INFORMATION {
27     LARGE_INTEGER FileId;
28     LARGE_INTEGER CreationTime;
29     LARGE_INTEGER LastAccessTime;
30     LARGE_INTEGER LastWriteTime;
31     LARGE_INTEGER ChangeTime;
32     LARGE_INTEGER AllocationSize;
33     LARGE_INTEGER EndOfFile;
34     ULONG         FileAttributes;
35     ULONG         ReparseTag;
36     ULONG         NumberOfLinks;
37     ACCESS_MASK   EffectiveAccess;
38     ULONG         LxFlags;
39     ULONG         LxUid;
40     ULONG         LxGid;
41     ULONG         LxMode;
42     ULONG         LxDeviceIdMajor;
43     ULONG         LxDeviceIdMinor;
44 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
45 
46 #define LX_FILE_METADATA_HAS_UID        0x01
47 #define LX_FILE_METADATA_HAS_GID        0x02
48 #define LX_FILE_METADATA_HAS_MODE       0x04
49 #define LX_FILE_METADATA_HAS_DEVICE_ID  0x08
50 #define LX_FILE_CASE_SENSITIVE_DIR      0x10
51 
52 #endif
53 #endif
54 
55 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
56     FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
57     fcb* fcb = FileObject->FsContext;
58     ccb* ccb = FileObject->FsContext2;
59     file_ref* fileref = ccb ? ccb->fileref : NULL;
60     ULONG defda, filter = 0;
61     BOOL inode_item_changed = FALSE;
62     NTSTATUS Status;
63 
64     if (fcb->ads) {
65         if (fileref && fileref->parent)
66             fcb = fileref->parent->fcb;
67         else {
68             ERR("stream did not have fileref\n");
69             return STATUS_INTERNAL_ERROR;
70         }
71     }
72 
73     if (!ccb) {
74         ERR("ccb was NULL\n");
75         return STATUS_INVALID_PARAMETER;
76     }
77 
78     TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes);
79 
80     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
81 
82     if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
83         WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
84         Status = STATUS_INVALID_PARAMETER;
85         goto end;
86     }
87 
88     if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) &&
89         (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
90         Status = STATUS_ACCESS_DENIED;
91         goto end;
92     }
93 
94     // don't allow readonly subvol to be made r/w if send operation running on it
95     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY &&
96         fcb->subvol->send_ops > 0) {
97         Status = STATUS_DEVICE_NOT_READY;
98         goto end;
99     }
100 
101     // times of -2 are some sort of undocumented behaviour to do with LXSS
102 
103     if (fbi->CreationTime.QuadPart == -2)
104         fbi->CreationTime.QuadPart = 0;
105 
106     if (fbi->LastAccessTime.QuadPart == -2)
107         fbi->LastAccessTime.QuadPart = 0;
108 
109     if (fbi->LastWriteTime.QuadPart == -2)
110         fbi->LastWriteTime.QuadPart = 0;
111 
112     if (fbi->ChangeTime.QuadPart == -2)
113         fbi->ChangeTime.QuadPart = 0;
114 
115     if (fbi->CreationTime.QuadPart == -1)
116         ccb->user_set_creation_time = TRUE;
117     else if (fbi->CreationTime.QuadPart != 0) {
118         win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
119         inode_item_changed = TRUE;
120         filter |= FILE_NOTIFY_CHANGE_CREATION;
121 
122         ccb->user_set_creation_time = TRUE;
123     }
124 
125     if (fbi->LastAccessTime.QuadPart == -1)
126         ccb->user_set_access_time = TRUE;
127     else if (fbi->LastAccessTime.QuadPart != 0) {
128         win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
129         inode_item_changed = TRUE;
130         filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
131 
132         ccb->user_set_access_time = TRUE;
133     }
134 
135     if (fbi->LastWriteTime.QuadPart == -1)
136         ccb->user_set_write_time = TRUE;
137     else if (fbi->LastWriteTime.QuadPart != 0) {
138         win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
139         inode_item_changed = TRUE;
140         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
141 
142         ccb->user_set_write_time = TRUE;
143     }
144 
145     if (fbi->ChangeTime.QuadPart == -1)
146         ccb->user_set_change_time = TRUE;
147     else if (fbi->ChangeTime.QuadPart != 0) {
148         win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
149         inode_item_changed = TRUE;
150         // no filter for this
151 
152         ccb->user_set_change_time = TRUE;
153     }
154 
155     // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
156     if (fbi->FileAttributes != 0) {
157         LARGE_INTEGER time;
158         BTRFS_TIME now;
159 
160         fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
161 
162         defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.',
163                                     TRUE, Irp);
164 
165         if (fcb->type == BTRFS_TYPE_DIRECTORY)
166             fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
167         else if (fcb->type == BTRFS_TYPE_SYMLINK)
168             fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
169 
170         fcb->atts_changed = TRUE;
171 
172         if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)
173             fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
174 
175         if (defda == fbi->FileAttributes)
176             fcb->atts_deleted = TRUE;
177         else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY))
178             fcb->atts_deleted = TRUE;
179 
180         fcb->atts = fbi->FileAttributes;
181 
182         KeQuerySystemTime(&time);
183         win_time_to_unix(time, &now);
184 
185         if (!ccb->user_set_change_time)
186             fcb->inode_item.st_ctime = now;
187 
188         fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
189         fcb->subvol->root_item.ctime = now;
190 
191         if (fcb->inode == SUBVOL_ROOT_INODE) {
192             if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)
193                 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY;
194             else
195                 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY;
196         }
197 
198         inode_item_changed = TRUE;
199 
200         filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
201     }
202 
203     if (inode_item_changed) {
204         fcb->inode_item.transid = Vcb->superblock.generation;
205         fcb->inode_item.sequence++;
206         fcb->inode_item_changed = TRUE;
207 
208         mark_fcb_dirty(fcb);
209     }
210 
211     if (filter != 0)
212         send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
213 
214     Status = STATUS_SUCCESS;
215 
216 end:
217     ExReleaseResourceLite(fcb->Header.Resource);
218 
219     return Status;
220 }
221 
222 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
223     FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
224     fcb* fcb = FileObject->FsContext;
225     ccb* ccb = FileObject->FsContext2;
226     file_ref* fileref = ccb ? ccb->fileref : NULL;
227     ULONG atts;
228     NTSTATUS Status;
229 
230     if (!fileref)
231         return STATUS_INVALID_PARAMETER;
232 
233     acquire_fcb_lock_exclusive(Vcb);
234 
235     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
236 
237     TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
238 
239     if (fcb->ads) {
240         if (fileref->parent)
241             atts = fileref->parent->fcb->atts;
242         else {
243             ERR("no fileref for stream\n");
244             Status = STATUS_INTERNAL_ERROR;
245             goto end;
246         }
247     } else
248         atts = fcb->atts;
249 
250     TRACE("atts = %x\n", atts);
251 
252     if (atts & FILE_ATTRIBUTE_READONLY) {
253         TRACE("not allowing readonly file to be deleted\n");
254         Status = STATUS_CANNOT_DELETE;
255         goto end;
256     }
257 
258     // FIXME - can we skip this bit for subvols?
259     if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
260         TRACE("directory not empty\n");
261         Status = STATUS_DIRECTORY_NOT_EMPTY;
262         goto end;
263     }
264 
265     if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
266         TRACE("trying to delete file which is being mapped as an image\n");
267         Status = STATUS_CANNOT_DELETE;
268         goto end;
269     }
270 
271     ccb->fileref->delete_on_close = fdi->DeleteFile;
272 
273     FileObject->DeletePending = fdi->DeleteFile;
274 
275     Status = STATUS_SUCCESS;
276 
277 end:
278     ExReleaseResourceLite(fcb->Header.Resource);
279 
280     release_fcb_lock(Vcb);
281 
282     // send notification that directory is about to be deleted
283     if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
284         FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
285                                        NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
286     }
287 
288     return Status;
289 }
290 
291 BOOL has_open_children(file_ref* fileref) {
292     LIST_ENTRY* le = fileref->children.Flink;
293 
294     if (IsListEmpty(&fileref->children))
295         return FALSE;
296 
297     while (le != &fileref->children) {
298         file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
299 
300         if (c->open_count > 0)
301             return TRUE;
302 
303         if (has_open_children(c))
304             return TRUE;
305 
306         le = le->Flink;
307     }
308 
309     return FALSE;
310 }
311 
312 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
313     device_extension* Vcb = oldfcb->Vcb;
314     fcb* fcb;
315     LIST_ENTRY* le;
316 
317     // FIXME - we can skip a lot of this if the inode is about to be deleted
318 
319     fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
320     if (!fcb) {
321         ERR("out of memory\n");
322         return STATUS_INSUFFICIENT_RESOURCES;
323     }
324 
325     fcb->Vcb = Vcb;
326 
327     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
328     fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
329     fcb->Header.FileSize = oldfcb->Header.FileSize;
330     fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength;
331 
332     fcb->type = oldfcb->type;
333 
334     if (oldfcb->ads) {
335         fcb->ads = TRUE;
336         fcb->adshash = oldfcb->adshash;
337         fcb->adsmaxlen = oldfcb->adsmaxlen;
338 
339         if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
340             fcb->adsxattr.Length = oldfcb->adsxattr.Length;
341             fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
342             fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
343 
344             if (!fcb->adsxattr.Buffer) {
345                 ERR("out of memory\n");
346                 free_fcb(Vcb, fcb);
347                 return STATUS_INSUFFICIENT_RESOURCES;
348             }
349 
350             RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
351             fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
352         }
353 
354         if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
355             fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
356             fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
357 
358             if (!fcb->adsdata.Buffer) {
359                 ERR("out of memory\n");
360                 free_fcb(Vcb, fcb);
361                 return STATUS_INSUFFICIENT_RESOURCES;
362             }
363 
364             RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
365         }
366 
367         goto end;
368     }
369 
370     RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM));
371     fcb->inode_item_changed = TRUE;
372 
373     if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) {
374         fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
375         if (!fcb->sd) {
376             ERR("out of memory\n");
377             free_fcb(Vcb, fcb);
378             return STATUS_INSUFFICIENT_RESOURCES;
379         }
380 
381         RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd));
382     }
383 
384     fcb->atts = oldfcb->atts;
385 
386     le = oldfcb->extents.Flink;
387     while (le != &oldfcb->extents) {
388         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
389 
390         if (!ext->ignore) {
391             extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
392 
393             if (!ext2) {
394                 ERR("out of memory\n");
395                 free_fcb(Vcb, fcb);
396                 return STATUS_INSUFFICIENT_RESOURCES;
397             }
398 
399             ext2->offset = ext->offset;
400             ext2->datalen = ext->datalen;
401 
402             if (ext2->datalen > 0)
403                 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
404 
405             ext2->unique = FALSE;
406             ext2->ignore = FALSE;
407             ext2->inserted = TRUE;
408 
409             if (ext->csum) {
410                 ULONG len;
411                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
412 
413                 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
414                     len = (ULONG)ed2->num_bytes;
415                 else
416                     len = (ULONG)ed2->size;
417 
418                 len = len * sizeof(UINT32) / Vcb->superblock.sector_size;
419 
420                 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
421                 if (!ext2->csum) {
422                     ERR("out of memory\n");
423                     free_fcb(Vcb, fcb);
424                     return STATUS_INSUFFICIENT_RESOURCES;
425                 }
426 
427                 RtlCopyMemory(ext2->csum, ext->csum, len);
428             } else
429                 ext2->csum = NULL;
430 
431             InsertTailList(&fcb->extents, &ext2->list_entry);
432         }
433 
434         le = le->Flink;
435     }
436 
437     le = oldfcb->hardlinks.Flink;
438     while (le != &oldfcb->hardlinks) {
439         hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2;
440 
441         hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
442 
443         if (!hl2) {
444             ERR("out of memory\n");
445             free_fcb(Vcb, fcb);
446             return STATUS_INSUFFICIENT_RESOURCES;
447         }
448 
449         hl2->parent = hl->parent;
450         hl2->index = hl->index;
451 
452         hl2->name.Length = hl2->name.MaximumLength = hl->name.Length;
453         hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG);
454 
455         if (!hl2->name.Buffer) {
456             ERR("out of memory\n");
457             ExFreePool(hl2);
458             free_fcb(Vcb, fcb);
459             return STATUS_INSUFFICIENT_RESOURCES;
460         }
461 
462         RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length);
463 
464         hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length;
465         hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG);
466 
467         if (!hl2->utf8.Buffer) {
468             ERR("out of memory\n");
469             ExFreePool(hl2->name.Buffer);
470             ExFreePool(hl2);
471             free_fcb(Vcb, fcb);
472             return STATUS_INSUFFICIENT_RESOURCES;
473         }
474 
475         RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
476 
477         InsertTailList(&fcb->hardlinks, &hl2->list_entry);
478 
479         le = le->Flink;
480     }
481 
482     if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
483         fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
484 
485         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
486         if (!fcb->reparse_xattr.Buffer) {
487             ERR("out of memory\n");
488             free_fcb(Vcb, fcb);
489             return STATUS_INSUFFICIENT_RESOURCES;
490         }
491 
492         RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
493     }
494 
495     if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) {
496         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length;
497 
498         fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
499         if (!fcb->ea_xattr.Buffer) {
500             ERR("out of memory\n");
501             free_fcb(Vcb, fcb);
502             return STATUS_INSUFFICIENT_RESOURCES;
503         }
504 
505         RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
506     }
507 
508     fcb->prop_compression = oldfcb->prop_compression;
509 
510     le = oldfcb->xattrs.Flink;
511     while (le != &oldfcb->xattrs) {
512         xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
513 
514         if (xa->valuelen > 0) {
515             xattr* xa2;
516 
517             xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG);
518 
519             if (!xa2) {
520                 ERR("out of memory\n");
521                 free_fcb(Vcb, fcb);
522                 return STATUS_INSUFFICIENT_RESOURCES;
523             }
524 
525             xa2->namelen = xa->namelen;
526             xa2->valuelen = xa->valuelen;
527             xa2->dirty = xa->dirty;
528             memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen);
529 
530             InsertTailList(&fcb->xattrs, &xa2->list_entry);
531         }
532 
533         le = le->Flink;
534     }
535 
536 end:
537     *pfcb = fcb;
538 
539     return STATUS_SUCCESS;
540 }
541 
542 typedef struct _move_entry {
543     file_ref* fileref;
544     fcb* dummyfcb;
545     file_ref* dummyfileref;
546     struct _move_entry* parent;
547     LIST_ENTRY list_entry;
548 } move_entry;
549 
550 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
551     NTSTATUS Status;
552     LIST_ENTRY* le;
553 
554     ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, TRUE);
555 
556     le = me->fileref->fcb->dir_children_index.Flink;
557 
558     while (le != &me->fileref->fcb->dir_children_index) {
559         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
560         file_ref* fr;
561         move_entry* me2;
562 
563         Status = open_fileref_child(Vcb, me->fileref, &dc->name, TRUE, TRUE, dc->index == 0 ? TRUE : FALSE, PagedPool, &fr, Irp);
564 
565         if (!NT_SUCCESS(Status)) {
566             ERR("open_fileref_child returned %08x\n", Status);
567             ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
568             return Status;
569         }
570 
571         me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
572         if (!me2) {
573             ERR("out of memory\n");
574             ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
575             return STATUS_INSUFFICIENT_RESOURCES;
576         }
577 
578         me2->fileref = fr;
579         me2->dummyfcb = NULL;
580         me2->dummyfileref = NULL;
581         me2->parent = me;
582 
583         InsertHeadList(&me->list_entry, &me2->list_entry);
584 
585         le = le->Flink;
586     }
587 
588     ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
589 
590     return STATUS_SUCCESS;
591 }
592 
593 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
594     UINT8 c;
595 
596     c = dc->hash >> 24;
597 
598     if (fcb->hash_ptrs[c] == &dc->list_entry_hash) {
599         if (dc->list_entry_hash.Flink == &fcb->dir_children_hash)
600             fcb->hash_ptrs[c] = NULL;
601         else {
602             dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash);
603 
604             if (dc2->hash >> 24 == c)
605                 fcb->hash_ptrs[c] = &dc2->list_entry_hash;
606             else
607                 fcb->hash_ptrs[c] = NULL;
608         }
609     }
610 
611     RemoveEntryList(&dc->list_entry_hash);
612 
613     c = dc->hash_uc >> 24;
614 
615     if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) {
616         if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc)
617             fcb->hash_ptrs_uc[c] = NULL;
618         else {
619             dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc);
620 
621             if (dc2->hash_uc >> 24 == c)
622                 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc;
623             else
624                 fcb->hash_ptrs_uc[c] = NULL;
625         }
626     }
627 
628     RemoveEntryList(&dc->list_entry_hash_uc);
629 }
630 
631 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) {
632     NTSTATUS Status;
633     fcb* fcb;
634     SECURITY_SUBJECT_CONTEXT subjcont;
635     PSID owner;
636     BOOLEAN defaulted;
637     LARGE_INTEGER time;
638     BTRFS_TIME now;
639 
640     fcb = create_fcb(Vcb, PagedPool);
641     if (!fcb) {
642         ERR("out of memory\n");
643         return STATUS_INSUFFICIENT_RESOURCES;
644     }
645 
646     KeQuerySystemTime(&time);
647     win_time_to_unix(time, &now);
648 
649     fcb->Vcb = Vcb;
650 
651     fcb->subvol = r;
652     fcb->inode = InterlockedIncrement64(&r->lastinode);
653     fcb->type = BTRFS_TYPE_DIRECTORY;
654 
655     fcb->inode_item.generation = Vcb->superblock.generation;
656     fcb->inode_item.transid = Vcb->superblock.generation;
657     fcb->inode_item.st_nlink = 1;
658     fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, TRUE);
659     fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now;
660     fcb->inode_item.st_gid = GID_NOBODY;
661 
662     fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, FALSE, TRUE, NULL);
663 
664     SeCaptureSubjectContext(&subjcont);
665 
666     Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
667 
668     if (!NT_SUCCESS(Status)) {
669         ERR("SeAssignSecurity returned %08x\n", Status);
670         return Status;
671     }
672 
673     if (!fcb->sd) {
674         ERR("SeAssignSecurity returned NULL security descriptor\n");
675         return STATUS_INTERNAL_ERROR;
676     }
677 
678     Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
679     if (!NT_SUCCESS(Status)) {
680         ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
681         fcb->inode_item.st_uid = UID_NOBODY;
682         fcb->sd_dirty = TRUE;
683     } else {
684         fcb->inode_item.st_uid = sid_to_uid(owner);
685         fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
686     }
687 
688     find_gid(fcb, parfcb, &subjcont);
689 
690     fcb->inode_item_changed = TRUE;
691 
692     InsertTailList(&r->fcbs, &fcb->list_entry);
693     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
694 
695     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
696     fcb->Header.AllocationSize.QuadPart = 0;
697     fcb->Header.FileSize.QuadPart = 0;
698     fcb->Header.ValidDataLength.QuadPart = 0;
699 
700     fcb->created = TRUE;
701     mark_fcb_dirty(fcb);
702 
703     if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
704         fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
705 
706     fcb->prop_compression = parfcb->prop_compression;
707     fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
708 
709     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
710     if (!fcb->hash_ptrs) {
711         ERR("out of memory\n");
712         return STATUS_INSUFFICIENT_RESOURCES;
713     }
714 
715     RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
716 
717     fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
718     if (!fcb->hash_ptrs_uc) {
719         ERR("out of memory\n");
720         return STATUS_INSUFFICIENT_RESOURCES;
721     }
722 
723     RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
724 
725     *pfcb = fcb;
726 
727     return STATUS_SUCCESS;
728 }
729 
730 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
731     NTSTATUS Status;
732     LIST_ENTRY move_list, *le;
733     move_entry* me;
734     LARGE_INTEGER time;
735     BTRFS_TIME now;
736     file_ref* origparent;
737 
738     InitializeListHead(&move_list);
739 
740     KeQuerySystemTime(&time);
741     win_time_to_unix(time, &now);
742 
743     me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
744 
745     if (!me) {
746         ERR("out of memory\n");
747         Status = STATUS_INSUFFICIENT_RESOURCES;
748         goto end;
749     }
750 
751     origparent = fileref->parent;
752 
753     me->fileref = fileref;
754     increase_fileref_refcount(me->fileref);
755     me->dummyfcb = NULL;
756     me->dummyfileref = NULL;
757     me->parent = NULL;
758 
759     InsertTailList(&move_list, &me->list_entry);
760 
761     le = move_list.Flink;
762     while (le != &move_list) {
763         me = CONTAINING_RECORD(le, move_entry, list_entry);
764 
765         ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
766 
767         if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
768             Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
769 
770             if (!NT_SUCCESS(Status)) {
771                 ERR("add_children_to_move_list returned %08x\n", Status);
772                 goto end;
773             }
774         }
775 
776         ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
777 
778         le = le->Flink;
779     }
780 
781     send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
782 
783     // loop through list and create new inodes
784 
785     le = move_list.Flink;
786     while (le != &move_list) {
787         me = CONTAINING_RECORD(le, move_entry, list_entry);
788 
789         if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) {
790             if (!me->dummyfcb) {
791                 ULONG defda;
792                 BOOL inserted = FALSE;
793                 LIST_ENTRY* le3;
794 
795                 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
796 
797                 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb);
798                 if (!NT_SUCCESS(Status)) {
799                     ERR("duplicate_fcb returned %08x\n", Status);
800                     ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
801                     goto end;
802                 }
803 
804                 me->dummyfcb->subvol = me->fileref->fcb->subvol;
805                 me->dummyfcb->inode = me->fileref->fcb->inode;
806 
807                 if (!me->dummyfcb->ads) {
808                     me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty;
809                     me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed;
810                     me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted;
811                     me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed;
812                     me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed;
813                     me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed;
814                 }
815 
816                 me->dummyfcb->created = me->fileref->fcb->created;
817                 me->dummyfcb->deleted = me->fileref->fcb->deleted;
818                 mark_fcb_dirty(me->dummyfcb);
819 
820                 if (!me->fileref->fcb->ads) {
821                     LIST_ENTRY* le2;
822 
823                     me->fileref->fcb->subvol = destdir->fcb->subvol;
824                     me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
825                     me->fileref->fcb->inode_item.st_nlink = 1;
826 
827                     defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
828                                                 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
829                                                 TRUE, Irp);
830 
831                     me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
832                     me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
833                     me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents);
834                     me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer;
835                     me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
836                     me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs);
837                     me->fileref->fcb->inode_item_changed = TRUE;
838 
839                     le2 = me->fileref->fcb->xattrs.Flink;
840                     while (le2 != &me->fileref->fcb->xattrs) {
841                         xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry);
842 
843                         xa->dirty = TRUE;
844 
845                         le2 = le2->Flink;
846                     }
847 
848                     if (le == move_list.Flink) { // first entry
849                         me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
850                         me->fileref->fcb->inode_item.sequence++;
851 
852                         if (!ccb->user_set_change_time)
853                             me->fileref->fcb->inode_item.st_ctime = now;
854                     }
855 
856                     le2 = me->fileref->fcb->extents.Flink;
857                     while (le2 != &me->fileref->fcb->extents) {
858                         extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
859 
860                         if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
861                             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
862 
863                             if (ed2->size != 0) {
864                                 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
865 
866                                 if (!c) {
867                                     ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
868                                 } else {
869                                     Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
870                                                                        ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
871 
872                                     if (!NT_SUCCESS(Status)) {
873                                         ERR("update_changed_extent_ref returned %08x\n", Status);
874                                         ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
875                                         goto end;
876                                     }
877                                 }
878 
879                             }
880                         }
881 
882                         le2 = le2->Flink;
883                     }
884                 } else {
885                     me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol;
886                     me->fileref->fcb->inode = me->parent->fileref->fcb->inode;
887                 }
888 
889                 me->fileref->fcb->created = TRUE;
890 
891                 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry);
892                 RemoveEntryList(&me->fileref->fcb->list_entry);
893 
894                 le3 = destdir->fcb->subvol->fcbs.Flink;
895                 while (le3 != &destdir->fcb->subvol->fcbs) {
896                     fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry);
897 
898                     if (fcb->inode > me->fileref->fcb->inode) {
899                         InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry);
900                         inserted = TRUE;
901                         break;
902                     }
903 
904                     le3 = le3->Flink;
905                 }
906 
907                 if (!inserted)
908                     InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry);
909 
910                 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
911 
912                 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
913                     hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
914 
915                     if (hl->name.Buffer)
916                         ExFreePool(hl->name.Buffer);
917 
918                     if (hl->utf8.Buffer)
919                         ExFreePool(hl->utf8.Buffer);
920 
921                     ExFreePool(hl);
922                 }
923 
924                 me->fileref->fcb->inode_item_changed = TRUE;
925                 mark_fcb_dirty(me->fileref->fcb);
926 
927                 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) {
928                     LIST_ENTRY* le2 = le->Flink;
929 
930                     while (le2 != &move_list) {
931                         move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry);
932 
933                         if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) {
934                             me2->dummyfcb = me->dummyfcb;
935                             InterlockedIncrement(&me->dummyfcb->refcount);
936                         }
937 
938                         le2 = le2->Flink;
939                     }
940                 }
941 
942                 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
943             } else {
944                 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
945                 me->fileref->fcb->inode_item.st_nlink++;
946                 me->fileref->fcb->inode_item_changed = TRUE;
947                 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
948             }
949         }
950 
951         le = le->Flink;
952     }
953 
954     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
955     fileref->fcb->subvol->root_item.ctime = now;
956 
957     // loop through list and create new filerefs
958 
959     le = move_list.Flink;
960     while (le != &move_list) {
961         hardlink* hl;
962         BOOL name_changed = FALSE;
963 
964         me = CONTAINING_RECORD(le, move_entry, list_entry);
965 
966         me->dummyfileref = create_fileref(fileref->fcb->Vcb);
967         if (!me->dummyfileref) {
968             ERR("out of memory\n");
969             Status = STATUS_INSUFFICIENT_RESOURCES;
970             goto end;
971         }
972 
973         if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) {
974             root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol;
975 
976             Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb);
977             if (!NT_SUCCESS(Status)) {
978                 ERR("create_directory_fcb returnd %08x\n", Status);
979                 goto end;
980             }
981 
982             me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
983             me->fileref->dc->key.obj_type = TYPE_INODE_ITEM;
984 
985             me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb;
986         } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) {
987             me->dummyfileref->fcb = me->fileref->fcb;
988 
989             me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id;
990         } else
991             me->dummyfileref->fcb = me->dummyfcb;
992 
993         InterlockedIncrement(&me->dummyfileref->fcb->refcount);
994 
995         me->dummyfileref->oldutf8 = me->fileref->oldutf8;
996         me->dummyfileref->oldindex = me->fileref->dc->index;
997 
998         if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length))
999             name_changed = TRUE;
1000 
1001         if ((le == move_list.Flink || me->fileref->fcb->inode == SUBVOL_ROOT_INODE) && !me->dummyfileref->oldutf8.Buffer) {
1002             me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
1003             if (!me->dummyfileref->oldutf8.Buffer) {
1004                 ERR("out of memory\n");
1005                 Status = STATUS_INSUFFICIENT_RESOURCES;
1006                 goto end;
1007             }
1008 
1009             RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1010 
1011             me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
1012         }
1013 
1014         me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
1015         me->dummyfileref->deleted = me->fileref->deleted;
1016 
1017         me->dummyfileref->created = me->fileref->created;
1018         me->fileref->created = TRUE;
1019 
1020         me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
1021         increase_fileref_refcount(me->dummyfileref->parent);
1022 
1023         ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->nonpaged->children_lock, TRUE);
1024         InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
1025         ExReleaseResourceLite(&me->dummyfileref->parent->nonpaged->children_lock);
1026 
1027         me->dummyfileref->debug_desc = me->fileref->debug_desc;
1028 
1029         if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
1030             me->dummyfileref->fcb->fileref = me->dummyfileref;
1031 
1032         if (!me->parent) {
1033             RemoveEntryList(&me->fileref->list_entry);
1034 
1035             increase_fileref_refcount(destdir);
1036 
1037             if (me->fileref->dc) {
1038                 // remove from old parent
1039                 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1040                 RemoveEntryList(&me->fileref->dc->list_entry_index);
1041                 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1042                 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1043 
1044                 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
1045                 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1046                 me->fileref->parent->fcb->inode_item.sequence++;
1047                 me->fileref->parent->fcb->inode_item.st_ctime = now;
1048                 me->fileref->parent->fcb->inode_item.st_mtime = now;
1049                 me->fileref->parent->fcb->inode_item_changed = TRUE;
1050                 mark_fcb_dirty(me->fileref->parent->fcb);
1051 
1052                 if (name_changed) {
1053                     ExFreePool(me->fileref->dc->utf8.Buffer);
1054                     ExFreePool(me->fileref->dc->name.Buffer);
1055                     ExFreePool(me->fileref->dc->name_uc.Buffer);
1056 
1057                     me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1058                     if (!me->fileref->dc->utf8.Buffer) {
1059                         ERR("out of memory\n");
1060                         Status = STATUS_INSUFFICIENT_RESOURCES;
1061                         goto end;
1062                     }
1063 
1064                     me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
1065                     RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1066 
1067                     me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
1068                     if (!me->fileref->dc->name.Buffer) {
1069                         ERR("out of memory\n");
1070                         Status = STATUS_INSUFFICIENT_RESOURCES;
1071                         goto end;
1072                     }
1073 
1074                     me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
1075                     RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
1076 
1077                     Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1078                     if (!NT_SUCCESS(Status)) {
1079                         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1080                         goto end;
1081                     }
1082 
1083                     me->fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1084                     me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
1085                 }
1086 
1087                 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
1088                     me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1089 
1090                 // add to new parent
1091 
1092                 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, TRUE);
1093 
1094                 if (IsListEmpty(&destdir->fcb->dir_children_index))
1095                     me->fileref->dc->index = 2;
1096                 else {
1097                     dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1098 
1099                     me->fileref->dc->index = max(2, dc2->index + 1);
1100                 }
1101 
1102                 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1103                 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
1104                 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
1105             }
1106 
1107             free_fileref(fileref->fcb->Vcb, me->fileref->parent);
1108             me->fileref->parent = destdir;
1109 
1110             ExAcquireResourceExclusiveLite(&me->fileref->parent->nonpaged->children_lock, TRUE);
1111             InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
1112             ExReleaseResourceLite(&me->fileref->parent->nonpaged->children_lock);
1113 
1114             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1115             me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
1116             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1117             me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1118             me->fileref->parent->fcb->inode_item.sequence++;
1119             me->fileref->parent->fcb->inode_item.st_ctime = now;
1120             me->fileref->parent->fcb->inode_item.st_mtime = now;
1121             me->fileref->parent->fcb->inode_item_changed = TRUE;
1122             mark_fcb_dirty(me->fileref->parent->fcb);
1123         } else {
1124             if (me->fileref->dc) {
1125                 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1126                 RemoveEntryList(&me->fileref->dc->list_entry_index);
1127 
1128                 if (!me->fileref->fcb->ads)
1129                     remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1130 
1131                 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1132 
1133                 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, TRUE);
1134 
1135                 if (me->fileref->fcb->ads)
1136                     InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1137                 else {
1138                     if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE)
1139                         me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1140 
1141                     if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index))
1142                         me->fileref->dc->index = 2;
1143                     else {
1144                         dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1145 
1146                         me->fileref->dc->index = max(2, dc2->index + 1);
1147                     }
1148 
1149                     InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1150                     insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
1151                 }
1152 
1153                 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
1154             }
1155         }
1156 
1157         if (!me->dummyfileref->fcb->ads) {
1158             Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1159             if (!NT_SUCCESS(Status)) {
1160                 ERR("delete_fileref returned %08x\n", Status);
1161                 goto end;
1162             }
1163         }
1164 
1165         if (me->fileref->fcb->inode_item.st_nlink > 1) {
1166             hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1167             if (!hl) {
1168                 ERR("out of memory\n");
1169                 Status = STATUS_INSUFFICIENT_RESOURCES;
1170                 goto end;
1171             }
1172 
1173             hl->parent = me->fileref->parent->fcb->inode;
1174             hl->index = me->fileref->dc->index;
1175 
1176             hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
1177             hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1178             if (!hl->utf8.Buffer) {
1179                 ERR("out of memory\n");
1180                 Status = STATUS_INSUFFICIENT_RESOURCES;
1181                 ExFreePool(hl);
1182                 goto end;
1183             }
1184 
1185             RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1186 
1187             hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
1188             hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1189             if (!hl->name.Buffer) {
1190                 ERR("out of memory\n");
1191                 Status = STATUS_INSUFFICIENT_RESOURCES;
1192                 ExFreePool(hl->utf8.Buffer);
1193                 ExFreePool(hl);
1194                 goto end;
1195             }
1196 
1197             RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1198 
1199             InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
1200         }
1201 
1202         mark_fileref_dirty(me->fileref);
1203 
1204         le = le->Flink;
1205     }
1206 
1207     // loop through, and only mark streams as deleted if their parent inodes are also deleted
1208 
1209     le = move_list.Flink;
1210     while (le != &move_list) {
1211         me = CONTAINING_RECORD(le, move_entry, list_entry);
1212 
1213         if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
1214             Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1215             if (!NT_SUCCESS(Status)) {
1216                 ERR("delete_fileref returned %08x\n", Status);
1217                 goto end;
1218             }
1219         }
1220 
1221         le = le->Flink;
1222     }
1223 
1224     destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation;
1225     destdir->fcb->subvol->root_item.ctime = now;
1226 
1227     me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
1228     send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1229     send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1230     send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1231 
1232     Status = STATUS_SUCCESS;
1233 
1234 end:
1235     while (!IsListEmpty(&move_list)) {
1236         le = RemoveHeadList(&move_list);
1237         me = CONTAINING_RECORD(le, move_entry, list_entry);
1238 
1239         if (me->dummyfcb)
1240             free_fcb(fileref->fcb->Vcb, me->dummyfcb);
1241 
1242         if (me->dummyfileref)
1243             free_fileref(fileref->fcb->Vcb, me->dummyfileref);
1244 
1245         free_fileref(fileref->fcb->Vcb, me->fileref);
1246 
1247         ExFreePool(me);
1248     }
1249 
1250     return Status;
1251 }
1252 
1253 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
1254     BOOL inserted;
1255     LIST_ENTRY* le;
1256     UINT8 c, d;
1257 
1258     c = dc->hash >> 24;
1259 
1260     inserted = FALSE;
1261 
1262     d = c;
1263     do {
1264         le = fcb->hash_ptrs[d];
1265 
1266         if (d == 0)
1267             break;
1268 
1269         d--;
1270     } while (!le);
1271 
1272     if (!le)
1273         le = fcb->dir_children_hash.Flink;
1274 
1275     while (le != &fcb->dir_children_hash) {
1276         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
1277 
1278         if (dc2->hash > dc->hash) {
1279             InsertHeadList(le->Blink, &dc->list_entry_hash);
1280             inserted = TRUE;
1281             break;
1282         }
1283 
1284         le = le->Flink;
1285     }
1286 
1287     if (!inserted)
1288         InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash);
1289 
1290     if (!fcb->hash_ptrs[c])
1291         fcb->hash_ptrs[c] = &dc->list_entry_hash;
1292     else {
1293         dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash);
1294 
1295         if (dc2->hash > dc->hash)
1296             fcb->hash_ptrs[c] = &dc->list_entry_hash;
1297     }
1298 
1299     c = dc->hash_uc >> 24;
1300 
1301     inserted = FALSE;
1302 
1303     d = c;
1304     do {
1305         le = fcb->hash_ptrs_uc[d];
1306 
1307         if (d == 0)
1308             break;
1309 
1310         d--;
1311     } while (!le);
1312 
1313     if (!le)
1314         le = fcb->dir_children_hash_uc.Flink;
1315 
1316     while (le != &fcb->dir_children_hash_uc) {
1317         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
1318 
1319         if (dc2->hash_uc > dc->hash_uc) {
1320             InsertHeadList(le->Blink, &dc->list_entry_hash_uc);
1321             inserted = TRUE;
1322             break;
1323         }
1324 
1325         le = le->Flink;
1326     }
1327 
1328     if (!inserted)
1329         InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc);
1330 
1331     if (!fcb->hash_ptrs_uc[c])
1332         fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1333     else {
1334         dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc);
1335 
1336         if (dc2->hash_uc > dc->hash_uc)
1337             fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1338     }
1339 }
1340 
1341 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
1342     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1343     FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
1344     fcb *fcb = FileObject->FsContext;
1345     ccb* ccb = FileObject->FsContext2;
1346     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
1347     WCHAR* fn;
1348     ULONG fnlen, utf8len, origutf8len;
1349     UNICODE_STRING fnus;
1350     ANSI_STRING utf8;
1351     NTSTATUS Status;
1352     LARGE_INTEGER time;
1353     BTRFS_TIME now;
1354     LIST_ENTRY rollback, *le;
1355     hardlink* hl;
1356     SECURITY_SUBJECT_CONTEXT subjcont;
1357     ACCESS_MASK access;
1358 
1359     InitializeListHead(&rollback);
1360 
1361     TRACE("tfo = %p\n", tfo);
1362     TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
1363     TRACE("RootDirectory = %p\n", fri->RootDirectory);
1364     TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
1365 
1366     fn = fri->FileName;
1367     fnlen = fri->FileNameLength / sizeof(WCHAR);
1368 
1369     if (!tfo) {
1370         if (!fileref || !fileref->parent) {
1371             ERR("no fileref set and no directory given\n");
1372             return STATUS_INVALID_PARAMETER;
1373         }
1374     } else {
1375         LONG i;
1376 
1377         while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
1378             fnlen--;
1379 
1380         if (fnlen == 0)
1381             return STATUS_INVALID_PARAMETER;
1382 
1383         for (i = fnlen - 1; i >= 0; i--) {
1384             if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
1385                 fn = &fri->FileName[i+1];
1386                 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
1387                 break;
1388             }
1389         }
1390     }
1391 
1392     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1393     acquire_fcb_lock_exclusive(Vcb);
1394     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1395 
1396     if (fcb->ads) {
1397         // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1398         // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1399         // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1400         Status = STATUS_INVALID_PARAMETER;
1401         goto end;
1402     }
1403 
1404     fnus.Buffer = fn;
1405     fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
1406 
1407     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
1408 
1409     origutf8len = fileref->dc->utf8.Length;
1410 
1411     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1412     if (!NT_SUCCESS(Status))
1413         goto end;
1414 
1415     utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
1416     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1417     if (!utf8.Buffer) {
1418         ERR("out of memory\n");
1419         Status = STATUS_INSUFFICIENT_RESOURCES;
1420         goto end;
1421     }
1422 
1423     Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1424     if (!NT_SUCCESS(Status))
1425         goto end;
1426 
1427     if (tfo && tfo->FsContext2) {
1428         struct _ccb* relatedccb = tfo->FsContext2;
1429 
1430         related = relatedccb->fileref;
1431         increase_fileref_refcount(related);
1432     } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') {
1433         related = fileref->parent;
1434         increase_fileref_refcount(related);
1435     }
1436 
1437     Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive,  Irp);
1438 
1439     if (NT_SUCCESS(Status)) {
1440         TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
1441 
1442         if (fileref != oldfileref && !oldfileref->deleted) {
1443             if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
1444                 Status = STATUS_OBJECT_NAME_COLLISION;
1445                 goto end;
1446             } else if ((oldfileref->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
1447                 WARN("trying to overwrite open file\n");
1448                 Status = STATUS_ACCESS_DENIED;
1449                 goto end;
1450             }
1451 
1452             if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
1453                 WARN("trying to overwrite directory\n");
1454                 Status = STATUS_ACCESS_DENIED;
1455                 goto end;
1456             }
1457         }
1458 
1459         if (fileref == oldfileref || oldfileref->deleted) {
1460             free_fileref(Vcb, oldfileref);
1461             oldfileref = NULL;
1462         }
1463     }
1464 
1465     if (!related) {
1466         Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
1467 
1468         if (!NT_SUCCESS(Status)) {
1469             ERR("open_fileref returned %08x\n", Status);
1470             goto end;
1471         }
1472     }
1473 
1474     if (related->fcb == Vcb->dummy_fcb) {
1475         Status = STATUS_ACCESS_DENIED;
1476         goto end;
1477     }
1478 
1479     SeCaptureSubjectContext(&subjcont);
1480 
1481     if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
1482         IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1483         SeReleaseSubjectContext(&subjcont);
1484         TRACE("SeAccessCheck failed, returning %08x\n", Status);
1485         goto end;
1486     }
1487 
1488     SeReleaseSubjectContext(&subjcont);
1489 
1490     if (has_open_children(fileref)) {
1491         WARN("trying to rename file with open children\n");
1492         Status = STATUS_ACCESS_DENIED;
1493         goto end;
1494     }
1495 
1496     if (oldfileref) {
1497         SeCaptureSubjectContext(&subjcont);
1498 
1499         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
1500                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1501             SeReleaseSubjectContext(&subjcont);
1502             TRACE("SeAccessCheck failed, returning %08x\n", Status);
1503             goto end;
1504         }
1505 
1506         SeReleaseSubjectContext(&subjcont);
1507 
1508         Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
1509         if (!NT_SUCCESS(Status)) {
1510             ERR("delete_fileref returned %08x\n", Status);
1511             goto end;
1512         }
1513     }
1514 
1515     if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) {
1516         Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
1517         if (!NT_SUCCESS(Status)) {
1518             ERR("move_across_subvols returned %08x\n", Status);
1519         }
1520         goto end;
1521     }
1522 
1523     if (related == fileref->parent) { // keeping file in same directory
1524         UNICODE_STRING oldfn, newfn;
1525         USHORT name_offset;
1526         ULONG reqlen, oldutf8len;
1527 
1528         oldfn.Length = oldfn.MaximumLength = 0;
1529 
1530         Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1531         if (Status != STATUS_BUFFER_OVERFLOW) {
1532             ERR("fileref_get_filename returned %08x\n", Status);
1533             goto end;
1534         }
1535 
1536         oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1537         if (!oldfn.Buffer) {
1538             ERR("out of memory\n");
1539             Status = STATUS_INSUFFICIENT_RESOURCES;
1540             goto end;
1541         }
1542 
1543         oldfn.MaximumLength = (UINT16)reqlen;
1544 
1545         Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1546         if (!NT_SUCCESS(Status)) {
1547             ERR("fileref_get_filename returned %08x\n", Status);
1548             ExFreePool(oldfn.Buffer);
1549             goto end;
1550         }
1551 
1552         oldutf8len = fileref->dc->utf8.Length;
1553 
1554         if (!fileref->created && !fileref->oldutf8.Buffer) {
1555             fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1556             if (!fileref->oldutf8.Buffer) {
1557                 ERR("out of memory\n");
1558                 Status = STATUS_INSUFFICIENT_RESOURCES;
1559                 goto end;
1560             }
1561 
1562             fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1563             RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1564         }
1565 
1566         TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer);
1567 
1568         mark_fileref_dirty(fileref);
1569 
1570         if (fileref->dc) {
1571             ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1572 
1573             ExFreePool(fileref->dc->utf8.Buffer);
1574             ExFreePool(fileref->dc->name.Buffer);
1575             ExFreePool(fileref->dc->name_uc.Buffer);
1576 
1577             fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1578             if (!fileref->dc->utf8.Buffer) {
1579                 ERR("out of memory\n");
1580                 Status = STATUS_INSUFFICIENT_RESOURCES;
1581                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1582                 ExFreePool(oldfn.Buffer);
1583                 goto end;
1584             }
1585 
1586             fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1587             RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1588 
1589             fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1590             if (!fileref->dc->name.Buffer) {
1591                 ERR("out of memory\n");
1592                 Status = STATUS_INSUFFICIENT_RESOURCES;
1593                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1594                 ExFreePool(oldfn.Buffer);
1595                 goto end;
1596             }
1597 
1598             fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1599             RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1600 
1601             Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1602             if (!NT_SUCCESS(Status)) {
1603                 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1604                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1605                 ExFreePool(oldfn.Buffer);
1606                 goto end;
1607             }
1608 
1609             remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
1610 
1611             fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1612             fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1613 
1614             insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc);
1615 
1616             ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1617         }
1618 
1619         newfn.Length = newfn.MaximumLength = 0;
1620 
1621         Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1622         if (Status != STATUS_BUFFER_OVERFLOW) {
1623             ERR("fileref_get_filename returned %08x\n", Status);
1624             ExFreePool(oldfn.Buffer);
1625             goto end;
1626         }
1627 
1628         newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1629         if (!newfn.Buffer) {
1630             ERR("out of memory\n");
1631             Status = STATUS_INSUFFICIENT_RESOURCES;
1632             ExFreePool(oldfn.Buffer);
1633             goto end;
1634         }
1635 
1636         newfn.MaximumLength = (UINT16)reqlen;
1637 
1638         Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1639         if (!NT_SUCCESS(Status)) {
1640             ERR("fileref_get_filename returned %08x\n", Status);
1641             ExFreePool(oldfn.Buffer);
1642             ExFreePool(newfn.Buffer);
1643             goto end;
1644         }
1645 
1646         KeQuerySystemTime(&time);
1647         win_time_to_unix(time, &now);
1648 
1649         if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1650             fcb->inode_item.transid = Vcb->superblock.generation;
1651             fcb->inode_item.sequence++;
1652 
1653             if (!ccb->user_set_change_time)
1654                 fcb->inode_item.st_ctime = now;
1655 
1656             fcb->inode_item_changed = TRUE;
1657             mark_fcb_dirty(fcb);
1658         }
1659 
1660         // update parent's INODE_ITEM
1661 
1662         related->fcb->inode_item.transid = Vcb->superblock.generation;
1663         TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1664         related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len);
1665         TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1666         related->fcb->inode_item.sequence++;
1667         related->fcb->inode_item.st_ctime = now;
1668         related->fcb->inode_item.st_mtime = now;
1669 
1670         related->fcb->inode_item_changed = TRUE;
1671         mark_fcb_dirty(related->fcb);
1672         send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1673 
1674         FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL,
1675                                       fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL);
1676         FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL,
1677                                       fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL);
1678 
1679         ExFreePool(oldfn.Buffer);
1680         ExFreePool(newfn.Buffer);
1681 
1682         Status = STATUS_SUCCESS;
1683         goto end;
1684     }
1685 
1686     // We move files by moving the existing fileref to the new directory, and
1687     // replacing it with a dummy fileref with the same original values, but marked as deleted.
1688 
1689     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
1690 
1691     fr2 = create_fileref(Vcb);
1692 
1693     fr2->fcb = fileref->fcb;
1694     fr2->fcb->refcount++;
1695 
1696     fr2->oldutf8 = fileref->oldutf8;
1697     fr2->oldindex = fileref->dc->index;
1698     fr2->delete_on_close = fileref->delete_on_close;
1699     fr2->deleted = TRUE;
1700     fr2->created = fileref->created;
1701     fr2->parent = fileref->parent;
1702     fr2->dc = NULL;
1703 
1704     if (!fr2->oldutf8.Buffer) {
1705         fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1706         if (!fr2->oldutf8.Buffer) {
1707             ERR("out of memory\n");
1708             Status = STATUS_INSUFFICIENT_RESOURCES;
1709             goto end;
1710         }
1711 
1712         RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1713 
1714         fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1715     }
1716 
1717     if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
1718         fr2->fcb->fileref = fr2;
1719 
1720     if (fileref->fcb->inode == SUBVOL_ROOT_INODE)
1721         fileref->fcb->subvol->parent = related->fcb->subvol->id;
1722 
1723     fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
1724     fileref->oldutf8.Buffer = NULL;
1725     fileref->deleted = FALSE;
1726     fileref->created = TRUE;
1727     fileref->parent = related;
1728 
1729     ExAcquireResourceExclusiveLite(&fileref->parent->nonpaged->children_lock, TRUE);
1730     InsertHeadList(&fileref->list_entry, &fr2->list_entry);
1731     RemoveEntryList(&fileref->list_entry);
1732     ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock);
1733 
1734     mark_fileref_dirty(fr2);
1735     mark_fileref_dirty(fileref);
1736 
1737     if (fileref->dc) {
1738         // remove from old parent
1739         ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, TRUE);
1740         RemoveEntryList(&fileref->dc->list_entry_index);
1741         remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
1742         ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
1743 
1744         if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
1745             // handle changed name
1746 
1747             ExFreePool(fileref->dc->utf8.Buffer);
1748             ExFreePool(fileref->dc->name.Buffer);
1749             ExFreePool(fileref->dc->name_uc.Buffer);
1750 
1751             fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1752             if (!fileref->dc->utf8.Buffer) {
1753                 ERR("out of memory\n");
1754                 Status = STATUS_INSUFFICIENT_RESOURCES;
1755                 goto end;
1756             }
1757 
1758             fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1759             RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1760 
1761             fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1762             if (!fileref->dc->name.Buffer) {
1763                 ERR("out of memory\n");
1764                 Status = STATUS_INSUFFICIENT_RESOURCES;
1765                 goto end;
1766             }
1767 
1768             fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1769             RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1770 
1771             Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1772             if (!NT_SUCCESS(Status)) {
1773                 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1774                 goto end;
1775             }
1776 
1777             fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1778             fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1779         }
1780 
1781         // add to new parent
1782         ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
1783 
1784         if (IsListEmpty(&related->fcb->dir_children_index))
1785             fileref->dc->index = 2;
1786         else {
1787             dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1788 
1789             fileref->dc->index = max(2, dc2->index + 1);
1790         }
1791 
1792         InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
1793         insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
1794         ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
1795     }
1796 
1797     ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
1798     InsertTailList(&related->children, &fileref->list_entry);
1799     ExReleaseResourceLite(&related->nonpaged->children_lock);
1800 
1801     if (fcb->inode_item.st_nlink > 1) {
1802         // add new hardlink entry to fcb
1803 
1804         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1805         if (!hl) {
1806             ERR("out of memory\n");
1807             Status = STATUS_INSUFFICIENT_RESOURCES;
1808             goto end;
1809         }
1810 
1811         hl->parent = related->fcb->inode;
1812         hl->index = fileref->dc->index;
1813 
1814         hl->name.Length = hl->name.MaximumLength = fnus.Length;
1815         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1816 
1817         if (!hl->name.Buffer) {
1818             ERR("out of memory\n");
1819             ExFreePool(hl);
1820             Status = STATUS_INSUFFICIENT_RESOURCES;
1821             goto end;
1822         }
1823 
1824         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
1825 
1826         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
1827         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1828 
1829         if (!hl->utf8.Buffer) {
1830             ERR("out of memory\n");
1831             ExFreePool(hl->name.Buffer);
1832             ExFreePool(hl);
1833             Status = STATUS_INSUFFICIENT_RESOURCES;
1834             goto end;
1835         }
1836 
1837         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1838 
1839         InsertTailList(&fcb->hardlinks, &hl->list_entry);
1840     }
1841 
1842     // delete old hardlink entry from fcb
1843 
1844     le = fcb->hardlinks.Flink;
1845     while (le != &fcb->hardlinks) {
1846         hl = CONTAINING_RECORD(le, hardlink, list_entry);
1847 
1848         if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
1849             RemoveEntryList(&hl->list_entry);
1850 
1851             if (hl->utf8.Buffer)
1852                 ExFreePool(hl->utf8.Buffer);
1853 
1854             if (hl->name.Buffer)
1855                 ExFreePool(hl->name.Buffer);
1856 
1857             ExFreePool(hl);
1858             break;
1859         }
1860 
1861         le = le->Flink;
1862     }
1863 
1864     // update inode's INODE_ITEM
1865 
1866     KeQuerySystemTime(&time);
1867     win_time_to_unix(time, &now);
1868 
1869     if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1870         fcb->inode_item.transid = Vcb->superblock.generation;
1871         fcb->inode_item.sequence++;
1872 
1873         if (!ccb->user_set_change_time)
1874             fcb->inode_item.st_ctime = now;
1875 
1876         fcb->inode_item_changed = TRUE;
1877         mark_fcb_dirty(fcb);
1878     }
1879 
1880     // update new parent's INODE_ITEM
1881 
1882     related->fcb->inode_item.transid = Vcb->superblock.generation;
1883     TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1884     related->fcb->inode_item.st_size += 2 * utf8len;
1885     TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1886     related->fcb->inode_item.sequence++;
1887     related->fcb->inode_item.st_ctime = now;
1888     related->fcb->inode_item.st_mtime = now;
1889 
1890     related->fcb->inode_item_changed = TRUE;
1891     mark_fcb_dirty(related->fcb);
1892 
1893     // update old parent's INODE_ITEM
1894 
1895     fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
1896     TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1897     fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
1898     TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1899     fr2->parent->fcb->inode_item.sequence++;
1900     fr2->parent->fcb->inode_item.st_ctime = now;
1901     fr2->parent->fcb->inode_item.st_mtime = now;
1902 
1903     free_fileref(Vcb, fr2);
1904 
1905     fr2->parent->fcb->inode_item_changed = TRUE;
1906     mark_fcb_dirty(fr2->parent->fcb);
1907 
1908     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1909     send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1910     send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1911 
1912     Status = STATUS_SUCCESS;
1913 
1914 end:
1915     if (oldfileref)
1916         free_fileref(Vcb, oldfileref);
1917 
1918     if (!NT_SUCCESS(Status) && related)
1919         free_fileref(Vcb, related);
1920 
1921     if (!NT_SUCCESS(Status) && fr2)
1922         free_fileref(Vcb, fr2);
1923 
1924     if (NT_SUCCESS(Status))
1925         clear_rollback(&rollback);
1926     else
1927         do_rollback(Vcb, &rollback);
1928 
1929     ExReleaseResourceLite(fcb->Header.Resource);
1930     release_fcb_lock(Vcb);
1931     ExReleaseResourceLite(&Vcb->tree_lock);
1932 
1933     return Status;
1934 }
1935 
1936 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, UINT16 end, fcb* fcb, file_ref* fileref, BOOL advance_only) {
1937     LARGE_INTEGER time;
1938     BTRFS_TIME now;
1939 
1940     TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adsdata.Length);
1941 
1942     if (!fileref || !fileref->parent) {
1943         ERR("no fileref for stream\n");
1944         return STATUS_INTERNAL_ERROR;
1945     }
1946 
1947     if (end < fcb->adsdata.Length) {
1948         if (advance_only)
1949             return STATUS_SUCCESS;
1950 
1951         TRACE("truncating stream to %llx bytes\n", end);
1952 
1953         fcb->adsdata.Length = end;
1954     } else if (end > fcb->adsdata.Length) {
1955         TRACE("extending stream to %llx bytes\n", end);
1956 
1957         if (end > fcb->adsmaxlen) {
1958             ERR("error - xattr too long (%u > %u)\n", end, fcb->adsmaxlen);
1959             return STATUS_DISK_FULL;
1960         }
1961 
1962         if (end > fcb->adsdata.MaximumLength) {
1963             char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
1964             if (!data) {
1965                 ERR("out of memory\n");
1966                 ExFreePool(data);
1967                 return STATUS_INSUFFICIENT_RESOURCES;
1968             }
1969 
1970             if (fcb->adsdata.Buffer) {
1971                 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length);
1972                 ExFreePool(fcb->adsdata.Buffer);
1973             }
1974 
1975             fcb->adsdata.Buffer = data;
1976             fcb->adsdata.MaximumLength = end;
1977         }
1978 
1979         RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length);
1980 
1981         fcb->adsdata.Length = end;
1982     }
1983 
1984     mark_fcb_dirty(fcb);
1985 
1986     fcb->Header.AllocationSize.QuadPart = end;
1987     fcb->Header.FileSize.QuadPart = end;
1988     fcb->Header.ValidDataLength.QuadPart = end;
1989 
1990     KeQuerySystemTime(&time);
1991     win_time_to_unix(time, &now);
1992 
1993     fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
1994     fileref->parent->fcb->inode_item.sequence++;
1995     fileref->parent->fcb->inode_item.st_ctime = now;
1996 
1997     fileref->parent->fcb->inode_item_changed = TRUE;
1998     mark_fcb_dirty(fileref->parent->fcb);
1999 
2000     fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2001     fileref->parent->fcb->subvol->root_item.ctime = now;
2002 
2003     return STATUS_SUCCESS;
2004 }
2005 
2006 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, BOOL prealloc) {
2007     FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
2008     fcb* fcb = FileObject->FsContext;
2009     ccb* ccb = FileObject->FsContext2;
2010     file_ref* fileref = ccb ? ccb->fileref : NULL;
2011     NTSTATUS Status;
2012     LARGE_INTEGER time;
2013     CC_FILE_SIZES ccfs;
2014     LIST_ENTRY rollback;
2015     BOOL set_size = FALSE;
2016     ULONG filter;
2017 
2018     if (!fileref) {
2019         ERR("fileref is NULL\n");
2020         return STATUS_INVALID_PARAMETER;
2021     }
2022 
2023     InitializeListHead(&rollback);
2024 
2025     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2026 
2027     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2028 
2029     if (fileref ? fileref->deleted : fcb->deleted) {
2030         Status = STATUS_FILE_CLOSED;
2031         goto end;
2032     }
2033 
2034     if (fcb->ads) {
2035         if (feofi->EndOfFile.QuadPart > 0xffff) {
2036             Status = STATUS_DISK_FULL;
2037             goto end;
2038         }
2039 
2040         if (feofi->EndOfFile.QuadPart < 0) {
2041             Status = STATUS_INVALID_PARAMETER;
2042             goto end;
2043         }
2044 
2045         Status = stream_set_end_of_file_information(Vcb, (UINT16)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only);
2046 
2047         if (NT_SUCCESS(Status)) {
2048             ccfs.AllocationSize = fcb->Header.AllocationSize;
2049             ccfs.FileSize = fcb->Header.FileSize;
2050             ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2051             set_size = TRUE;
2052         }
2053 
2054         filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
2055 
2056         if (!ccb->user_set_write_time) {
2057             KeQuerySystemTime(&time);
2058             win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime);
2059             filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2060 
2061             fileref->parent->fcb->inode_item_changed = TRUE;
2062             mark_fcb_dirty(fileref->parent->fcb);
2063         }
2064 
2065         send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
2066 
2067         goto end;
2068     }
2069 
2070     TRACE("file: %S\n", file_desc(FileObject));
2071     TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
2072     TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2073         fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2074 
2075     TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
2076 
2077     if ((UINT64)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
2078         if (advance_only) {
2079             Status = STATUS_SUCCESS;
2080             goto end;
2081         }
2082 
2083         TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2084 
2085         if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
2086             Status = STATUS_USER_MAPPED_FILE;
2087             goto end;
2088         }
2089 
2090         Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
2091         if (!NT_SUCCESS(Status)) {
2092             ERR("error - truncate_file failed\n");
2093             goto end;
2094         }
2095     } else if ((UINT64)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
2096         if (Irp->Flags & IRP_PAGING_IO) {
2097             TRACE("paging IO tried to extend file size\n");
2098             Status = STATUS_SUCCESS;
2099             goto end;
2100         }
2101 
2102         TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2103 
2104         Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
2105         if (!NT_SUCCESS(Status)) {
2106             ERR("error - extend_file failed\n");
2107             goto end;
2108         }
2109     } else if ((UINT64)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
2110         Status = STATUS_SUCCESS;
2111         goto end;
2112     }
2113 
2114     ccfs.AllocationSize = fcb->Header.AllocationSize;
2115     ccfs.FileSize = fcb->Header.FileSize;
2116     ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2117     set_size = TRUE;
2118 
2119     filter = FILE_NOTIFY_CHANGE_SIZE;
2120 
2121     if (!ccb->user_set_write_time) {
2122         KeQuerySystemTime(&time);
2123         win_time_to_unix(time, &fcb->inode_item.st_mtime);
2124         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2125     }
2126 
2127     fcb->inode_item_changed = TRUE;
2128     mark_fcb_dirty(fcb);
2129     send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2130 
2131     Status = STATUS_SUCCESS;
2132 
2133 end:
2134     if (NT_SUCCESS(Status))
2135         clear_rollback(&rollback);
2136     else
2137         do_rollback(Vcb, &rollback);
2138 
2139     ExReleaseResourceLite(fcb->Header.Resource);
2140 
2141     if (set_size) {
2142         _SEH2_TRY {
2143             CcSetFileSizes(FileObject, &ccfs);
2144         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2145             Status = _SEH2_GetExceptionCode();
2146         } _SEH2_END;
2147 
2148         if (!NT_SUCCESS(Status))
2149             ERR("CcSetFileSizes threw exception %08x\n", Status);
2150     }
2151 
2152     ExReleaseResourceLite(&Vcb->tree_lock);
2153 
2154     return Status;
2155 }
2156 
2157 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
2158     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2159 
2160     TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
2161 
2162     // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2163 
2164     FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
2165 
2166     return STATUS_SUCCESS;
2167 }
2168 
2169 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
2170     FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
2171     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
2172     ccb* ccb = FileObject->FsContext2;
2173     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2174     WCHAR* fn;
2175     ULONG fnlen, utf8len;
2176     UNICODE_STRING fnus;
2177     ANSI_STRING utf8;
2178     NTSTATUS Status;
2179     LARGE_INTEGER time;
2180     BTRFS_TIME now;
2181     LIST_ENTRY rollback;
2182     hardlink* hl;
2183     ACCESS_MASK access;
2184     SECURITY_SUBJECT_CONTEXT subjcont;
2185     dir_child* dc = NULL;
2186 
2187     InitializeListHead(&rollback);
2188 
2189     // FIXME - check fli length
2190     // FIXME - don't ignore fli->RootDirectory
2191 
2192     TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
2193     TRACE("RootDirectory = %p\n", fli->RootDirectory);
2194     TRACE("FileNameLength = %x\n", fli->FileNameLength);
2195     TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
2196 
2197     fn = fli->FileName;
2198     fnlen = fli->FileNameLength / sizeof(WCHAR);
2199 
2200     if (!tfo) {
2201         if (!fileref || !fileref->parent) {
2202             ERR("no fileref set and no directory given\n");
2203             return STATUS_INVALID_PARAMETER;
2204         }
2205 
2206         parfcb = fileref->parent->fcb;
2207         tfofcb = NULL;
2208     } else {
2209         LONG i;
2210 
2211         tfofcb = tfo->FsContext;
2212         parfcb = tfofcb;
2213 
2214         while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
2215             fnlen--;
2216 
2217         if (fnlen == 0)
2218             return STATUS_INVALID_PARAMETER;
2219 
2220         for (i = fnlen - 1; i >= 0; i--) {
2221             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
2222                 fn = &fli->FileName[i+1];
2223                 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
2224                 break;
2225             }
2226         }
2227     }
2228 
2229     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2230     acquire_fcb_lock_exclusive(Vcb);
2231     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2232 
2233     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2234         WARN("tried to create hard link on directory\n");
2235         Status = STATUS_FILE_IS_A_DIRECTORY;
2236         goto end;
2237     }
2238 
2239     if (fcb->ads) {
2240         WARN("tried to create hard link on stream\n");
2241         Status = STATUS_INVALID_PARAMETER;
2242         goto end;
2243     }
2244 
2245     if (fcb->inode_item.st_nlink >= 65535) {
2246         Status = STATUS_TOO_MANY_LINKS;
2247         goto end;
2248     }
2249 
2250     fnus.Buffer = fn;
2251     fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
2252 
2253     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
2254 
2255     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2256     if (!NT_SUCCESS(Status))
2257         goto end;
2258 
2259     utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
2260     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2261     if (!utf8.Buffer) {
2262         ERR("out of memory\n");
2263         Status = STATUS_INSUFFICIENT_RESOURCES;
2264         goto end;
2265     }
2266 
2267     Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2268     if (!NT_SUCCESS(Status))
2269         goto end;
2270 
2271     if (tfo && tfo->FsContext2) {
2272         struct _ccb* relatedccb = tfo->FsContext2;
2273 
2274         related = relatedccb->fileref;
2275         increase_fileref_refcount(related);
2276     }
2277 
2278     Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2279 
2280     if (NT_SUCCESS(Status)) {
2281         if (!oldfileref->deleted) {
2282             WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
2283 
2284             if (!fli->ReplaceIfExists) {
2285                 Status = STATUS_OBJECT_NAME_COLLISION;
2286                 goto end;
2287             } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) {
2288                 WARN("trying to overwrite open file\n");
2289                 Status = STATUS_ACCESS_DENIED;
2290                 goto end;
2291             } else if (fileref == oldfileref) {
2292                 Status = STATUS_ACCESS_DENIED;
2293                 goto end;
2294             }
2295 
2296             if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2297                 WARN("trying to overwrite directory\n");
2298                 Status = STATUS_ACCESS_DENIED;
2299                 goto end;
2300             }
2301         } else {
2302             free_fileref(Vcb, oldfileref);
2303             oldfileref = NULL;
2304         }
2305     }
2306 
2307     if (!related) {
2308         Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2309 
2310         if (!NT_SUCCESS(Status)) {
2311             ERR("open_fileref returned %08x\n", Status);
2312             goto end;
2313         }
2314     }
2315 
2316     SeCaptureSubjectContext(&subjcont);
2317 
2318     if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL,
2319                        IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2320         SeReleaseSubjectContext(&subjcont);
2321         TRACE("SeAccessCheck failed, returning %08x\n", Status);
2322         goto end;
2323     }
2324 
2325     SeReleaseSubjectContext(&subjcont);
2326 
2327     if (fcb->subvol != parfcb->subvol) {
2328         WARN("can't create hard link over subvolume boundary\n");
2329         Status = STATUS_INVALID_PARAMETER;
2330         goto end;
2331     }
2332 
2333     if (oldfileref) {
2334         SeCaptureSubjectContext(&subjcont);
2335 
2336         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
2337                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2338             SeReleaseSubjectContext(&subjcont);
2339             TRACE("SeAccessCheck failed, returning %08x\n", Status);
2340             goto end;
2341         }
2342 
2343         SeReleaseSubjectContext(&subjcont);
2344 
2345         Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
2346         if (!NT_SUCCESS(Status)) {
2347             ERR("delete_fileref returned %08x\n", Status);
2348             goto end;
2349         }
2350     }
2351 
2352     fr2 = create_fileref(Vcb);
2353 
2354     fr2->fcb = fcb;
2355     fcb->refcount++;
2356 
2357     fr2->created = TRUE;
2358     fr2->parent = related;
2359 
2360     Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc);
2361     if (!NT_SUCCESS(Status))
2362         WARN("add_dir_child returned %08x\n", Status);
2363 
2364     fr2->dc = dc;
2365     dc->fileref = fr2;
2366 
2367     ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
2368     InsertTailList(&related->children, &fr2->list_entry);
2369     ExReleaseResourceLite(&related->nonpaged->children_lock);
2370 
2371     // add hardlink for existing fileref, if it's not there already
2372     if (IsListEmpty(&fcb->hardlinks)) {
2373         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2374         if (!hl) {
2375             ERR("out of memory\n");
2376             Status = STATUS_INSUFFICIENT_RESOURCES;
2377             goto end;
2378         }
2379 
2380         hl->parent = fileref->parent->fcb->inode;
2381         hl->index = fileref->dc->index;
2382 
2383         hl->name.Length = hl->name.MaximumLength = fnus.Length;
2384         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2385 
2386         if (!hl->name.Buffer) {
2387             ERR("out of memory\n");
2388             ExFreePool(hl);
2389             Status = STATUS_INSUFFICIENT_RESOURCES;
2390             goto end;
2391         }
2392 
2393         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2394 
2395         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
2396         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2397 
2398         if (!hl->utf8.Buffer) {
2399             ERR("out of memory\n");
2400             ExFreePool(hl->name.Buffer);
2401             ExFreePool(hl);
2402             Status = STATUS_INSUFFICIENT_RESOURCES;
2403             goto end;
2404         }
2405 
2406         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2407 
2408         InsertTailList(&fcb->hardlinks, &hl->list_entry);
2409     }
2410 
2411     hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2412     if (!hl) {
2413         ERR("out of memory\n");
2414         Status = STATUS_INSUFFICIENT_RESOURCES;
2415         goto end;
2416     }
2417 
2418     hl->parent = related->fcb->inode;
2419     hl->index = dc->index;
2420 
2421     hl->name.Length = hl->name.MaximumLength = fnus.Length;
2422     hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
2423 
2424     if (!hl->name.Buffer) {
2425         ERR("out of memory\n");
2426         ExFreePool(hl);
2427         Status = STATUS_INSUFFICIENT_RESOURCES;
2428         goto end;
2429     }
2430 
2431     RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2432 
2433     hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
2434     hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2435 
2436     if (!hl->utf8.Buffer) {
2437         ERR("out of memory\n");
2438         ExFreePool(hl->name.Buffer);
2439         ExFreePool(hl);
2440         Status = STATUS_INSUFFICIENT_RESOURCES;
2441         goto end;
2442     }
2443 
2444     RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
2445     ExFreePool(utf8.Buffer);
2446 
2447     InsertTailList(&fcb->hardlinks, &hl->list_entry);
2448 
2449     mark_fileref_dirty(fr2);
2450     free_fileref(Vcb, fr2);
2451 
2452     // update inode's INODE_ITEM
2453 
2454     KeQuerySystemTime(&time);
2455     win_time_to_unix(time, &now);
2456 
2457     fcb->inode_item.transid = Vcb->superblock.generation;
2458     fcb->inode_item.sequence++;
2459     fcb->inode_item.st_nlink++;
2460 
2461     if (!ccb->user_set_change_time)
2462         fcb->inode_item.st_ctime = now;
2463 
2464     fcb->inode_item_changed = TRUE;
2465     mark_fcb_dirty(fcb);
2466 
2467     // update parent's INODE_ITEM
2468 
2469     parfcb->inode_item.transid = Vcb->superblock.generation;
2470     TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2471     parfcb->inode_item.st_size += 2 * utf8len;
2472     TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2473     parfcb->inode_item.sequence++;
2474     parfcb->inode_item.st_ctime = now;
2475 
2476     parfcb->inode_item_changed = TRUE;
2477     mark_fcb_dirty(parfcb);
2478 
2479     send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
2480 
2481     Status = STATUS_SUCCESS;
2482 
2483 end:
2484     if (oldfileref)
2485         free_fileref(Vcb, oldfileref);
2486 
2487     if (!NT_SUCCESS(Status) && related)
2488         free_fileref(Vcb, related);
2489 
2490     if (!NT_SUCCESS(Status) && fr2)
2491         free_fileref(Vcb, fr2);
2492 
2493     if (NT_SUCCESS(Status))
2494         clear_rollback(&rollback);
2495     else
2496         do_rollback(Vcb, &rollback);
2497 
2498     ExReleaseResourceLite(fcb->Header.Resource);
2499     release_fcb_lock(Vcb);
2500     ExReleaseResourceLite(&Vcb->tree_lock);
2501 
2502     return Status;
2503 }
2504 
2505 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2506     FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
2507     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2508     fcb* fcb = FileObject->FsContext;
2509     ccb* ccb = FileObject->FsContext2;
2510     file_ref* fileref = ccb ? ccb->fileref : NULL;
2511     NTSTATUS Status;
2512     LARGE_INTEGER time;
2513     CC_FILE_SIZES ccfs;
2514     LIST_ENTRY rollback;
2515     BOOL set_size = FALSE;
2516     ULONG filter;
2517 
2518     if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
2519         ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
2520         return STATUS_INVALID_PARAMETER;
2521     }
2522 
2523     if (!fileref) {
2524         ERR("fileref is NULL\n");
2525         return STATUS_INVALID_PARAMETER;
2526     }
2527 
2528     InitializeListHead(&rollback);
2529 
2530     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2531 
2532     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2533 
2534     if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
2535         Status = STATUS_INVALID_PARAMETER;
2536         goto end;
2537     }
2538 
2539     if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
2540         TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart,
2541               fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
2542         Status = STATUS_INVALID_PARAMETER;
2543         goto end;
2544     }
2545 
2546     if (fileref ? fileref->deleted : fcb->deleted) {
2547         Status = STATUS_FILE_CLOSED;
2548         goto end;
2549     }
2550 
2551     // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2552     // and we set it to the max anyway.
2553 
2554     ccfs.AllocationSize = fcb->Header.AllocationSize;
2555     ccfs.FileSize = fcb->Header.FileSize;
2556     ccfs.ValidDataLength = fvdli->ValidDataLength;
2557     set_size = TRUE;
2558 
2559     filter = FILE_NOTIFY_CHANGE_SIZE;
2560 
2561     if (!ccb->user_set_write_time) {
2562         KeQuerySystemTime(&time);
2563         win_time_to_unix(time, &fcb->inode_item.st_mtime);
2564         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2565     }
2566 
2567     fcb->inode_item_changed = TRUE;
2568     mark_fcb_dirty(fcb);
2569 
2570     send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2571 
2572     Status = STATUS_SUCCESS;
2573 
2574 end:
2575     if (NT_SUCCESS(Status))
2576         clear_rollback(&rollback);
2577     else
2578         do_rollback(Vcb, &rollback);
2579 
2580     ExReleaseResourceLite(fcb->Header.Resource);
2581 
2582     if (set_size) {
2583         _SEH2_TRY {
2584             CcSetFileSizes(FileObject, &ccfs);
2585         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2586             Status = _SEH2_GetExceptionCode();
2587         } _SEH2_END;
2588 
2589         if (!NT_SUCCESS(Status))
2590             ERR("CcSetFileSizes threw exception %08x\n", Status);
2591         else
2592             fcb->Header.AllocationSize = ccfs.AllocationSize;
2593     }
2594 
2595     ExReleaseResourceLite(&Vcb->tree_lock);
2596 
2597     return Status;
2598 }
2599 
2600 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
2601 _Function_class_(DRIVER_DISPATCH)
2602 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2603     NTSTATUS Status;
2604     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2605     device_extension* Vcb = DeviceObject->DeviceExtension;
2606     fcb* fcb = IrpSp->FileObject->FsContext;
2607     ccb* ccb = IrpSp->FileObject->FsContext2;
2608     BOOL top_level;
2609 
2610     FsRtlEnterFileSystem();
2611 
2612     top_level = is_top_level(Irp);
2613 
2614     Irp->IoStatus.Information = 0;
2615 
2616     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2617         Status = vol_set_information(DeviceObject, Irp);
2618         goto end;
2619     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2620         Status = STATUS_INVALID_PARAMETER;
2621         goto end;
2622     }
2623 
2624     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
2625         Status = STATUS_ACCESS_DENIED;
2626         goto end;
2627     }
2628 
2629     if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
2630         Status = STATUS_MEDIA_WRITE_PROTECTED;
2631         goto end;
2632     }
2633 
2634     if (!fcb) {
2635         ERR("no fcb\n");
2636         Status = STATUS_INVALID_PARAMETER;
2637         goto end;
2638     }
2639 
2640     if (!ccb) {
2641         ERR("no ccb\n");
2642         Status = STATUS_INVALID_PARAMETER;
2643         goto end;
2644     }
2645 
2646     if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
2647         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
2648         Status = STATUS_ACCESS_DENIED;
2649         goto end;
2650     }
2651 
2652     Status = STATUS_NOT_IMPLEMENTED;
2653 
2654     TRACE("set information\n");
2655 
2656     switch (IrpSp->Parameters.SetFile.FileInformationClass) {
2657         case FileAllocationInformation:
2658         {
2659             TRACE("FileAllocationInformation\n");
2660 
2661             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
2662                 WARN("insufficient privileges\n");
2663                 Status = STATUS_ACCESS_DENIED;
2664                 break;
2665             }
2666 
2667             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE);
2668             break;
2669         }
2670 
2671         case FileBasicInformation:
2672         {
2673             TRACE("FileBasicInformation\n");
2674 
2675             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
2676                 WARN("insufficient privileges\n");
2677                 Status = STATUS_ACCESS_DENIED;
2678                 break;
2679             }
2680 
2681             Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
2682 
2683             break;
2684         }
2685 
2686         case FileDispositionInformation:
2687         {
2688             TRACE("FileDispositionInformation\n");
2689 
2690             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
2691                 WARN("insufficient privileges\n");
2692                 Status = STATUS_ACCESS_DENIED;
2693                 break;
2694             }
2695 
2696             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
2697 
2698             break;
2699         }
2700 
2701         case FileEndOfFileInformation:
2702         {
2703             TRACE("FileEndOfFileInformation\n");
2704 
2705             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2706                 WARN("insufficient privileges\n");
2707                 Status = STATUS_ACCESS_DENIED;
2708                 break;
2709             }
2710 
2711             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE);
2712 
2713             break;
2714         }
2715 
2716         case FileLinkInformation:
2717             TRACE("FileLinkInformation\n");
2718             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2719             break;
2720 
2721         case FilePositionInformation:
2722             TRACE("FilePositionInformation\n");
2723             Status = set_position_information(IrpSp->FileObject, Irp);
2724             break;
2725 
2726         case FileRenameInformation:
2727             TRACE("FileRenameInformation\n");
2728             // FIXME - make this work with streams
2729             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2730             break;
2731 
2732         case FileValidDataLengthInformation:
2733         {
2734             TRACE("FileValidDataLengthInformation\n");
2735 
2736             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2737                 WARN("insufficient privileges\n");
2738                 Status = STATUS_ACCESS_DENIED;
2739                 break;
2740             }
2741 
2742             Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
2743 
2744             break;
2745         }
2746 
2747         default:
2748             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
2749     }
2750 
2751 end:
2752     Irp->IoStatus.Status = Status;
2753 
2754     TRACE("returning %08x\n", Status);
2755 
2756     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2757 
2758     if (top_level)
2759         IoSetTopLevelIrp(NULL);
2760 
2761     FsRtlExitFileSystem();
2762 
2763     return Status;
2764 }
2765 
2766 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
2767     RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
2768 
2769     *length -= sizeof(FILE_BASIC_INFORMATION);
2770 
2771     if (fcb == fcb->Vcb->dummy_fcb) {
2772         LARGE_INTEGER time;
2773 
2774         KeQuerySystemTime(&time);
2775         fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
2776     } else {
2777         fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2778         fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2779         fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2780         fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2781     }
2782 
2783     if (fcb->ads) {
2784         if (!fileref || !fileref->parent) {
2785             ERR("no fileref for stream\n");
2786             return STATUS_INTERNAL_ERROR;
2787         } else
2788             fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2789     } else
2790         fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2791 
2792     return STATUS_SUCCESS;
2793 }
2794 
2795 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
2796     INODE_ITEM* ii;
2797 
2798     if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
2799         WARN("overflow\n");
2800         return STATUS_BUFFER_OVERFLOW;
2801     }
2802 
2803     RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
2804 
2805     *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
2806 
2807     if (fcb->ads) {
2808         if (!fileref || !fileref->parent) {
2809             ERR("no fileref for stream\n");
2810             return STATUS_INTERNAL_ERROR;
2811         }
2812 
2813         ii = &fileref->parent->fcb->inode_item;
2814     } else
2815         ii = &fcb->inode_item;
2816 
2817     if (fcb == fcb->Vcb->dummy_fcb) {
2818         LARGE_INTEGER time;
2819 
2820         KeQuerySystemTime(&time);
2821         fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
2822     } else {
2823         fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2824         fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2825         fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2826         fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2827     }
2828 
2829     if (fcb->ads) {
2830         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
2831         fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2832     } else {
2833         fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2834         fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2835         fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2836     }
2837 
2838     return STATUS_SUCCESS;
2839 }
2840 
2841 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
2842     RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
2843 
2844     *length -= sizeof(FILE_STANDARD_INFORMATION);
2845 
2846     if (fcb->ads) {
2847         if (!fileref || !fileref->parent) {
2848             ERR("no fileref for stream\n");
2849             return STATUS_INTERNAL_ERROR;
2850         }
2851 
2852         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
2853         fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
2854         fsi->Directory = FALSE;
2855     } else {
2856         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2857         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2858         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
2859         fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
2860     }
2861 
2862     TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
2863 
2864     fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE;
2865 
2866     return STATUS_SUCCESS;
2867 }
2868 
2869 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
2870     *length -= sizeof(FILE_INTERNAL_INFORMATION);
2871 
2872     fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
2873 
2874     return STATUS_SUCCESS;
2875 }
2876 
2877 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
2878     *length -= sizeof(FILE_EA_INFORMATION);
2879 
2880     /* This value appears to be the size of the structure NTFS stores on disk, and not,
2881      * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2882      * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2883      * item. */
2884 
2885     eai->EaSize = fcb->ealen;
2886 
2887     return STATUS_SUCCESS;
2888 }
2889 
2890 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
2891     RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
2892 
2893     *length -= sizeof(FILE_POSITION_INFORMATION);
2894 
2895     fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
2896 
2897     return STATUS_SUCCESS;
2898 }
2899 
2900 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
2901     file_ref* fr;
2902     NTSTATUS Status;
2903     ULONG reqlen = 0;
2904     USHORT offset;
2905     BOOL overflow = FALSE;
2906 
2907     // FIXME - we need a lock on filerefs' filepart
2908 
2909     if (fileref == fileref->fcb->Vcb->root_fileref) {
2910         if (fn->MaximumLength >= sizeof(WCHAR)) {
2911             fn->Buffer[0] = '\\';
2912             fn->Length = sizeof(WCHAR);
2913 
2914             if (name_offset)
2915                 *name_offset = 0;
2916 
2917             return STATUS_SUCCESS;
2918         } else {
2919             if (preqlen)
2920                 *preqlen = sizeof(WCHAR);
2921             fn->Length = 0;
2922 
2923             return STATUS_BUFFER_OVERFLOW;
2924         }
2925     }
2926 
2927     fr = fileref;
2928     offset = 0;
2929 
2930     while (fr->parent) {
2931         USHORT movelen;
2932 
2933         if (!fr->dc)
2934             return STATUS_INTERNAL_ERROR;
2935 
2936         if (!overflow) {
2937             if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
2938                 overflow = TRUE;
2939         }
2940 
2941         if (overflow)
2942             movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
2943         else
2944             movelen = fn->Length;
2945 
2946         if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
2947             RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
2948             offset += fr->dc->name.Length + sizeof(WCHAR);
2949         }
2950 
2951         if (fn->MaximumLength >= sizeof(WCHAR)) {
2952             fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
2953             fn->Length += sizeof(WCHAR);
2954 
2955             if (fn->MaximumLength > sizeof(WCHAR)) {
2956                 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
2957                 fn->Length += fr->dc->name.Length;
2958             }
2959 
2960             if (fn->Length > fn->MaximumLength) {
2961                 fn->Length = fn->MaximumLength;
2962                 overflow = TRUE;
2963             }
2964         }
2965 
2966         reqlen += sizeof(WCHAR) + fr->dc->name.Length;
2967 
2968         fr = fr->parent;
2969     }
2970 
2971     offset += sizeof(WCHAR);
2972 
2973     if (overflow) {
2974         if (preqlen)
2975             *preqlen = reqlen;
2976         Status = STATUS_BUFFER_OVERFLOW;
2977     } else {
2978         if (name_offset)
2979             *name_offset = offset;
2980 
2981         Status = STATUS_SUCCESS;
2982     }
2983 
2984     return Status;
2985 }
2986 
2987 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
2988     ULONG reqlen;
2989     UNICODE_STRING fn;
2990     NTSTATUS Status;
2991     static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
2992     UINT16 datasuflen = sizeof(datasuf) - sizeof(WCHAR);
2993 
2994     if (!fileref) {
2995         ERR("called without fileref\n");
2996         return STATUS_INVALID_PARAMETER;
2997     }
2998 
2999     *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
3000 
3001     TRACE("maximum length is %u\n", *length);
3002     fni->FileNameLength = 0;
3003 
3004     fni->FileName[0] = 0;
3005 
3006     fn.Buffer = fni->FileName;
3007     fn.Length = 0;
3008     fn.MaximumLength = (UINT16)*length;
3009 
3010     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
3011     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
3012         ERR("fileref_get_filename returned %08x\n", Status);
3013         return Status;
3014     }
3015 
3016     if (fcb->ads) {
3017         if (Status == STATUS_BUFFER_OVERFLOW)
3018             reqlen += datasuflen;
3019         else {
3020             if (fn.Length + datasuflen > fn.MaximumLength) {
3021                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
3022                 reqlen += datasuflen;
3023                 Status = STATUS_BUFFER_OVERFLOW;
3024             } else {
3025                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
3026                 fn.Length += datasuflen;
3027             }
3028         }
3029     }
3030 
3031     if (Status == STATUS_BUFFER_OVERFLOW) {
3032         *length = -1;
3033         fni->FileNameLength = reqlen;
3034         TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3035     } else {
3036         *length -= fn.Length;
3037         fni->FileNameLength = fn.Length;
3038         TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3039     }
3040 
3041     return Status;
3042 }
3043 
3044 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
3045     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
3046 
3047     if (fcb->ads) {
3048         if (!ccb->fileref || !ccb->fileref->parent) {
3049             ERR("no fileref for stream\n");
3050             return STATUS_INTERNAL_ERROR;
3051         }
3052 
3053         ati->FileAttributes = ccb->fileref->parent->fcb->atts;
3054     } else
3055         ati->FileAttributes = fcb->atts;
3056 
3057     if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3058         ati->ReparseTag = 0;
3059     else
3060         ati->ReparseTag = get_reparse_tag_fcb(fcb);
3061 
3062     return STATUS_SUCCESS;
3063 }
3064 
3065 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
3066     LONG reqsize;
3067     LIST_ENTRY* le;
3068     FILE_STREAM_INFORMATION *entry, *lastentry;
3069     NTSTATUS Status;
3070 
3071     static const WCHAR datasuf[] = L":$DATA";
3072     UNICODE_STRING suf;
3073 
3074     if (!fileref) {
3075         ERR("fileref was NULL\n");
3076         return STATUS_INVALID_PARAMETER;
3077     }
3078 
3079     suf.Buffer = (WCHAR*)datasuf;
3080     suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3081 
3082     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
3083         reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
3084     else
3085         reqsize = 0;
3086 
3087     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
3088 
3089     le = fileref->fcb->dir_children_index.Flink;
3090     while (le != &fileref->fcb->dir_children_index) {
3091         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3092 
3093         if (dc->index == 0) {
3094             reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
3095             reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
3096         } else
3097             break;
3098 
3099         le = le->Flink;
3100     }
3101 
3102     TRACE("length = %i, reqsize = %u\n", *length, reqsize);
3103 
3104     if (reqsize > *length) {
3105         Status = STATUS_BUFFER_OVERFLOW;
3106         goto end;
3107     }
3108 
3109     entry = fsi;
3110     lastentry = NULL;
3111 
3112     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
3113         ULONG off;
3114 
3115         entry->NextEntryOffset = 0;
3116         entry->StreamNameLength = suf.Length + sizeof(WCHAR);
3117         entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
3118         entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
3119 
3120         entry->StreamName[0] = ':';
3121         RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
3122 
3123         off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
3124 
3125         lastentry = entry;
3126         entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3127     }
3128 
3129     le = fileref->fcb->dir_children_index.Flink;
3130     while (le != &fileref->fcb->dir_children_index) {
3131         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3132 
3133         if (dc->index == 0) {
3134             ULONG off;
3135 
3136             entry->NextEntryOffset = 0;
3137             entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
3138 
3139             if (dc->fileref)
3140                 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
3141             else
3142                 entry->StreamSize.QuadPart = dc->size;
3143 
3144             entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
3145 
3146             entry->StreamName[0] = ':';
3147 
3148             RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
3149             RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
3150 
3151             if (lastentry)
3152                 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry);
3153 
3154             off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
3155 
3156             lastentry = entry;
3157             entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3158         } else
3159             break;
3160 
3161         le = le->Flink;
3162     }
3163 
3164     *length -= reqsize;
3165 
3166     Status = STATUS_SUCCESS;
3167 
3168 end:
3169     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
3170 
3171     return Status;
3172 }
3173 
3174 #ifndef __REACTOS__
3175 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
3176     TRACE("FileStandardLinkInformation\n");
3177 
3178     // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3179 
3180     fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
3181     fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
3182     fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
3183     fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE;
3184 
3185     *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
3186 
3187     return STATUS_SUCCESS;
3188 }
3189 #endif /* __REACTOS__ */
3190 
3191 NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
3192                                root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
3193     NTSTATUS Status;
3194     fcb* fcb;
3195     UINT64 parent = 0;
3196     UNICODE_STRING name;
3197     BOOL hl_alloc = FALSE;
3198     file_ref *parfr, *fr;
3199 
3200     Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
3201     if (!NT_SUCCESS(Status)) {
3202         ERR("open_fcb returned %08x\n", Status);
3203         return Status;
3204     }
3205 
3206     if (fcb->fileref) {
3207         *pfr = fcb->fileref;
3208         increase_fileref_refcount(fcb->fileref);
3209         return STATUS_SUCCESS;
3210     }
3211 
3212     // find hardlink if fcb doesn't have any loaded
3213     if (IsListEmpty(&fcb->hardlinks)) {
3214         KEY searchkey;
3215         traverse_ptr tp;
3216 
3217         searchkey.obj_id = fcb->inode;
3218         searchkey.obj_type = TYPE_INODE_EXTREF;
3219         searchkey.offset = 0xffffffffffffffff;
3220 
3221         Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
3222         if (!NT_SUCCESS(Status)) {
3223             ERR("find_item returned %08x\n", Status);
3224             free_fcb(Vcb, fcb);
3225             return Status;
3226         }
3227 
3228         if (tp.item->key.obj_id == fcb->inode) {
3229             if (tp.item->key.obj_type == TYPE_INODE_REF) {
3230                 INODE_REF* ir;
3231                 ULONG stringlen;
3232 
3233                 ir = (INODE_REF*)tp.item->data;
3234 
3235                 parent = tp.item->key.offset;
3236 
3237                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
3238                 if (!NT_SUCCESS(Status)) {
3239                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3240                     free_fcb(Vcb, fcb);
3241                     return Status;
3242                 }
3243 
3244                 name.Length = name.MaximumLength = (UINT16)stringlen;
3245 
3246                 if (stringlen == 0)
3247                     name.Buffer = NULL;
3248                 else {
3249                     name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3250 
3251                     if (!name.Buffer) {
3252                         ERR("out of memory\n");
3253                         free_fcb(Vcb, fcb);
3254                         return STATUS_INSUFFICIENT_RESOURCES;
3255                     }
3256 
3257                     Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
3258                     if (!NT_SUCCESS(Status)) {
3259                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3260                         ExFreePool(name.Buffer);
3261                         free_fcb(Vcb, fcb);
3262                         return Status;
3263                     }
3264 
3265                     hl_alloc = TRUE;
3266                 }
3267             } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3268                 INODE_EXTREF* ier;
3269                 ULONG stringlen;
3270 
3271                 ier = (INODE_EXTREF*)tp.item->data;
3272 
3273                 parent = ier->dir;
3274 
3275                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
3276                 if (!NT_SUCCESS(Status)) {
3277                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3278                     free_fcb(Vcb, fcb);
3279                     return Status;
3280                 }
3281 
3282                 name.Length = name.MaximumLength = (UINT16)stringlen;
3283 
3284                 if (stringlen == 0)
3285                     name.Buffer = NULL;
3286                 else {
3287                     name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3288 
3289                     if (!name.Buffer) {
3290                         ERR("out of memory\n");
3291                         free_fcb(Vcb, fcb);
3292                         return STATUS_INSUFFICIENT_RESOURCES;
3293                     }
3294 
3295                     Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
3296                     if (!NT_SUCCESS(Status)) {
3297                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3298                         ExFreePool(name.Buffer);
3299                         free_fcb(Vcb, fcb);
3300                         return Status;
3301                     }
3302 
3303                     hl_alloc = TRUE;
3304                 }
3305 
3306             }
3307         }
3308     } else {
3309         hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
3310 
3311         name = hl->name;
3312         parent = hl->parent;
3313     }
3314 
3315     if (parent == 0) {
3316         ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
3317         free_fcb(Vcb, fcb);
3318         if (hl_alloc) ExFreePool(name.Buffer);
3319         return STATUS_INVALID_PARAMETER;
3320     }
3321 
3322     if (parent == inode) { // subvolume root
3323         KEY searchkey;
3324         traverse_ptr tp;
3325 
3326         searchkey.obj_id = subvol->id;
3327         searchkey.obj_type = TYPE_ROOT_BACKREF;
3328         searchkey.offset = 0xffffffffffffffff;
3329 
3330         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3331         if (!NT_SUCCESS(Status)) {
3332             ERR("find_item returned %08x\n", Status);
3333             free_fcb(Vcb, fcb);
3334             if (hl_alloc) ExFreePool(name.Buffer);
3335             return Status;
3336         }
3337 
3338         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
3339             ROOT_REF* rr = (ROOT_REF*)tp.item->data;
3340             LIST_ENTRY* le;
3341             root* r = NULL;
3342             ULONG stringlen;
3343 
3344             if (tp.item->size < sizeof(ROOT_REF)) {
3345                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
3346                 free_fcb(Vcb, fcb);
3347                 if (hl_alloc) ExFreePool(name.Buffer);
3348                 return STATUS_INTERNAL_ERROR;
3349             }
3350 
3351             if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
3352                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n);
3353                 free_fcb(Vcb, fcb);
3354                 if (hl_alloc) ExFreePool(name.Buffer);
3355                 return STATUS_INTERNAL_ERROR;
3356             }
3357 
3358             le = Vcb->roots.Flink;
3359             while (le != &Vcb->roots) {
3360                 root* r2 = CONTAINING_RECORD(le, root, list_entry);
3361 
3362                 if (r2->id == tp.item->key.offset) {
3363                     r = r2;
3364                     break;
3365                 }
3366 
3367                 le = le->Flink;
3368             }
3369 
3370             if (!r) {
3371                 ERR("couldn't find subvol %llx\n", tp.item->key.offset);
3372                 free_fcb(Vcb, fcb);
3373                 if (hl_alloc) ExFreePool(name.Buffer);
3374                 return STATUS_INTERNAL_ERROR;
3375             }
3376 
3377             Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
3378             if (!NT_SUCCESS(Status)) {
3379                 ERR("open_fileref_by_inode returned %08x\n", Status);
3380                 free_fcb(Vcb, fcb);
3381                 if (hl_alloc) ExFreePool(name.Buffer);
3382                 return Status;
3383             }
3384 
3385             if (hl_alloc) {
3386                 ExFreePool(name.Buffer);
3387                 hl_alloc = FALSE;
3388             }
3389 
3390             Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
3391             if (!NT_SUCCESS(Status)) {
3392                 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3393                 free_fcb(Vcb, fcb);
3394                 return Status;
3395             }
3396 
3397             name.Length = name.MaximumLength = (UINT16)stringlen;
3398 
3399             if (stringlen == 0)
3400                 name.Buffer = NULL;
3401             else {
3402                 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3403 
3404                 if (!name.Buffer) {
3405                     ERR("out of memory\n");
3406                     free_fcb(Vcb, fcb);
3407                     return STATUS_INSUFFICIENT_RESOURCES;
3408                 }
3409 
3410                 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
3411                 if (!NT_SUCCESS(Status)) {
3412                     ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3413                     ExFreePool(name.Buffer);
3414                     free_fcb(Vcb, fcb);
3415                     return Status;
3416                 }
3417 
3418                 hl_alloc = TRUE;
3419             }
3420         } else {
3421             ERR("couldn't find parent for subvol %llx\n", subvol->id);
3422             free_fcb(Vcb, fcb);
3423             if (hl_alloc) ExFreePool(name.Buffer);
3424             return STATUS_INTERNAL_ERROR;
3425         }
3426     } else {
3427         Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
3428         if (!NT_SUCCESS(Status)) {
3429             ERR("open_fileref_by_inode returned %08x\n", Status);
3430             free_fcb(Vcb, fcb);
3431 
3432             if (hl_alloc)
3433                 ExFreePool(name.Buffer);
3434 
3435             return Status;
3436         }
3437     }
3438 
3439     Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
3440 
3441     if (!NT_SUCCESS(Status)) {
3442         ERR("open_fileref_child returned %08x\n", Status);
3443 
3444         if (hl_alloc)
3445             ExFreePool(name.Buffer);
3446 
3447         free_fcb(Vcb, fcb);
3448         free_fileref(Vcb, parfr);
3449 
3450         return Status;
3451     }
3452 
3453     *pfr = fr;
3454 
3455     if (hl_alloc)
3456         ExFreePool(name.Buffer);
3457 
3458     free_fcb(Vcb, fcb);
3459     free_fileref(Vcb, parfr);
3460 
3461     return STATUS_SUCCESS;
3462 }
3463 
3464 #ifndef __REACTOS__
3465 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
3466     NTSTATUS Status;
3467     LIST_ENTRY* le;
3468     LONG bytes_needed;
3469     FILE_LINK_ENTRY_INFORMATION* feli;
3470     BOOL overflow = FALSE;
3471     fcb* fcb = fileref->fcb;
3472     ULONG len;
3473 
3474     if (fcb->ads)
3475         return STATUS_INVALID_PARAMETER;
3476 
3477     if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
3478         return STATUS_INVALID_PARAMETER;
3479 
3480     RtlZeroMemory(fli, *length);
3481 
3482     bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
3483     len = bytes_needed;
3484     feli = NULL;
3485 
3486     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
3487 
3488     if (fcb->inode == SUBVOL_ROOT_INODE) {
3489         ULONG namelen;
3490 
3491         if (fcb == fcb->Vcb->root_fileref->fcb)
3492             namelen = sizeof(WCHAR);
3493         else
3494             namelen = fileref->dc->name.Length;
3495 
3496         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
3497 
3498         if (bytes_needed > *length)
3499             overflow = TRUE;
3500 
3501         if (!overflow) {
3502             feli = &fli->Entry;
3503 
3504             feli->NextEntryOffset = 0;
3505             feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
3506 
3507             if (fcb == fcb->Vcb->root_fileref->fcb) {
3508                 feli->FileNameLength = 1;
3509                 feli->FileName[0] = '.';
3510             } else {
3511                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3512                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3513             }
3514 
3515             fli->EntriesReturned++;
3516 
3517             len = bytes_needed;
3518         }
3519     } else {
3520         acquire_fcb_lock_exclusive(fcb->Vcb);
3521 
3522         if (IsListEmpty(&fcb->hardlinks)) {
3523             bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
3524 
3525             if (bytes_needed > *length)
3526                 overflow = TRUE;
3527 
3528             if (!overflow) {
3529                 feli = &fli->Entry;
3530 
3531                 feli->NextEntryOffset = 0;
3532                 feli->ParentFileId = fileref->parent->fcb->inode;
3533                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3534                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3535 
3536                 fli->EntriesReturned++;
3537 
3538                 len = bytes_needed;
3539             }
3540         } else {
3541             le = fcb->hardlinks.Flink;
3542             while (le != &fcb->hardlinks) {
3543                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
3544                 file_ref* parfr;
3545 
3546                 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
3547 
3548                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
3549 
3550                 if (!NT_SUCCESS(Status)) {
3551                     ERR("open_fileref_by_inode returned %08x\n", Status);
3552                 } else if (!parfr->deleted) {
3553                     LIST_ENTRY* le2;
3554                     BOOL found = FALSE, deleted = FALSE;
3555                     UNICODE_STRING* fn = NULL;
3556 
3557                     le2 = parfr->children.Flink;
3558                     while (le2 != &parfr->children) {
3559                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
3560 
3561                         if (fr2->dc->index == hl->index) {
3562                             found = TRUE;
3563                             deleted = fr2->deleted;
3564 
3565                             if (!deleted)
3566                                 fn = &fr2->dc->name;
3567 
3568                             break;
3569                         }
3570 
3571                         le2 = le2->Flink;
3572                     }
3573 
3574                     if (!found)
3575                         fn = &hl->name;
3576 
3577                     if (!deleted) {
3578                         TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
3579 
3580                         if (feli)
3581                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
3582 
3583                         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
3584 
3585                         if (bytes_needed > *length)
3586                             overflow = TRUE;
3587 
3588                         if (!overflow) {
3589                             if (feli) {
3590                                 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
3591                                 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset);
3592                             } else
3593                                 feli = &fli->Entry;
3594 
3595                             feli->NextEntryOffset = 0;
3596                             feli->ParentFileId = parfr->fcb->inode;
3597                             feli->FileNameLength = fn->Length / sizeof(WCHAR);
3598                             RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
3599 
3600                             fli->EntriesReturned++;
3601 
3602                             len = bytes_needed;
3603                         }
3604                     }
3605 
3606                     free_fileref(fcb->Vcb, parfr);
3607                 }
3608 
3609                 le = le->Flink;
3610             }
3611         }
3612 
3613         release_fcb_lock(fcb->Vcb);
3614     }
3615 
3616     fli->BytesNeeded = bytes_needed;
3617 
3618     *length -= len;
3619 
3620     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
3621 
3622     ExReleaseResourceLite(fcb->Header.Resource);
3623 
3624     return Status;
3625 }
3626 #endif /* __REACTOS__ */
3627 
3628 #if (NTDDI_VERSION >= NTDDI_WIN10)
3629 #ifdef __MINGW32__
3630 typedef struct _FILE_ID_128 {
3631     UCHAR Identifier[16];
3632 } FILE_ID_128, *PFILE_ID_128;
3633 
3634 typedef struct _FILE_ID_INFORMATION {
3635     ULONGLONG VolumeSerialNumber;
3636     FILE_ID_128 FileId;
3637 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
3638 #endif
3639 
3640 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
3641     RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64));
3642     RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
3643     RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
3644 
3645     *length -= sizeof(FILE_ID_INFORMATION);
3646 
3647     return STATUS_SUCCESS;
3648 }
3649 #endif
3650 
3651 #ifndef __REACTOS__
3652 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
3653     INODE_ITEM* ii;
3654 
3655     fsli->FileId.LowPart = (UINT32)fcb->inode;
3656     fsli->FileId.HighPart = (UINT32)fcb->subvol->id;
3657 
3658     if (fcb->ads)
3659         ii = &ccb->fileref->parent->fcb->inode_item;
3660     else
3661         ii = &fcb->inode_item;
3662 
3663     if (fcb == fcb->Vcb->dummy_fcb) {
3664         LARGE_INTEGER time;
3665 
3666         KeQuerySystemTime(&time);
3667         fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
3668     } else {
3669         fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
3670         fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
3671         fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
3672         fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
3673     }
3674 
3675     if (fcb->ads) {
3676         fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
3677         fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
3678     } else {
3679         fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
3680         fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
3681         fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
3682     }
3683 
3684     if (fcb->type == BTRFS_TYPE_SOCKET)
3685         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
3686     else if (fcb->type == BTRFS_TYPE_FIFO)
3687         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
3688     else if (fcb->type == BTRFS_TYPE_CHARDEV)
3689         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
3690     else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
3691         fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
3692     else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3693         fsli->ReparseTag = 0;
3694     else
3695         fsli->ReparseTag = get_reparse_tag_fcb(fcb);
3696 
3697     if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
3698         fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
3699 
3700     if (fcb->ads)
3701         fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
3702     else
3703         fsli->NumberOfLinks = fcb->inode_item.st_nlink;
3704 
3705     fsli->EffectiveAccess = ccb->access;
3706     fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME - LX_FILE_CASE_SENSITIVE_DIR
3707     fsli->LxUid = ii->st_uid;
3708     fsli->LxGid = ii->st_gid;
3709     fsli->LxMode = ii->st_mode;
3710 
3711     if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
3712         fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
3713         fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
3714     } else {
3715         fsli->LxDeviceIdMajor = 0;
3716         fsli->LxDeviceIdMinor = 0;
3717     }
3718 
3719     *length -= sizeof(FILE_STAT_LX_INFORMATION);
3720 
3721     return STATUS_SUCCESS;
3722 }
3723 #endif
3724 
3725 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
3726     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3727     LONG length = IrpSp->Parameters.QueryFile.Length;
3728     fcb* fcb = FileObject->FsContext;
3729     ccb* ccb = FileObject->FsContext2;
3730     file_ref* fileref = ccb ? ccb->fileref : NULL;
3731     NTSTATUS Status;
3732 
3733     TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
3734     TRACE("fcb = %p\n", fcb);
3735 
3736     if (fcb == Vcb->volume_fcb)
3737         return STATUS_INVALID_PARAMETER;
3738 
3739     if (!ccb) {
3740         ERR("ccb is NULL\n");
3741         return STATUS_INVALID_PARAMETER;
3742     }
3743 
3744     switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
3745         case FileAllInformation:
3746         {
3747             FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
3748             INODE_ITEM* ii;
3749 
3750             TRACE("FileAllInformation\n");
3751 
3752             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3753                 WARN("insufficient privileges\n");
3754                 Status = STATUS_ACCESS_DENIED;
3755                 goto exit;
3756             }
3757 
3758             if (fcb->ads) {
3759                 if (!fileref || !fileref->parent) {
3760                     ERR("no fileref for stream\n");
3761                     Status = STATUS_INTERNAL_ERROR;
3762                     goto exit;
3763                 }
3764 
3765                 ii = &fileref->parent->fcb->inode_item;
3766             } else
3767                 ii = &fcb->inode_item;
3768 
3769             // Access, mode, and alignment are all filled in by the kernel
3770 
3771             if (length > 0)
3772                 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
3773 
3774             if (length > 0)
3775                 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
3776 
3777             if (length > 0)
3778                 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
3779 
3780             if (length > 0)
3781                 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
3782 
3783             length -= sizeof(FILE_ACCESS_INFORMATION);
3784 
3785             if (length > 0)
3786                 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
3787 
3788             length -= sizeof(FILE_MODE_INFORMATION);
3789 
3790             length -= sizeof(FILE_ALIGNMENT_INFORMATION);
3791 
3792             if (length > 0)
3793                 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
3794 
3795             Status = STATUS_SUCCESS;
3796 
3797             break;
3798         }
3799 
3800         case FileAttributeTagInformation:
3801         {
3802             FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
3803 
3804             TRACE("FileAttributeTagInformation\n");
3805 
3806             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3807                 WARN("insufficient privileges\n");
3808                 Status = STATUS_ACCESS_DENIED;
3809                 goto exit;
3810             }
3811 
3812             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3813             Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
3814             ExReleaseResourceLite(&Vcb->tree_lock);
3815 
3816             break;
3817         }
3818 
3819         case FileBasicInformation:
3820         {
3821             FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
3822             INODE_ITEM* ii;
3823 
3824             TRACE("FileBasicInformation\n");
3825 
3826             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3827                 WARN("insufficient privileges\n");
3828                 Status = STATUS_ACCESS_DENIED;
3829                 goto exit;
3830             }
3831 
3832             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
3833                 WARN("overflow\n");
3834                 Status = STATUS_BUFFER_OVERFLOW;
3835                 goto exit;
3836             }
3837 
3838             if (fcb->ads) {
3839                 if (!fileref || !fileref->parent) {
3840                     ERR("no fileref for stream\n");
3841                     Status = STATUS_INTERNAL_ERROR;
3842                     goto exit;
3843                 }
3844 
3845                 ii = &fileref->parent->fcb->inode_item;
3846             } else
3847                 ii = &fcb->inode_item;
3848 
3849             Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
3850             break;
3851         }
3852 
3853         case FileCompressionInformation:
3854             FIXME("STUB: FileCompressionInformation\n");
3855             Status = STATUS_INVALID_PARAMETER;
3856             goto exit;
3857 
3858         case FileEaInformation:
3859         {
3860             FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
3861 
3862             TRACE("FileEaInformation\n");
3863 
3864             Status = fill_in_file_ea_information(eai, fcb, &length);
3865 
3866             break;
3867         }
3868 
3869         case FileInternalInformation:
3870         {
3871             FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3872 
3873             TRACE("FileInternalInformation\n");
3874 
3875             Status = fill_in_file_internal_information(fii, fcb, &length);
3876 
3877             break;
3878         }
3879 
3880         case FileNameInformation:
3881         {
3882             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3883 
3884             TRACE("FileNameInformation\n");
3885 
3886             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3887 
3888             break;
3889         }
3890 
3891         case FileNetworkOpenInformation:
3892         {
3893             FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
3894 
3895             TRACE("FileNetworkOpenInformation\n");
3896 
3897             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3898                 WARN("insufficient privileges\n");
3899                 Status = STATUS_ACCESS_DENIED;
3900                 goto exit;
3901             }
3902 
3903             Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
3904 
3905             break;
3906         }
3907 
3908         case FilePositionInformation:
3909         {
3910             FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
3911 
3912             TRACE("FilePositionInformation\n");
3913 
3914             Status = fill_in_file_position_information(fpi, FileObject, &length);
3915 
3916             break;
3917         }
3918 
3919         case FileStandardInformation:
3920         {
3921             FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3922 
3923             TRACE("FileStandardInformation\n");
3924 
3925             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
3926                 WARN("overflow\n");
3927                 Status = STATUS_BUFFER_OVERFLOW;
3928                 goto exit;
3929             }
3930 
3931             Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
3932 
3933             break;
3934         }
3935 
3936         case FileStreamInformation:
3937         {
3938             FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3939 
3940             TRACE("FileStreamInformation\n");
3941 
3942             Status = fill_in_file_stream_information(fsi, fileref, &length);
3943 
3944             break;
3945         }
3946 
3947 #if (NTDDI_VERSION >= NTDDI_VISTA)
3948         case FileHardLinkInformation:
3949         {
3950             FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
3951 
3952             TRACE("FileHardLinkInformation\n");
3953 
3954             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3955             Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
3956             ExReleaseResourceLite(&Vcb->tree_lock);
3957 
3958             break;
3959         }
3960 
3961         case FileNormalizedNameInformation:
3962         {
3963             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3964 
3965             TRACE("FileNormalizedNameInformation\n");
3966 
3967             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3968 
3969             break;
3970         }
3971 #endif
3972 
3973 #if (NTDDI_VERSION >= NTDDI_WIN7)
3974         case FileStandardLinkInformation:
3975         {
3976             FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
3977 
3978             TRACE("FileStandardLinkInformation\n");
3979 
3980             Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
3981 
3982             break;
3983         }
3984 
3985         case FileRemoteProtocolInformation:
3986             TRACE("FileRemoteProtocolInformation\n");
3987             Status = STATUS_INVALID_PARAMETER;
3988             goto exit;
3989 #endif
3990 
3991 #if (NTDDI_VERSION >= NTDDI_WIN10)
3992 #ifndef _MSC_VER
3993 #pragma GCC diagnostic push
3994 #pragma GCC diagnostic ignored "-Wswitch"
3995 #endif
3996         case FileIdInformation:
3997         {
3998             FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3999 
4000             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
4001                 WARN("overflow\n");
4002                 Status = STATUS_BUFFER_OVERFLOW;
4003                 goto exit;
4004             }
4005 
4006             TRACE("FileIdInformation\n");
4007 
4008             Status = fill_in_file_id_information(fii, fcb, &length);
4009 
4010             break;
4011         }
4012 
4013         case FileStatLxInformation:
4014         {
4015             FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
4016 
4017             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
4018                 WARN("overflow\n");
4019                 Status = STATUS_BUFFER_OVERFLOW;
4020                 goto exit;
4021             }
4022 
4023             TRACE("FileStatLxInformation\n");
4024 
4025             Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
4026 
4027             break;
4028         }
4029 #ifndef _MSC_VER
4030 #pragma GCC diagnostic pop
4031 #endif
4032 #endif
4033 
4034         default:
4035             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
4036             Status = STATUS_INVALID_PARAMETER;
4037             goto exit;
4038     }
4039 
4040     if (length < 0) {
4041         length = 0;
4042         Status = STATUS_BUFFER_OVERFLOW;
4043     }
4044 
4045     Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
4046 
4047 exit:
4048     TRACE("query_info returning %08x\n", Status);
4049 
4050     return Status;
4051 }
4052 
4053 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
4054 _Function_class_(DRIVER_DISPATCH)
4055 NTSTATUS NTAPI drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4056     PIO_STACK_LOCATION IrpSp;
4057     NTSTATUS Status;
4058     fcb* fcb;
4059     device_extension* Vcb = DeviceObject->DeviceExtension;
4060     BOOL top_level;
4061 
4062     FsRtlEnterFileSystem();
4063 
4064     top_level = is_top_level(Irp);
4065 
4066     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4067         Status = vol_query_information(DeviceObject, Irp);
4068         goto end;
4069     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4070         Status = STATUS_INVALID_PARAMETER;
4071         goto end;
4072     }
4073 
4074     Irp->IoStatus.Information = 0;
4075 
4076     TRACE("query information\n");
4077 
4078     IrpSp = IoGetCurrentIrpStackLocation(Irp);
4079 
4080     fcb = IrpSp->FileObject->FsContext;
4081     TRACE("fcb = %p\n", fcb);
4082     TRACE("fcb->subvol = %p\n", fcb->subvol);
4083 
4084     Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
4085 
4086 end:
4087     TRACE("returning %08x\n", Status);
4088 
4089     Irp->IoStatus.Status = Status;
4090 
4091     IoCompleteRequest( Irp, IO_NO_INCREMENT );
4092 
4093     if (top_level)
4094         IoSetTopLevelIrp(NULL);
4095 
4096     FsRtlExitFileSystem();
4097 
4098     return Status;
4099 }
4100 
4101 _Dispatch_type_(IRP_MJ_QUERY_EA)
4102 _Function_class_(DRIVER_DISPATCH)
4103 NTSTATUS NTAPI drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4104     NTSTATUS Status;
4105     BOOL top_level;
4106     device_extension* Vcb = DeviceObject->DeviceExtension;
4107     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4108     PFILE_OBJECT FileObject = IrpSp->FileObject;
4109     fcb* fcb;
4110     ccb* ccb;
4111     FILE_FULL_EA_INFORMATION* ffei;
4112     ULONG retlen = 0;
4113 
4114     FsRtlEnterFileSystem();
4115 
4116     TRACE("(%p, %p)\n", DeviceObject, Irp);
4117 
4118     top_level = is_top_level(Irp);
4119 
4120     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4121         Status = vol_query_ea(DeviceObject, Irp);
4122         goto end;
4123     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4124         Status = STATUS_INVALID_PARAMETER;
4125         goto end;
4126     }
4127 
4128     ffei = map_user_buffer(Irp, NormalPagePriority);
4129     if (!ffei) {
4130         ERR("could not get output buffer\n");
4131         Status = STATUS_INVALID_PARAMETER;
4132         goto end;
4133     }
4134 
4135     if (!FileObject) {
4136         ERR("no file object\n");
4137         Status = STATUS_INVALID_PARAMETER;
4138         goto end;
4139     }
4140 
4141     fcb = FileObject->FsContext;
4142 
4143     if (!fcb) {
4144         ERR("no fcb\n");
4145         Status = STATUS_INVALID_PARAMETER;
4146         goto end;
4147     }
4148 
4149     ccb = FileObject->FsContext2;
4150 
4151     if (!ccb) {
4152         ERR("no ccb\n");
4153         Status = STATUS_INVALID_PARAMETER;
4154         goto end;
4155     }
4156 
4157     if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
4158         WARN("insufficient privileges\n");
4159         Status = STATUS_ACCESS_DENIED;
4160         goto end;
4161     }
4162 
4163     if (fcb->ads)
4164         fcb = ccb->fileref->parent->fcb;
4165 
4166     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
4167 
4168     Status = STATUS_SUCCESS;
4169 
4170     if (fcb->ea_xattr.Length == 0)
4171         goto end2;
4172 
4173     if (IrpSp->Parameters.QueryEa.EaList) {
4174         FILE_FULL_EA_INFORMATION *ea, *out;
4175         FILE_GET_EA_INFORMATION* in;
4176 
4177         in = IrpSp->Parameters.QueryEa.EaList;
4178         do {
4179             STRING s;
4180 
4181             s.Length = s.MaximumLength = in->EaNameLength;
4182             s.Buffer = in->EaName;
4183 
4184             RtlUpperString(&s, &s);
4185 
4186             if (in->NextEntryOffset == 0)
4187                 break;
4188 
4189             in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4190         } while (TRUE);
4191 
4192         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4193         out = NULL;
4194 
4195         do {
4196             BOOL found = FALSE;
4197 
4198             in = IrpSp->Parameters.QueryEa.EaList;
4199             do {
4200                 if (in->EaNameLength == ea->EaNameLength &&
4201                     RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
4202                     found = TRUE;
4203                     break;
4204                 }
4205 
4206                 if (in->NextEntryOffset == 0)
4207                     break;
4208 
4209                 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4210             } while (TRUE);
4211 
4212             if (found) {
4213                 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4214 
4215                 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4216                     Status = STATUS_BUFFER_OVERFLOW;
4217                     retlen = 0;
4218                     goto end2;
4219                 }
4220 
4221                 retlen += padding;
4222 
4223                 if (out) {
4224                     out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4225                     out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4226                 } else
4227                     out = ffei;
4228 
4229                 out->NextEntryOffset = 0;
4230                 out->Flags = ea->Flags;
4231                 out->EaNameLength = ea->EaNameLength;
4232                 out->EaValueLength = ea->EaValueLength;
4233                 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4234 
4235                 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4236 
4237                 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4238                     break;
4239             }
4240 
4241             if (ea->NextEntryOffset == 0)
4242                 break;
4243 
4244             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4245         } while (TRUE);
4246     } else {
4247         FILE_FULL_EA_INFORMATION *ea, *out;
4248         ULONG index;
4249 
4250         if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
4251             // The index is 1-based
4252             if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
4253                 Status = STATUS_NONEXISTENT_EA_ENTRY;
4254                 goto end2;
4255             } else
4256                 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
4257         } else if (IrpSp->Flags & SL_RESTART_SCAN)
4258             index = ccb->ea_index = 0;
4259         else
4260             index = ccb->ea_index;
4261 
4262         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4263 
4264         if (index > 0) {
4265             ULONG i;
4266 
4267             for (i = 0; i < index; i++) {
4268                 if (ea->NextEntryOffset == 0) // last item
4269                     goto end2;
4270 
4271                 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4272             }
4273         }
4274 
4275         out = NULL;
4276 
4277         do {
4278             UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4279 
4280             if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4281                 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
4282                 goto end2;
4283             }
4284 
4285             retlen += padding;
4286 
4287             if (out) {
4288                 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4289                 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4290             } else
4291                 out = ffei;
4292 
4293             out->NextEntryOffset = 0;
4294             out->Flags = ea->Flags;
4295             out->EaNameLength = ea->EaNameLength;
4296             out->EaValueLength = ea->EaValueLength;
4297             RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4298 
4299             retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4300 
4301             if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
4302                 ccb->ea_index++;
4303 
4304             if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4305                 break;
4306 
4307             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4308         } while (TRUE);
4309     }
4310 
4311 end2:
4312     ExReleaseResourceLite(fcb->Header.Resource);
4313 
4314 end:
4315     TRACE("returning %08x\n", Status);
4316 
4317     Irp->IoStatus.Status = Status;
4318     Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
4319 
4320     IoCompleteRequest( Irp, IO_NO_INCREMENT );
4321 
4322     if (top_level)
4323         IoSetTopLevelIrp(NULL);
4324 
4325     FsRtlExitFileSystem();
4326 
4327     return Status;
4328 }
4329 
4330 _Dispatch_type_(IRP_MJ_SET_EA)
4331 _Function_class_(DRIVER_DISPATCH)
4332 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4333     device_extension* Vcb = DeviceObject->DeviceExtension;
4334     NTSTATUS Status;
4335     BOOL top_level;
4336     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4337     PFILE_OBJECT FileObject = IrpSp->FileObject;
4338     fcb* fcb;
4339     ccb* ccb;
4340     file_ref* fileref;
4341     FILE_FULL_EA_INFORMATION* ffei;
4342     ULONG offset;
4343     LIST_ENTRY ealist;
4344     ea_item* item;
4345     FILE_FULL_EA_INFORMATION* ea;
4346     LIST_ENTRY* le;
4347     LARGE_INTEGER time;
4348     BTRFS_TIME now;
4349 
4350     FsRtlEnterFileSystem();
4351 
4352     TRACE("(%p, %p)\n", DeviceObject, Irp);
4353 
4354     top_level = is_top_level(Irp);
4355 
4356     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4357         Status = vol_set_ea(DeviceObject, Irp);
4358         goto end;
4359     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4360         Status = STATUS_INVALID_PARAMETER;
4361         goto end;
4362     }
4363 
4364     if (Vcb->readonly) {
4365         Status = STATUS_MEDIA_WRITE_PROTECTED;
4366         goto end;
4367     }
4368 
4369     ffei = map_user_buffer(Irp, NormalPagePriority);
4370     if (!ffei) {
4371         ERR("could not get output buffer\n");
4372         Status = STATUS_INVALID_PARAMETER;
4373         goto end;
4374     }
4375 
4376     Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
4377     if (!NT_SUCCESS(Status)) {
4378         ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
4379         goto end;
4380     }
4381 
4382     if (!FileObject) {
4383         ERR("no file object\n");
4384         Status = STATUS_INVALID_PARAMETER;
4385         goto end;
4386     }
4387 
4388     fcb = FileObject->FsContext;
4389 
4390     if (!fcb) {
4391         ERR("no fcb\n");
4392         Status = STATUS_INVALID_PARAMETER;
4393         goto end;
4394     }
4395 
4396     ccb = FileObject->FsContext2;
4397 
4398     if (!ccb) {
4399         ERR("no ccb\n");
4400         Status = STATUS_INVALID_PARAMETER;
4401         goto end;
4402     }
4403 
4404     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
4405         WARN("insufficient privileges\n");
4406         Status = STATUS_ACCESS_DENIED;
4407         goto end;
4408     }
4409 
4410     if (fcb->ads) {
4411         fileref = ccb->fileref->parent;
4412         fcb = fileref->fcb;
4413     } else
4414         fileref = ccb->fileref;
4415 
4416     InitializeListHead(&ealist);
4417 
4418     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4419 
4420     if (fcb->ea_xattr.Length > 0) {
4421         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4422 
4423         do {
4424             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4425             if (!item) {
4426                 ERR("out of memory\n");
4427                 Status = STATUS_INSUFFICIENT_RESOURCES;
4428                 goto end2;
4429             }
4430 
4431             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4432             item->name.Buffer = ea->EaName;
4433 
4434             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4435             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4436 
4437             item->flags = ea->Flags;
4438 
4439             InsertTailList(&ealist, &item->list_entry);
4440 
4441             if (ea->NextEntryOffset == 0)
4442                 break;
4443 
4444             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4445         } while (TRUE);
4446     }
4447 
4448     ea = ffei;
4449 
4450     do {
4451         STRING s;
4452         BOOL found = FALSE;
4453 
4454         s.Length = s.MaximumLength = ea->EaNameLength;
4455         s.Buffer = ea->EaName;
4456 
4457         RtlUpperString(&s, &s);
4458 
4459         le = ealist.Flink;
4460         while (le != &ealist) {
4461             item = CONTAINING_RECORD(le, ea_item, list_entry);
4462 
4463             if (item->name.Length == s.Length &&
4464                 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
4465                 item->flags = ea->Flags;
4466                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4467                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4468                 found = TRUE;
4469                 break;
4470             }
4471 
4472             le = le->Flink;
4473         }
4474 
4475         if (!found) {
4476             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4477             if (!item) {
4478                 ERR("out of memory\n");
4479                 Status = STATUS_INSUFFICIENT_RESOURCES;
4480                 goto end2;
4481             }
4482 
4483             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4484             item->name.Buffer = ea->EaName;
4485 
4486             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4487             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4488 
4489             item->flags = ea->Flags;
4490 
4491             InsertTailList(&ealist, &item->list_entry);
4492         }
4493 
4494         if (ea->NextEntryOffset == 0)
4495             break;
4496 
4497         ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4498     } while (TRUE);
4499 
4500     // remove entries with zero-length value
4501     le = ealist.Flink;
4502     while (le != &ealist) {
4503         LIST_ENTRY* le2 = le->Flink;
4504 
4505         item = CONTAINING_RECORD(le, ea_item, list_entry);
4506 
4507         if (item->value.Length == 0) {
4508             RemoveEntryList(&item->list_entry);
4509             ExFreePool(item);
4510         }
4511 
4512         le = le2;
4513     }
4514 
4515     // handle LXSS values
4516     le = ealist.Flink;
4517     while (le != &ealist) {
4518         LIST_ENTRY* le2 = le->Flink;
4519 
4520         item = CONTAINING_RECORD(le, ea_item, list_entry);
4521 
4522         if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
4523             if (item->value.Length < sizeof(UINT32)) {
4524                 ERR("uid value was shorter than expected\n");
4525                 Status = STATUS_INVALID_PARAMETER;
4526                 goto end2;
4527             }
4528 
4529             if (Irp->RequestorMode == KernelMode) {
4530                 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(UINT32));
4531                 fcb->sd_dirty = TRUE;
4532                 fcb->sd_deleted = FALSE;
4533             }
4534 
4535             RemoveEntryList(&item->list_entry);
4536             ExFreePool(item);
4537         } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
4538             if (item->value.Length < sizeof(UINT32)) {
4539                 ERR("gid value was shorter than expected\n");
4540                 Status = STATUS_INVALID_PARAMETER;
4541                 goto end2;
4542             }
4543 
4544             if (Irp->RequestorMode == KernelMode)
4545                 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(UINT32));
4546 
4547             RemoveEntryList(&item->list_entry);
4548             ExFreePool(item);
4549         } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
4550             if (item->value.Length < sizeof(UINT32)) {
4551                 ERR("mode value was shorter than expected\n");
4552                 Status = STATUS_INVALID_PARAMETER;
4553                 goto end2;
4554             }
4555 
4556             if (Irp->RequestorMode == KernelMode) {
4557                 UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
4558                 UINT32 val;
4559 
4560                 RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32));
4561 
4562                 fcb->inode_item.st_mode &= ~allowed;
4563                 fcb->inode_item.st_mode |= val & allowed;
4564             }
4565 
4566             RemoveEntryList(&item->list_entry);
4567             ExFreePool(item);
4568         }
4569 
4570         le = le2;
4571     }
4572 
4573     if (IsListEmpty(&ealist)) {
4574         fcb->ealen = 0;
4575 
4576         if (fcb->ea_xattr.Buffer)
4577             ExFreePool(fcb->ea_xattr.Buffer);
4578 
4579         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4580         fcb->ea_xattr.Buffer = NULL;
4581     } else {
4582         UINT16 size = 0;
4583         char *buf, *oldbuf;
4584 
4585         le = ealist.Flink;
4586         while (le != &ealist) {
4587             item = CONTAINING_RECORD(le, ea_item, list_entry);
4588 
4589             if (size % 4 > 0)
4590                 size += 4 - (size % 4);
4591 
4592             size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
4593 
4594             le = le->Flink;
4595         }
4596 
4597         buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
4598         if (!buf) {
4599             ERR("out of memory\n");
4600             Status = STATUS_INSUFFICIENT_RESOURCES;
4601             goto end2;
4602         }
4603 
4604         oldbuf = fcb->ea_xattr.Buffer;
4605 
4606         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
4607         fcb->ea_xattr.Buffer = buf;
4608 
4609         fcb->ealen = 4;
4610         ea = NULL;
4611 
4612         le = ealist.Flink;
4613         while (le != &ealist) {
4614             item = CONTAINING_RECORD(le, ea_item, list_entry);
4615 
4616             if (ea) {
4617                 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
4618 
4619                 if (ea->NextEntryOffset % 4 > 0)
4620                     ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
4621 
4622                 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4623             } else
4624                 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4625 
4626             ea->NextEntryOffset = 0;
4627             ea->Flags = item->flags;
4628             ea->EaNameLength = (UCHAR)item->name.Length;
4629             ea->EaValueLength = item->value.Length;
4630 
4631             RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
4632             ea->EaName[item->name.Length] = 0;
4633             RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
4634 
4635             fcb->ealen += 5 + item->name.Length + item->value.Length;
4636 
4637             le = le->Flink;
4638         }
4639 
4640         if (oldbuf)
4641             ExFreePool(oldbuf);
4642     }
4643 
4644     fcb->ea_changed = TRUE;
4645 
4646     KeQuerySystemTime(&time);
4647     win_time_to_unix(time, &now);
4648 
4649     fcb->inode_item.transid = Vcb->superblock.generation;
4650     fcb->inode_item.sequence++;
4651 
4652     if (!ccb->user_set_change_time)
4653         fcb->inode_item.st_ctime = now;
4654 
4655     fcb->inode_item_changed = TRUE;
4656     mark_fcb_dirty(fcb);
4657 
4658     send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
4659 
4660     Status = STATUS_SUCCESS;
4661 
4662 end2:
4663     ExReleaseResourceLite(fcb->Header.Resource);
4664 
4665     while (!IsListEmpty(&ealist)) {
4666         le = RemoveHeadList(&ealist);
4667 
4668         item = CONTAINING_RECORD(le, ea_item, list_entry);
4669 
4670         ExFreePool(item);
4671     }
4672 
4673 end:
4674     TRACE("returning %08x\n", Status);
4675 
4676     Irp->IoStatus.Status = Status;
4677     Irp->IoStatus.Information = 0;
4678 
4679     IoCompleteRequest(Irp, IO_NO_INCREMENT);
4680 
4681     if (top_level)
4682         IoSetTopLevelIrp(NULL);
4683 
4684     FsRtlExitFileSystem();
4685 
4686     return Status;
4687 }
4688