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