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