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     } else if ((UINT64)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
2068         Status = STATUS_SUCCESS;
2069         goto end;
2070     }
2071 
2072     ccfs.AllocationSize = fcb->Header.AllocationSize;
2073     ccfs.FileSize = fcb->Header.FileSize;
2074     ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2075     set_size = TRUE;
2076 
2077     filter = FILE_NOTIFY_CHANGE_SIZE;
2078 
2079     if (!ccb->user_set_write_time) {
2080         KeQuerySystemTime(&time);
2081         win_time_to_unix(time, &fcb->inode_item.st_mtime);
2082         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2083     }
2084 
2085     fcb->inode_item_changed = TRUE;
2086     mark_fcb_dirty(fcb);
2087     send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2088 
2089     Status = STATUS_SUCCESS;
2090 
2091 end:
2092     if (NT_SUCCESS(Status))
2093         clear_rollback(&rollback);
2094     else
2095         do_rollback(Vcb, &rollback);
2096 
2097     ExReleaseResourceLite(fcb->Header.Resource);
2098 
2099     if (set_size) {
2100         _SEH2_TRY {
2101             CcSetFileSizes(FileObject, &ccfs);
2102         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2103             Status = _SEH2_GetExceptionCode();
2104         } _SEH2_END;
2105 
2106         if (!NT_SUCCESS(Status))
2107             ERR("CcSetFileSizes threw exception %08x\n", Status);
2108     }
2109 
2110     ExReleaseResourceLite(&Vcb->tree_lock);
2111 
2112     return Status;
2113 }
2114 
2115 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
2116     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2117 
2118     TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
2119 
2120     // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2121 
2122     FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
2123 
2124     return STATUS_SUCCESS;
2125 }
2126 
2127 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
2128     FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
2129     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
2130     ccb* ccb = FileObject->FsContext2;
2131     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2132     WCHAR* fn;
2133     ULONG fnlen, utf8len;
2134     UNICODE_STRING fnus;
2135     ANSI_STRING utf8;
2136     NTSTATUS Status;
2137     LARGE_INTEGER time;
2138     BTRFS_TIME now;
2139     LIST_ENTRY rollback;
2140     hardlink* hl;
2141     ACCESS_MASK access;
2142     SECURITY_SUBJECT_CONTEXT subjcont;
2143     dir_child* dc = NULL;
2144 
2145     InitializeListHead(&rollback);
2146 
2147     // FIXME - check fli length
2148     // FIXME - don't ignore fli->RootDirectory
2149 
2150     TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
2151     TRACE("RootDirectory = %p\n", fli->RootDirectory);
2152     TRACE("FileNameLength = %x\n", fli->FileNameLength);
2153     TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
2154 
2155     fn = fli->FileName;
2156     fnlen = fli->FileNameLength / sizeof(WCHAR);
2157 
2158     if (!tfo) {
2159         if (!fileref || !fileref->parent) {
2160             ERR("no fileref set and no directory given\n");
2161             return STATUS_INVALID_PARAMETER;
2162         }
2163 
2164         parfcb = fileref->parent->fcb;
2165         tfofcb = NULL;
2166     } else {
2167         LONG i;
2168 
2169         tfofcb = tfo->FsContext;
2170         parfcb = tfofcb;
2171 
2172         while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
2173             fnlen--;
2174 
2175         if (fnlen == 0)
2176             return STATUS_INVALID_PARAMETER;
2177 
2178         for (i = fnlen - 1; i >= 0; i--) {
2179             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
2180                 fn = &fli->FileName[i+1];
2181                 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
2182                 break;
2183             }
2184         }
2185     }
2186 
2187     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2188     acquire_fcb_lock_exclusive(Vcb);
2189     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2190 
2191     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2192         WARN("tried to create hard link on directory\n");
2193         Status = STATUS_FILE_IS_A_DIRECTORY;
2194         goto end;
2195     }
2196 
2197     if (fcb->ads) {
2198         WARN("tried to create hard link on stream\n");
2199         Status = STATUS_INVALID_PARAMETER;
2200         goto end;
2201     }
2202 
2203     if (fcb->inode_item.st_nlink >= 65535) {
2204         Status = STATUS_TOO_MANY_LINKS;
2205         goto end;
2206     }
2207 
2208     fnus.Buffer = fn;
2209     fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
2210 
2211     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
2212 
2213     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2214     if (!NT_SUCCESS(Status))
2215         goto end;
2216 
2217     utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
2218     utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2219     if (!utf8.Buffer) {
2220         ERR("out of memory\n");
2221         Status = STATUS_INSUFFICIENT_RESOURCES;
2222         goto end;
2223     }
2224 
2225     Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2226     if (!NT_SUCCESS(Status))
2227         goto end;
2228 
2229     if (tfo && tfo->FsContext2) {
2230         struct _ccb* relatedccb = tfo->FsContext2;
2231 
2232         related = relatedccb->fileref;
2233         increase_fileref_refcount(related);
2234     }
2235 
2236     Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2237 
2238     if (NT_SUCCESS(Status)) {
2239         if (!oldfileref->deleted) {
2240             WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
2241 
2242             if (!fli->ReplaceIfExists) {
2243                 Status = STATUS_OBJECT_NAME_COLLISION;
2244                 goto end;
2245             } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) {
2246                 WARN("trying to overwrite open file\n");
2247                 Status = STATUS_ACCESS_DENIED;
2248                 goto end;
2249             } else if (fileref == oldfileref) {
2250                 Status = STATUS_ACCESS_DENIED;
2251                 goto end;
2252             }
2253 
2254             if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2255                 WARN("trying to overwrite directory\n");
2256                 Status = STATUS_ACCESS_DENIED;
2257                 goto end;
2258             }
2259         } else {
2260             free_fileref(Vcb, oldfileref);
2261             oldfileref = NULL;
2262         }
2263     }
2264 
2265     if (!related) {
2266         Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2267 
2268         if (!NT_SUCCESS(Status)) {
2269             ERR("open_fileref returned %08x\n", Status);
2270             goto end;
2271         }
2272     }
2273 
2274     SeCaptureSubjectContext(&subjcont);
2275 
2276     if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL,
2277                        IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2278         SeReleaseSubjectContext(&subjcont);
2279         TRACE("SeAccessCheck failed, returning %08x\n", Status);
2280         goto end;
2281     }
2282 
2283     SeReleaseSubjectContext(&subjcont);
2284 
2285     if (fcb->subvol != parfcb->subvol) {
2286         WARN("can't create hard link over subvolume boundary\n");
2287         Status = STATUS_INVALID_PARAMETER;
2288         goto end;
2289     }
2290 
2291     if (oldfileref) {
2292         SeCaptureSubjectContext(&subjcont);
2293 
2294         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
2295                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2296             SeReleaseSubjectContext(&subjcont);
2297             TRACE("SeAccessCheck failed, returning %08x\n", Status);
2298             goto end;
2299         }
2300 
2301         SeReleaseSubjectContext(&subjcont);
2302 
2303         Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
2304         if (!NT_SUCCESS(Status)) {
2305             ERR("delete_fileref returned %08x\n", Status);
2306             goto end;
2307         }
2308     }
2309 
2310     fr2 = create_fileref(Vcb);
2311 
2312     fr2->fcb = fcb;
2313     fcb->refcount++;
2314 
2315     fr2->created = TRUE;
2316     fr2->parent = related;
2317 
2318     Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc);
2319     if (!NT_SUCCESS(Status))
2320         WARN("add_dir_child returned %08x\n", Status);
2321 
2322     fr2->dc = dc;
2323     dc->fileref = fr2;
2324 
2325     ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
2326     InsertTailList(&related->children, &fr2->list_entry);
2327     ExReleaseResourceLite(&related->nonpaged->children_lock);
2328 
2329     // add hardlink for existing fileref, if it's not there already
2330     if (IsListEmpty(&fcb->hardlinks)) {
2331         hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2332         if (!hl) {
2333             ERR("out of memory\n");
2334             Status = STATUS_INSUFFICIENT_RESOURCES;
2335             goto end;
2336         }
2337 
2338         hl->parent = fileref->parent->fcb->inode;
2339         hl->index = fileref->dc->index;
2340 
2341         hl->name.Length = hl->name.MaximumLength = fnus.Length;
2342         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2343 
2344         if (!hl->name.Buffer) {
2345             ERR("out of memory\n");
2346             ExFreePool(hl);
2347             Status = STATUS_INSUFFICIENT_RESOURCES;
2348             goto end;
2349         }
2350 
2351         RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2352 
2353         hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
2354         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2355 
2356         if (!hl->utf8.Buffer) {
2357             ERR("out of memory\n");
2358             ExFreePool(hl->name.Buffer);
2359             ExFreePool(hl);
2360             Status = STATUS_INSUFFICIENT_RESOURCES;
2361             goto end;
2362         }
2363 
2364         RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2365 
2366         InsertTailList(&fcb->hardlinks, &hl->list_entry);
2367     }
2368 
2369     hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2370     if (!hl) {
2371         ERR("out of memory\n");
2372         Status = STATUS_INSUFFICIENT_RESOURCES;
2373         goto end;
2374     }
2375 
2376     hl->parent = related->fcb->inode;
2377     hl->index = dc->index;
2378 
2379     hl->name.Length = hl->name.MaximumLength = fnus.Length;
2380     hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
2381 
2382     if (!hl->name.Buffer) {
2383         ERR("out of memory\n");
2384         ExFreePool(hl);
2385         Status = STATUS_INSUFFICIENT_RESOURCES;
2386         goto end;
2387     }
2388 
2389     RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2390 
2391     hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
2392     hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2393 
2394     if (!hl->utf8.Buffer) {
2395         ERR("out of memory\n");
2396         ExFreePool(hl->name.Buffer);
2397         ExFreePool(hl);
2398         Status = STATUS_INSUFFICIENT_RESOURCES;
2399         goto end;
2400     }
2401 
2402     RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
2403     ExFreePool(utf8.Buffer);
2404 
2405     InsertTailList(&fcb->hardlinks, &hl->list_entry);
2406 
2407     mark_fileref_dirty(fr2);
2408     free_fileref(Vcb, fr2);
2409 
2410     // update inode's INODE_ITEM
2411 
2412     KeQuerySystemTime(&time);
2413     win_time_to_unix(time, &now);
2414 
2415     fcb->inode_item.transid = Vcb->superblock.generation;
2416     fcb->inode_item.sequence++;
2417     fcb->inode_item.st_nlink++;
2418 
2419     if (!ccb->user_set_change_time)
2420         fcb->inode_item.st_ctime = now;
2421 
2422     fcb->inode_item_changed = TRUE;
2423     mark_fcb_dirty(fcb);
2424 
2425     // update parent's INODE_ITEM
2426 
2427     parfcb->inode_item.transid = Vcb->superblock.generation;
2428     TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2429     parfcb->inode_item.st_size += 2 * utf8len;
2430     TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2431     parfcb->inode_item.sequence++;
2432     parfcb->inode_item.st_ctime = now;
2433 
2434     parfcb->inode_item_changed = TRUE;
2435     mark_fcb_dirty(parfcb);
2436 
2437     send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
2438 
2439     Status = STATUS_SUCCESS;
2440 
2441 end:
2442     if (oldfileref)
2443         free_fileref(Vcb, oldfileref);
2444 
2445     if (!NT_SUCCESS(Status) && related)
2446         free_fileref(Vcb, related);
2447 
2448     if (!NT_SUCCESS(Status) && fr2)
2449         free_fileref(Vcb, fr2);
2450 
2451     if (NT_SUCCESS(Status))
2452         clear_rollback(&rollback);
2453     else
2454         do_rollback(Vcb, &rollback);
2455 
2456     ExReleaseResourceLite(fcb->Header.Resource);
2457     release_fcb_lock(Vcb);
2458     ExReleaseResourceLite(&Vcb->tree_lock);
2459 
2460     return Status;
2461 }
2462 
2463 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2464     FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
2465     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2466     fcb* fcb = FileObject->FsContext;
2467     ccb* ccb = FileObject->FsContext2;
2468     file_ref* fileref = ccb ? ccb->fileref : NULL;
2469     NTSTATUS Status;
2470     LARGE_INTEGER time;
2471     CC_FILE_SIZES ccfs;
2472     LIST_ENTRY rollback;
2473     BOOL set_size = FALSE;
2474     ULONG filter;
2475 
2476     if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
2477         ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
2478         return STATUS_INVALID_PARAMETER;
2479     }
2480 
2481     if (!fileref) {
2482         ERR("fileref is NULL\n");
2483         return STATUS_INVALID_PARAMETER;
2484     }
2485 
2486     InitializeListHead(&rollback);
2487 
2488     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2489 
2490     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2491 
2492     if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
2493         Status = STATUS_INVALID_PARAMETER;
2494         goto end;
2495     }
2496 
2497     if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
2498         TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart,
2499               fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
2500         Status = STATUS_INVALID_PARAMETER;
2501         goto end;
2502     }
2503 
2504     if (fileref ? fileref->deleted : fcb->deleted) {
2505         Status = STATUS_FILE_CLOSED;
2506         goto end;
2507     }
2508 
2509     // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2510     // and we set it to the max anyway.
2511 
2512     ccfs.AllocationSize = fcb->Header.AllocationSize;
2513     ccfs.FileSize = fcb->Header.FileSize;
2514     ccfs.ValidDataLength = fvdli->ValidDataLength;
2515     set_size = TRUE;
2516 
2517     filter = FILE_NOTIFY_CHANGE_SIZE;
2518 
2519     if (!ccb->user_set_write_time) {
2520         KeQuerySystemTime(&time);
2521         win_time_to_unix(time, &fcb->inode_item.st_mtime);
2522         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2523     }
2524 
2525     fcb->inode_item_changed = TRUE;
2526     mark_fcb_dirty(fcb);
2527 
2528     send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2529 
2530     Status = STATUS_SUCCESS;
2531 
2532 end:
2533     if (NT_SUCCESS(Status))
2534         clear_rollback(&rollback);
2535     else
2536         do_rollback(Vcb, &rollback);
2537 
2538     ExReleaseResourceLite(fcb->Header.Resource);
2539 
2540     if (set_size) {
2541         _SEH2_TRY {
2542             CcSetFileSizes(FileObject, &ccfs);
2543         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2544             Status = _SEH2_GetExceptionCode();
2545         } _SEH2_END;
2546 
2547         if (!NT_SUCCESS(Status))
2548             ERR("CcSetFileSizes threw exception %08x\n", Status);
2549         else
2550             fcb->Header.AllocationSize = ccfs.AllocationSize;
2551     }
2552 
2553     ExReleaseResourceLite(&Vcb->tree_lock);
2554 
2555     return Status;
2556 }
2557 
2558 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
2559 _Function_class_(DRIVER_DISPATCH)
2560 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2561     NTSTATUS Status;
2562     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2563     device_extension* Vcb = DeviceObject->DeviceExtension;
2564     fcb* fcb = IrpSp->FileObject->FsContext;
2565     ccb* ccb = IrpSp->FileObject->FsContext2;
2566     BOOL top_level;
2567 
2568     FsRtlEnterFileSystem();
2569 
2570     top_level = is_top_level(Irp);
2571 
2572     Irp->IoStatus.Information = 0;
2573 
2574     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2575         Status = vol_set_information(DeviceObject, Irp);
2576         goto end;
2577     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2578         Status = STATUS_INVALID_PARAMETER;
2579         goto end;
2580     }
2581 
2582     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
2583         Status = STATUS_ACCESS_DENIED;
2584         goto end;
2585     }
2586 
2587     if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
2588         Status = STATUS_MEDIA_WRITE_PROTECTED;
2589         goto end;
2590     }
2591 
2592     if (!fcb) {
2593         ERR("no fcb\n");
2594         Status = STATUS_INVALID_PARAMETER;
2595         goto end;
2596     }
2597 
2598     if (!ccb) {
2599         ERR("no ccb\n");
2600         Status = STATUS_INVALID_PARAMETER;
2601         goto end;
2602     }
2603 
2604     if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
2605         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
2606         Status = STATUS_ACCESS_DENIED;
2607         goto end;
2608     }
2609 
2610     Status = STATUS_NOT_IMPLEMENTED;
2611 
2612     TRACE("set information\n");
2613 
2614     switch (IrpSp->Parameters.SetFile.FileInformationClass) {
2615         case FileAllocationInformation:
2616         {
2617             TRACE("FileAllocationInformation\n");
2618 
2619             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
2620                 WARN("insufficient privileges\n");
2621                 Status = STATUS_ACCESS_DENIED;
2622                 break;
2623             }
2624 
2625             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE);
2626             break;
2627         }
2628 
2629         case FileBasicInformation:
2630         {
2631             TRACE("FileBasicInformation\n");
2632 
2633             if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
2634                 WARN("insufficient privileges\n");
2635                 Status = STATUS_ACCESS_DENIED;
2636                 break;
2637             }
2638 
2639             Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
2640 
2641             break;
2642         }
2643 
2644         case FileDispositionInformation:
2645         {
2646             TRACE("FileDispositionInformation\n");
2647 
2648             if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
2649                 WARN("insufficient privileges\n");
2650                 Status = STATUS_ACCESS_DENIED;
2651                 break;
2652             }
2653 
2654             Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
2655 
2656             break;
2657         }
2658 
2659         case FileEndOfFileInformation:
2660         {
2661             TRACE("FileEndOfFileInformation\n");
2662 
2663             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2664                 WARN("insufficient privileges\n");
2665                 Status = STATUS_ACCESS_DENIED;
2666                 break;
2667             }
2668 
2669             Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE);
2670 
2671             break;
2672         }
2673 
2674         case FileLinkInformation:
2675             TRACE("FileLinkInformation\n");
2676             Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2677             break;
2678 
2679         case FilePositionInformation:
2680             TRACE("FilePositionInformation\n");
2681             Status = set_position_information(IrpSp->FileObject, Irp);
2682             break;
2683 
2684         case FileRenameInformation:
2685             TRACE("FileRenameInformation\n");
2686             // FIXME - make this work with streams
2687             Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2688             break;
2689 
2690         case FileValidDataLengthInformation:
2691         {
2692             TRACE("FileValidDataLengthInformation\n");
2693 
2694             if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2695                 WARN("insufficient privileges\n");
2696                 Status = STATUS_ACCESS_DENIED;
2697                 break;
2698             }
2699 
2700             Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
2701 
2702             break;
2703         }
2704 
2705         default:
2706             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
2707     }
2708 
2709 end:
2710     Irp->IoStatus.Status = Status;
2711 
2712     TRACE("returning %08x\n", Status);
2713 
2714     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2715 
2716     if (top_level)
2717         IoSetTopLevelIrp(NULL);
2718 
2719     FsRtlExitFileSystem();
2720 
2721     return Status;
2722 }
2723 
2724 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
2725     RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
2726 
2727     *length -= sizeof(FILE_BASIC_INFORMATION);
2728 
2729     if (fcb == fcb->Vcb->dummy_fcb) {
2730         LARGE_INTEGER time;
2731 
2732         KeQuerySystemTime(&time);
2733         fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
2734     } else {
2735         fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2736         fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2737         fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2738         fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2739     }
2740 
2741     if (fcb->ads) {
2742         if (!fileref || !fileref->parent) {
2743             ERR("no fileref for stream\n");
2744             return STATUS_INTERNAL_ERROR;
2745         } else
2746             fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2747     } else
2748         fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2749 
2750     return STATUS_SUCCESS;
2751 }
2752 
2753 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
2754     INODE_ITEM* ii;
2755 
2756     if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
2757         WARN("overflow\n");
2758         return STATUS_BUFFER_OVERFLOW;
2759     }
2760 
2761     RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
2762 
2763     *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
2764 
2765     if (fcb->ads) {
2766         if (!fileref || !fileref->parent) {
2767             ERR("no fileref for stream\n");
2768             return STATUS_INTERNAL_ERROR;
2769         }
2770 
2771         ii = &fileref->parent->fcb->inode_item;
2772     } else
2773         ii = &fcb->inode_item;
2774 
2775     if (fcb == fcb->Vcb->dummy_fcb) {
2776         LARGE_INTEGER time;
2777 
2778         KeQuerySystemTime(&time);
2779         fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
2780     } else {
2781         fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2782         fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2783         fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2784         fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2785     }
2786 
2787     if (fcb->ads) {
2788         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
2789         fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2790     } else {
2791         fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2792         fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2793         fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2794     }
2795 
2796     return STATUS_SUCCESS;
2797 }
2798 
2799 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
2800     RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
2801 
2802     *length -= sizeof(FILE_STANDARD_INFORMATION);
2803 
2804     if (fcb->ads) {
2805         if (!fileref || !fileref->parent) {
2806             ERR("no fileref for stream\n");
2807             return STATUS_INTERNAL_ERROR;
2808         }
2809 
2810         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
2811         fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
2812         fsi->Directory = FALSE;
2813     } else {
2814         fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2815         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2816         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
2817         fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
2818     }
2819 
2820     TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
2821 
2822     fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE;
2823 
2824     return STATUS_SUCCESS;
2825 }
2826 
2827 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
2828     *length -= sizeof(FILE_INTERNAL_INFORMATION);
2829 
2830     fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
2831 
2832     return STATUS_SUCCESS;
2833 }
2834 
2835 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
2836     *length -= sizeof(FILE_EA_INFORMATION);
2837 
2838     /* This value appears to be the size of the structure NTFS stores on disk, and not,
2839      * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2840      * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2841      * item. */
2842 
2843     eai->EaSize = fcb->ealen;
2844 
2845     return STATUS_SUCCESS;
2846 }
2847 
2848 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
2849     RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
2850 
2851     *length -= sizeof(FILE_POSITION_INFORMATION);
2852 
2853     fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
2854 
2855     return STATUS_SUCCESS;
2856 }
2857 
2858 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
2859     file_ref* fr;
2860     NTSTATUS Status;
2861     ULONG reqlen = 0;
2862     USHORT offset;
2863     BOOL overflow = FALSE;
2864 
2865     // FIXME - we need a lock on filerefs' filepart
2866 
2867     if (fileref == fileref->fcb->Vcb->root_fileref) {
2868         if (fn->MaximumLength >= sizeof(WCHAR)) {
2869             fn->Buffer[0] = '\\';
2870             fn->Length = sizeof(WCHAR);
2871 
2872             if (name_offset)
2873                 *name_offset = 0;
2874 
2875             return STATUS_SUCCESS;
2876         } else {
2877             if (preqlen)
2878                 *preqlen = sizeof(WCHAR);
2879             fn->Length = 0;
2880 
2881             return STATUS_BUFFER_OVERFLOW;
2882         }
2883     }
2884 
2885     fr = fileref;
2886     offset = 0;
2887 
2888     while (fr->parent) {
2889         USHORT movelen;
2890 
2891         if (!fr->dc)
2892             return STATUS_INTERNAL_ERROR;
2893 
2894         if (!overflow) {
2895             if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
2896                 overflow = TRUE;
2897         }
2898 
2899         if (overflow)
2900             movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
2901         else
2902             movelen = fn->Length;
2903 
2904         if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
2905             RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
2906             offset += fr->dc->name.Length + sizeof(WCHAR);
2907         }
2908 
2909         if (fn->MaximumLength >= sizeof(WCHAR)) {
2910             fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
2911             fn->Length += sizeof(WCHAR);
2912 
2913             if (fn->MaximumLength > sizeof(WCHAR)) {
2914                 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
2915                 fn->Length += fr->dc->name.Length;
2916             }
2917 
2918             if (fn->Length > fn->MaximumLength) {
2919                 fn->Length = fn->MaximumLength;
2920                 overflow = TRUE;
2921             }
2922         }
2923 
2924         reqlen += sizeof(WCHAR) + fr->dc->name.Length;
2925 
2926         fr = fr->parent;
2927     }
2928 
2929     offset += sizeof(WCHAR);
2930 
2931     if (overflow) {
2932         if (preqlen)
2933             *preqlen = reqlen;
2934         Status = STATUS_BUFFER_OVERFLOW;
2935     } else {
2936         if (name_offset)
2937             *name_offset = offset;
2938 
2939         Status = STATUS_SUCCESS;
2940     }
2941 
2942     return Status;
2943 }
2944 
2945 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
2946     ULONG reqlen;
2947     UNICODE_STRING fn;
2948     NTSTATUS Status;
2949     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
2950     UINT16 datasuflen = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
2951 
2952     if (!fileref) {
2953         ERR("called without fileref\n");
2954         return STATUS_INVALID_PARAMETER;
2955     }
2956 
2957     *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
2958 
2959     TRACE("maximum length is %u\n", *length);
2960     fni->FileNameLength = 0;
2961 
2962     fni->FileName[0] = 0;
2963 
2964     fn.Buffer = fni->FileName;
2965     fn.Length = 0;
2966     fn.MaximumLength = (UINT16)*length;
2967 
2968     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
2969     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
2970         ERR("fileref_get_filename returned %08x\n", Status);
2971         return Status;
2972     }
2973 
2974     if (fcb->ads) {
2975         if (Status == STATUS_BUFFER_OVERFLOW)
2976             reqlen += datasuflen;
2977         else {
2978             if (fn.Length + datasuflen > fn.MaximumLength) {
2979                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
2980                 reqlen += datasuflen;
2981                 Status = STATUS_BUFFER_OVERFLOW;
2982             } else {
2983                 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
2984                 fn.Length += datasuflen;
2985             }
2986         }
2987     }
2988 
2989     if (Status == STATUS_BUFFER_OVERFLOW) {
2990         *length = -1;
2991         fni->FileNameLength = reqlen;
2992         TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
2993     } else {
2994         *length -= fn.Length;
2995         fni->FileNameLength = fn.Length;
2996         TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
2997     }
2998 
2999     return Status;
3000 }
3001 
3002 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, PIRP Irp, LONG* length) {
3003     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
3004 
3005     if (fcb->ads) {
3006         if (!ccb->fileref || !ccb->fileref->parent) {
3007             ERR("no fileref for stream\n");
3008             return STATUS_INTERNAL_ERROR;
3009         }
3010 
3011         ati->FileAttributes = ccb->fileref->parent->fcb->atts;
3012     } else
3013         ati->FileAttributes = fcb->atts;
3014 
3015     if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3016         ati->ReparseTag = 0;
3017     else
3018         ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, ccb->lxss, Irp);
3019 
3020     return STATUS_SUCCESS;
3021 }
3022 
3023 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
3024     LONG reqsize;
3025     LIST_ENTRY* le;
3026     FILE_STREAM_INFORMATION *entry, *lastentry;
3027     NTSTATUS Status;
3028 
3029     static WCHAR datasuf[] = L":$DATA";
3030     UNICODE_STRING suf;
3031 
3032     if (!fileref) {
3033         ERR("fileref was NULL\n");
3034         return STATUS_INVALID_PARAMETER;
3035     }
3036 
3037     suf.Buffer = datasuf;
3038     suf.Length = suf.MaximumLength = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
3039 
3040     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
3041         reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
3042     else
3043         reqsize = 0;
3044 
3045     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
3046 
3047     le = fileref->fcb->dir_children_index.Flink;
3048     while (le != &fileref->fcb->dir_children_index) {
3049         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3050 
3051         if (dc->index == 0) {
3052             reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
3053             reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
3054         } else
3055             break;
3056 
3057         le = le->Flink;
3058     }
3059 
3060     TRACE("length = %i, reqsize = %u\n", *length, reqsize);
3061 
3062     if (reqsize > *length) {
3063         Status = STATUS_BUFFER_OVERFLOW;
3064         goto end;
3065     }
3066 
3067     entry = fsi;
3068     lastentry = NULL;
3069 
3070     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
3071         ULONG off;
3072 
3073         entry->NextEntryOffset = 0;
3074         entry->StreamNameLength = suf.Length + sizeof(WCHAR);
3075         entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
3076         entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
3077 
3078         entry->StreamName[0] = ':';
3079         RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
3080 
3081         off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
3082 
3083         lastentry = entry;
3084         entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3085     }
3086 
3087     le = fileref->fcb->dir_children_index.Flink;
3088     while (le != &fileref->fcb->dir_children_index) {
3089         dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3090 
3091         if (dc->index == 0) {
3092             ULONG off;
3093 
3094             entry->NextEntryOffset = 0;
3095             entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
3096 
3097             if (dc->fileref)
3098                 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
3099             else
3100                 entry->StreamSize.QuadPart = dc->size;
3101 
3102             entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
3103 
3104             entry->StreamName[0] = ':';
3105 
3106             RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
3107             RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
3108 
3109             if (lastentry)
3110                 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry);
3111 
3112             off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
3113 
3114             lastentry = entry;
3115             entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3116         } else
3117             break;
3118 
3119         le = le->Flink;
3120     }
3121 
3122     *length -= reqsize;
3123 
3124     Status = STATUS_SUCCESS;
3125 
3126 end:
3127     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
3128 
3129     return Status;
3130 }
3131 
3132 #ifndef __REACTOS__
3133 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
3134     TRACE("FileStandardLinkInformation\n");
3135 
3136     // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3137 
3138     fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
3139     fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
3140     fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
3141     fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE;
3142 
3143     *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
3144 
3145     return STATUS_SUCCESS;
3146 }
3147 #endif /* __REACTOS__ */
3148 
3149 NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
3150                                root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
3151     NTSTATUS Status;
3152     fcb* fcb;
3153     UINT64 parent = 0;
3154     UNICODE_STRING name;
3155     BOOL hl_alloc = FALSE;
3156     file_ref *parfr, *fr;
3157 
3158     Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
3159     if (!NT_SUCCESS(Status)) {
3160         ERR("open_fcb returned %08x\n", Status);
3161         return Status;
3162     }
3163 
3164     if (fcb->fileref) {
3165         *pfr = fcb->fileref;
3166         increase_fileref_refcount(fcb->fileref);
3167         return STATUS_SUCCESS;
3168     }
3169 
3170     // find hardlink if fcb doesn't have any loaded
3171     if (IsListEmpty(&fcb->hardlinks)) {
3172         KEY searchkey;
3173         traverse_ptr tp;
3174 
3175         searchkey.obj_id = fcb->inode;
3176         searchkey.obj_type = TYPE_INODE_EXTREF;
3177         searchkey.offset = 0xffffffffffffffff;
3178 
3179         Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
3180         if (!NT_SUCCESS(Status)) {
3181             ERR("find_item returned %08x\n", Status);
3182             free_fcb(Vcb, fcb);
3183             return Status;
3184         }
3185 
3186         if (tp.item->key.obj_id == fcb->inode) {
3187             if (tp.item->key.obj_type == TYPE_INODE_REF) {
3188                 INODE_REF* ir;
3189                 ULONG stringlen;
3190 
3191                 ir = (INODE_REF*)tp.item->data;
3192 
3193                 parent = tp.item->key.offset;
3194 
3195                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
3196                 if (!NT_SUCCESS(Status)) {
3197                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3198                     free_fcb(Vcb, fcb);
3199                     return Status;
3200                 }
3201 
3202                 name.Length = name.MaximumLength = (UINT16)stringlen;
3203 
3204                 if (stringlen == 0)
3205                     name.Buffer = NULL;
3206                 else {
3207                     name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3208 
3209                     if (!name.Buffer) {
3210                         ERR("out of memory\n");
3211                         free_fcb(Vcb, fcb);
3212                         return STATUS_INSUFFICIENT_RESOURCES;
3213                     }
3214 
3215                     Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
3216                     if (!NT_SUCCESS(Status)) {
3217                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3218                         ExFreePool(name.Buffer);
3219                         free_fcb(Vcb, fcb);
3220                         return Status;
3221                     }
3222 
3223                     hl_alloc = TRUE;
3224                 }
3225             } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3226                 INODE_EXTREF* ier;
3227                 ULONG stringlen;
3228 
3229                 ier = (INODE_EXTREF*)tp.item->data;
3230 
3231                 parent = ier->dir;
3232 
3233                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
3234                 if (!NT_SUCCESS(Status)) {
3235                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3236                     free_fcb(Vcb, fcb);
3237                     return Status;
3238                 }
3239 
3240                 name.Length = name.MaximumLength = (UINT16)stringlen;
3241 
3242                 if (stringlen == 0)
3243                     name.Buffer = NULL;
3244                 else {
3245                     name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3246 
3247                     if (!name.Buffer) {
3248                         ERR("out of memory\n");
3249                         free_fcb(Vcb, fcb);
3250                         return STATUS_INSUFFICIENT_RESOURCES;
3251                     }
3252 
3253                     Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
3254                     if (!NT_SUCCESS(Status)) {
3255                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3256                         ExFreePool(name.Buffer);
3257                         free_fcb(Vcb, fcb);
3258                         return Status;
3259                     }
3260 
3261                     hl_alloc = TRUE;
3262                 }
3263 
3264             }
3265         }
3266     } else {
3267         hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
3268 
3269         name = hl->name;
3270         parent = hl->parent;
3271     }
3272 
3273     if (parent == 0) {
3274         ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
3275         free_fcb(Vcb, fcb);
3276         if (hl_alloc) ExFreePool(name.Buffer);
3277         return STATUS_INVALID_PARAMETER;
3278     }
3279 
3280     if (parent == inode) { // subvolume root
3281         KEY searchkey;
3282         traverse_ptr tp;
3283 
3284         searchkey.obj_id = subvol->id;
3285         searchkey.obj_type = TYPE_ROOT_BACKREF;
3286         searchkey.offset = 0xffffffffffffffff;
3287 
3288         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3289         if (!NT_SUCCESS(Status)) {
3290             ERR("find_item returned %08x\n", Status);
3291             free_fcb(Vcb, fcb);
3292             if (hl_alloc) ExFreePool(name.Buffer);
3293             return Status;
3294         }
3295 
3296         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
3297             ROOT_REF* rr = (ROOT_REF*)tp.item->data;
3298             LIST_ENTRY* le;
3299             root* r = NULL;
3300             ULONG stringlen;
3301 
3302             if (tp.item->size < sizeof(ROOT_REF)) {
3303                 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));
3304                 free_fcb(Vcb, fcb);
3305                 if (hl_alloc) ExFreePool(name.Buffer);
3306                 return STATUS_INTERNAL_ERROR;
3307             }
3308 
3309             if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
3310                 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);
3311                 free_fcb(Vcb, fcb);
3312                 if (hl_alloc) ExFreePool(name.Buffer);
3313                 return STATUS_INTERNAL_ERROR;
3314             }
3315 
3316             le = Vcb->roots.Flink;
3317             while (le != &Vcb->roots) {
3318                 root* r2 = CONTAINING_RECORD(le, root, list_entry);
3319 
3320                 if (r2->id == tp.item->key.offset) {
3321                     r = r2;
3322                     break;
3323                 }
3324 
3325                 le = le->Flink;
3326             }
3327 
3328             if (!r) {
3329                 ERR("couldn't find subvol %llx\n", tp.item->key.offset);
3330                 free_fcb(Vcb, fcb);
3331                 if (hl_alloc) ExFreePool(name.Buffer);
3332                 return STATUS_INTERNAL_ERROR;
3333             }
3334 
3335             Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
3336             if (!NT_SUCCESS(Status)) {
3337                 ERR("open_fileref_by_inode returned %08x\n", Status);
3338                 free_fcb(Vcb, fcb);
3339                 if (hl_alloc) ExFreePool(name.Buffer);
3340                 return Status;
3341             }
3342 
3343             if (hl_alloc) {
3344                 ExFreePool(name.Buffer);
3345                 hl_alloc = FALSE;
3346             }
3347 
3348             Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n);
3349             if (!NT_SUCCESS(Status)) {
3350                 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
3351                 free_fcb(Vcb, fcb);
3352                 return Status;
3353             }
3354 
3355             name.Length = name.MaximumLength = (UINT16)stringlen;
3356 
3357             if (stringlen == 0)
3358                 name.Buffer = NULL;
3359             else {
3360                 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
3361 
3362                 if (!name.Buffer) {
3363                     ERR("out of memory\n");
3364                     free_fcb(Vcb, fcb);
3365                     return STATUS_INSUFFICIENT_RESOURCES;
3366                 }
3367 
3368                 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
3369                 if (!NT_SUCCESS(Status)) {
3370                     ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
3371                     ExFreePool(name.Buffer);
3372                     free_fcb(Vcb, fcb);
3373                     return Status;
3374                 }
3375 
3376                 hl_alloc = TRUE;
3377             }
3378         } else {
3379             ERR("couldn't find parent for subvol %llx\n", subvol->id);
3380             free_fcb(Vcb, fcb);
3381             if (hl_alloc) ExFreePool(name.Buffer);
3382             return STATUS_INTERNAL_ERROR;
3383         }
3384     } else {
3385         Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
3386         if (!NT_SUCCESS(Status)) {
3387             ERR("open_fileref_by_inode returned %08x\n", Status);
3388             free_fcb(Vcb, fcb);
3389 
3390             if (hl_alloc)
3391                 ExFreePool(name.Buffer);
3392 
3393             return Status;
3394         }
3395     }
3396 
3397     Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
3398 
3399     if (!NT_SUCCESS(Status)) {
3400         ERR("open_fileref_child returned %08x\n", Status);
3401 
3402         if (hl_alloc)
3403             ExFreePool(name.Buffer);
3404 
3405         free_fcb(Vcb, fcb);
3406         free_fileref(Vcb, parfr);
3407 
3408         return Status;
3409     }
3410 
3411     *pfr = fr;
3412 
3413     if (hl_alloc)
3414         ExFreePool(name.Buffer);
3415 
3416     free_fcb(Vcb, fcb);
3417     free_fileref(Vcb, parfr);
3418 
3419     return STATUS_SUCCESS;
3420 }
3421 
3422 #ifndef __REACTOS__
3423 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
3424     NTSTATUS Status;
3425     LIST_ENTRY* le;
3426     LONG bytes_needed;
3427     FILE_LINK_ENTRY_INFORMATION* feli;
3428     BOOL overflow = FALSE;
3429     fcb* fcb = fileref->fcb;
3430     ULONG len;
3431 
3432     if (fcb->ads)
3433         return STATUS_INVALID_PARAMETER;
3434 
3435     if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
3436         return STATUS_INVALID_PARAMETER;
3437 
3438     RtlZeroMemory(fli, *length);
3439 
3440     bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
3441     len = bytes_needed;
3442     feli = NULL;
3443 
3444     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
3445 
3446     if (fcb->inode == SUBVOL_ROOT_INODE) {
3447         ULONG namelen;
3448 
3449         if (fcb == fcb->Vcb->root_fileref->fcb)
3450             namelen = sizeof(WCHAR);
3451         else
3452             namelen = fileref->dc->name.Length;
3453 
3454         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
3455 
3456         if (bytes_needed > *length)
3457             overflow = TRUE;
3458 
3459         if (!overflow) {
3460             feli = &fli->Entry;
3461 
3462             feli->NextEntryOffset = 0;
3463             feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
3464 
3465             if (fcb == fcb->Vcb->root_fileref->fcb) {
3466                 feli->FileNameLength = 1;
3467                 feli->FileName[0] = '.';
3468             } else {
3469                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3470                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3471             }
3472 
3473             fli->EntriesReturned++;
3474 
3475             len = bytes_needed;
3476         }
3477     } else {
3478         acquire_fcb_lock_exclusive(fcb->Vcb);
3479 
3480         if (IsListEmpty(&fcb->hardlinks)) {
3481             bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
3482 
3483             if (bytes_needed > *length)
3484                 overflow = TRUE;
3485 
3486             if (!overflow) {
3487                 feli = &fli->Entry;
3488 
3489                 feli->NextEntryOffset = 0;
3490                 feli->ParentFileId = fileref->parent->fcb->inode;
3491                 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3492                 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3493 
3494                 fli->EntriesReturned++;
3495 
3496                 len = bytes_needed;
3497             }
3498         } else {
3499             le = fcb->hardlinks.Flink;
3500             while (le != &fcb->hardlinks) {
3501                 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
3502                 file_ref* parfr;
3503 
3504                 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
3505 
3506                 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
3507 
3508                 if (!NT_SUCCESS(Status)) {
3509                     ERR("open_fileref_by_inode returned %08x\n", Status);
3510                 } else if (!parfr->deleted) {
3511                     LIST_ENTRY* le2;
3512                     BOOL found = FALSE, deleted = FALSE;
3513                     UNICODE_STRING* fn = NULL;
3514 
3515                     le2 = parfr->children.Flink;
3516                     while (le2 != &parfr->children) {
3517                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
3518 
3519                         if (fr2->dc->index == hl->index) {
3520                             found = TRUE;
3521                             deleted = fr2->deleted;
3522 
3523                             if (!deleted)
3524                                 fn = &fr2->dc->name;
3525 
3526                             break;
3527                         }
3528 
3529                         le2 = le2->Flink;
3530                     }
3531 
3532                     if (!found)
3533                         fn = &hl->name;
3534 
3535                     if (!deleted) {
3536                         TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
3537 
3538                         if (feli)
3539                             bytes_needed = (LONG)sector_align(bytes_needed, 8);
3540 
3541                         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
3542 
3543                         if (bytes_needed > *length)
3544                             overflow = TRUE;
3545 
3546                         if (!overflow) {
3547                             if (feli) {
3548                                 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
3549                                 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset);
3550                             } else
3551                                 feli = &fli->Entry;
3552 
3553                             feli->NextEntryOffset = 0;
3554                             feli->ParentFileId = parfr->fcb->inode;
3555                             feli->FileNameLength = fn->Length / sizeof(WCHAR);
3556                             RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
3557 
3558                             fli->EntriesReturned++;
3559 
3560                             len = bytes_needed;
3561                         }
3562                     }
3563 
3564                     free_fileref(fcb->Vcb, parfr);
3565                 }
3566 
3567                 le = le->Flink;
3568             }
3569         }
3570 
3571         release_fcb_lock(fcb->Vcb);
3572     }
3573 
3574     fli->BytesNeeded = bytes_needed;
3575 
3576     *length -= len;
3577 
3578     Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
3579 
3580     ExReleaseResourceLite(fcb->Header.Resource);
3581 
3582     return Status;
3583 }
3584 #endif /* __REACTOS__ */
3585 
3586 #if (NTDDI_VERSION >= NTDDI_WIN10)
3587 #ifdef __MINGW32__
3588 typedef struct _FILE_ID_128 {
3589     UCHAR Identifier[16];
3590 } FILE_ID_128, *PFILE_ID_128;
3591 
3592 typedef struct _FILE_ID_INFORMATION {
3593     ULONGLONG VolumeSerialNumber;
3594     FILE_ID_128 FileId;
3595 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
3596 #endif
3597 
3598 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
3599     RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64));
3600     RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
3601     RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
3602 
3603     *length -= sizeof(FILE_ID_INFORMATION);
3604 
3605     return STATUS_SUCCESS;
3606 }
3607 #endif
3608 
3609 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
3610     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3611     LONG length = IrpSp->Parameters.QueryFile.Length;
3612     fcb* fcb = FileObject->FsContext;
3613     ccb* ccb = FileObject->FsContext2;
3614     file_ref* fileref = ccb ? ccb->fileref : NULL;
3615     NTSTATUS Status;
3616 
3617     TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
3618     TRACE("fcb = %p\n", fcb);
3619 
3620     if (fcb == Vcb->volume_fcb)
3621         return STATUS_INVALID_PARAMETER;
3622 
3623     if (!ccb) {
3624         ERR("ccb is NULL\n");
3625         return STATUS_INVALID_PARAMETER;
3626     }
3627 
3628     switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
3629         case FileAllInformation:
3630         {
3631             FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
3632             INODE_ITEM* ii;
3633 
3634             TRACE("FileAllInformation\n");
3635 
3636             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3637                 WARN("insufficient privileges\n");
3638                 Status = STATUS_ACCESS_DENIED;
3639                 goto exit;
3640             }
3641 
3642             if (fcb->ads) {
3643                 if (!fileref || !fileref->parent) {
3644                     ERR("no fileref for stream\n");
3645                     Status = STATUS_INTERNAL_ERROR;
3646                     goto exit;
3647                 }
3648 
3649                 ii = &fileref->parent->fcb->inode_item;
3650             } else
3651                 ii = &fcb->inode_item;
3652 
3653             // Access, mode, and alignment are all filled in by the kernel
3654 
3655             if (length > 0)
3656                 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
3657 
3658             if (length > 0)
3659                 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
3660 
3661             if (length > 0)
3662                 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
3663 
3664             if (length > 0)
3665                 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
3666 
3667             length -= sizeof(FILE_ACCESS_INFORMATION);
3668 
3669             if (length > 0)
3670                 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
3671 
3672             length -= sizeof(FILE_MODE_INFORMATION);
3673 
3674             length -= sizeof(FILE_ALIGNMENT_INFORMATION);
3675 
3676             if (length > 0)
3677                 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
3678 
3679             Status = STATUS_SUCCESS;
3680 
3681             break;
3682         }
3683 
3684         case FileAttributeTagInformation:
3685         {
3686             FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
3687 
3688             TRACE("FileAttributeTagInformation\n");
3689 
3690             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3691                 WARN("insufficient privileges\n");
3692                 Status = STATUS_ACCESS_DENIED;
3693                 goto exit;
3694             }
3695 
3696             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3697             Status = fill_in_file_attribute_information(ati, fcb, ccb, Irp, &length);
3698             ExReleaseResourceLite(&Vcb->tree_lock);
3699 
3700             break;
3701         }
3702 
3703         case FileBasicInformation:
3704         {
3705             FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
3706             INODE_ITEM* ii;
3707 
3708             TRACE("FileBasicInformation\n");
3709 
3710             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3711                 WARN("insufficient privileges\n");
3712                 Status = STATUS_ACCESS_DENIED;
3713                 goto exit;
3714             }
3715 
3716             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
3717                 WARN("overflow\n");
3718                 Status = STATUS_BUFFER_OVERFLOW;
3719                 goto exit;
3720             }
3721 
3722             if (fcb->ads) {
3723                 if (!fileref || !fileref->parent) {
3724                     ERR("no fileref for stream\n");
3725                     Status = STATUS_INTERNAL_ERROR;
3726                     goto exit;
3727                 }
3728 
3729                 ii = &fileref->parent->fcb->inode_item;
3730             } else
3731                 ii = &fcb->inode_item;
3732 
3733             Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
3734             break;
3735         }
3736 
3737         case FileCompressionInformation:
3738             FIXME("STUB: FileCompressionInformation\n");
3739             Status = STATUS_INVALID_PARAMETER;
3740             goto exit;
3741 
3742         case FileEaInformation:
3743         {
3744             FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
3745 
3746             TRACE("FileEaInformation\n");
3747 
3748             Status = fill_in_file_ea_information(eai, fcb, &length);
3749 
3750             break;
3751         }
3752 
3753         case FileInternalInformation:
3754         {
3755             FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3756 
3757             TRACE("FileInternalInformation\n");
3758 
3759             Status = fill_in_file_internal_information(fii, fcb, &length);
3760 
3761             break;
3762         }
3763 
3764         case FileNameInformation:
3765         {
3766             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3767 
3768             TRACE("FileNameInformation\n");
3769 
3770             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3771 
3772             break;
3773         }
3774 
3775         case FileNetworkOpenInformation:
3776         {
3777             FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
3778 
3779             TRACE("FileNetworkOpenInformation\n");
3780 
3781             if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3782                 WARN("insufficient privileges\n");
3783                 Status = STATUS_ACCESS_DENIED;
3784                 goto exit;
3785             }
3786 
3787             Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
3788 
3789             break;
3790         }
3791 
3792         case FilePositionInformation:
3793         {
3794             FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
3795 
3796             TRACE("FilePositionInformation\n");
3797 
3798             Status = fill_in_file_position_information(fpi, FileObject, &length);
3799 
3800             break;
3801         }
3802 
3803         case FileStandardInformation:
3804         {
3805             FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3806 
3807             TRACE("FileStandardInformation\n");
3808 
3809             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
3810                 WARN("overflow\n");
3811                 Status = STATUS_BUFFER_OVERFLOW;
3812                 goto exit;
3813             }
3814 
3815             Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
3816 
3817             break;
3818         }
3819 
3820         case FileStreamInformation:
3821         {
3822             FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3823 
3824             TRACE("FileStreamInformation\n");
3825 
3826             Status = fill_in_file_stream_information(fsi, fileref, &length);
3827 
3828             break;
3829         }
3830 
3831 #if (NTDDI_VERSION >= NTDDI_VISTA)
3832         case FileHardLinkInformation:
3833         {
3834             FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
3835 
3836             TRACE("FileHardLinkInformation\n");
3837 
3838             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3839             Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
3840             ExReleaseResourceLite(&Vcb->tree_lock);
3841 
3842             break;
3843         }
3844 
3845         case FileNormalizedNameInformation:
3846         {
3847             FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3848 
3849             TRACE("FileNormalizedNameInformation\n");
3850 
3851             Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3852 
3853             break;
3854         }
3855 #endif
3856 
3857 #if (NTDDI_VERSION >= NTDDI_WIN7)
3858         case FileStandardLinkInformation:
3859         {
3860             FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
3861 
3862             TRACE("FileStandardLinkInformation\n");
3863 
3864             Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
3865 
3866             break;
3867         }
3868 
3869         case FileRemoteProtocolInformation:
3870             TRACE("FileRemoteProtocolInformation\n");
3871             Status = STATUS_INVALID_PARAMETER;
3872             goto exit;
3873 #endif
3874 
3875 #if (NTDDI_VERSION >= NTDDI_WIN10)
3876 #ifndef _MSC_VER
3877 #pragma GCC diagnostic push
3878 #pragma GCC diagnostic ignored "-Wswitch"
3879 #endif
3880         case FileIdInformation:
3881         {
3882             FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3883 
3884             if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
3885                 WARN("overflow\n");
3886                 Status = STATUS_BUFFER_OVERFLOW;
3887                 goto exit;
3888             }
3889 
3890             TRACE("FileIdInformation\n");
3891 
3892             Status = fill_in_file_id_information(fii, fcb, &length);
3893 
3894             break;
3895         }
3896 #ifndef _MSC_VER
3897 #pragma GCC diagnostic pop
3898 #endif
3899 #endif
3900 
3901         default:
3902             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
3903             Status = STATUS_INVALID_PARAMETER;
3904             goto exit;
3905     }
3906 
3907     if (length < 0) {
3908         length = 0;
3909         Status = STATUS_BUFFER_OVERFLOW;
3910     }
3911 
3912     Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
3913 
3914 exit:
3915     TRACE("query_info returning %08x\n", Status);
3916 
3917     return Status;
3918 }
3919 
3920 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
3921 _Function_class_(DRIVER_DISPATCH)
3922 NTSTATUS NTAPI drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3923     PIO_STACK_LOCATION IrpSp;
3924     NTSTATUS Status;
3925     fcb* fcb;
3926     device_extension* Vcb = DeviceObject->DeviceExtension;
3927     BOOL top_level;
3928 
3929     FsRtlEnterFileSystem();
3930 
3931     top_level = is_top_level(Irp);
3932 
3933     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3934         Status = vol_query_information(DeviceObject, Irp);
3935         goto end;
3936     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3937         Status = STATUS_INVALID_PARAMETER;
3938         goto end;
3939     }
3940 
3941     Irp->IoStatus.Information = 0;
3942 
3943     TRACE("query information\n");
3944 
3945     IrpSp = IoGetCurrentIrpStackLocation(Irp);
3946 
3947     fcb = IrpSp->FileObject->FsContext;
3948     TRACE("fcb = %p\n", fcb);
3949     TRACE("fcb->subvol = %p\n", fcb->subvol);
3950 
3951     Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
3952 
3953 end:
3954     TRACE("returning %08x\n", Status);
3955 
3956     Irp->IoStatus.Status = Status;
3957 
3958     IoCompleteRequest( Irp, IO_NO_INCREMENT );
3959 
3960     if (top_level)
3961         IoSetTopLevelIrp(NULL);
3962 
3963     FsRtlExitFileSystem();
3964 
3965     return Status;
3966 }
3967 
3968 _Dispatch_type_(IRP_MJ_QUERY_EA)
3969 _Function_class_(DRIVER_DISPATCH)
3970 NTSTATUS NTAPI drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3971     NTSTATUS Status;
3972     BOOL top_level;
3973     device_extension* Vcb = DeviceObject->DeviceExtension;
3974     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3975     PFILE_OBJECT FileObject = IrpSp->FileObject;
3976     fcb* fcb;
3977     ccb* ccb;
3978     FILE_FULL_EA_INFORMATION* ffei;
3979     ULONG retlen = 0;
3980 
3981     FsRtlEnterFileSystem();
3982 
3983     TRACE("(%p, %p)\n", DeviceObject, Irp);
3984 
3985     top_level = is_top_level(Irp);
3986 
3987     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3988         Status = vol_query_ea(DeviceObject, Irp);
3989         goto end;
3990     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3991         Status = STATUS_INVALID_PARAMETER;
3992         goto end;
3993     }
3994 
3995     ffei = map_user_buffer(Irp, NormalPagePriority);
3996     if (!ffei) {
3997         ERR("could not get output buffer\n");
3998         Status = STATUS_INVALID_PARAMETER;
3999         goto end;
4000     }
4001 
4002     if (!FileObject) {
4003         ERR("no file object\n");
4004         Status = STATUS_INVALID_PARAMETER;
4005         goto end;
4006     }
4007 
4008     fcb = FileObject->FsContext;
4009 
4010     if (!fcb) {
4011         ERR("no fcb\n");
4012         Status = STATUS_INVALID_PARAMETER;
4013         goto end;
4014     }
4015 
4016     ccb = FileObject->FsContext2;
4017 
4018     if (!ccb) {
4019         ERR("no ccb\n");
4020         Status = STATUS_INVALID_PARAMETER;
4021         goto end;
4022     }
4023 
4024     if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
4025         WARN("insufficient privileges\n");
4026         Status = STATUS_ACCESS_DENIED;
4027         goto end;
4028     }
4029 
4030     if (fcb->ads)
4031         fcb = ccb->fileref->parent->fcb;
4032 
4033     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
4034 
4035     Status = STATUS_SUCCESS;
4036 
4037     if (fcb->ea_xattr.Length == 0)
4038         goto end2;
4039 
4040     if (IrpSp->Parameters.QueryEa.EaList) {
4041         FILE_FULL_EA_INFORMATION *ea, *out;
4042         FILE_GET_EA_INFORMATION* in;
4043 
4044         in = IrpSp->Parameters.QueryEa.EaList;
4045         do {
4046             STRING s;
4047 
4048             s.Length = s.MaximumLength = in->EaNameLength;
4049             s.Buffer = in->EaName;
4050 
4051             RtlUpperString(&s, &s);
4052 
4053             if (in->NextEntryOffset == 0)
4054                 break;
4055 
4056             in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4057         } while (TRUE);
4058 
4059         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4060         out = NULL;
4061 
4062         do {
4063             BOOL found = FALSE;
4064 
4065             in = IrpSp->Parameters.QueryEa.EaList;
4066             do {
4067                 if (in->EaNameLength == ea->EaNameLength &&
4068                     RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
4069                     found = TRUE;
4070                     break;
4071                 }
4072 
4073                 if (in->NextEntryOffset == 0)
4074                     break;
4075 
4076                 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
4077             } while (TRUE);
4078 
4079             if (found) {
4080                 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4081 
4082                 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4083                     Status = STATUS_BUFFER_OVERFLOW;
4084                     retlen = 0;
4085                     goto end2;
4086                 }
4087 
4088                 retlen += padding;
4089 
4090                 if (out) {
4091                     out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4092                     out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4093                 } else
4094                     out = ffei;
4095 
4096                 out->NextEntryOffset = 0;
4097                 out->Flags = ea->Flags;
4098                 out->EaNameLength = ea->EaNameLength;
4099                 out->EaValueLength = ea->EaValueLength;
4100                 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4101 
4102                 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4103 
4104                 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4105                     break;
4106             }
4107 
4108             if (ea->NextEntryOffset == 0)
4109                 break;
4110 
4111             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4112         } while (TRUE);
4113     } else {
4114         FILE_FULL_EA_INFORMATION *ea, *out;
4115         ULONG index;
4116 
4117         if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
4118             // The index is 1-based
4119             if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
4120                 Status = STATUS_NONEXISTENT_EA_ENTRY;
4121                 goto end2;
4122             } else
4123                 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
4124         } else if (IrpSp->Flags & SL_RESTART_SCAN)
4125             index = ccb->ea_index = 0;
4126         else
4127             index = ccb->ea_index;
4128 
4129         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4130 
4131         if (index > 0) {
4132             ULONG i;
4133 
4134             for (i = 0; i < index; i++) {
4135                 if (ea->NextEntryOffset == 0) // last item
4136                     goto end2;
4137 
4138                 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4139             }
4140         }
4141 
4142         out = NULL;
4143 
4144         do {
4145             UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4146 
4147             if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4148                 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
4149                 goto end2;
4150             }
4151 
4152             retlen += padding;
4153 
4154             if (out) {
4155                 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4156                 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4157             } else
4158                 out = ffei;
4159 
4160             out->NextEntryOffset = 0;
4161             out->Flags = ea->Flags;
4162             out->EaNameLength = ea->EaNameLength;
4163             out->EaValueLength = ea->EaValueLength;
4164             RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4165 
4166             retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4167 
4168             if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
4169                 ccb->ea_index++;
4170 
4171             if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4172                 break;
4173 
4174             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4175         } while (TRUE);
4176     }
4177 
4178 end2:
4179     ExReleaseResourceLite(fcb->Header.Resource);
4180 
4181 end:
4182     TRACE("returning %08x\n", Status);
4183 
4184     Irp->IoStatus.Status = Status;
4185     Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
4186 
4187     IoCompleteRequest( Irp, IO_NO_INCREMENT );
4188 
4189     if (top_level)
4190         IoSetTopLevelIrp(NULL);
4191 
4192     FsRtlExitFileSystem();
4193 
4194     return Status;
4195 }
4196 
4197 typedef struct {
4198     ANSI_STRING name;
4199     ANSI_STRING value;
4200     UCHAR flags;
4201     LIST_ENTRY list_entry;
4202 } ea_item;
4203 
4204 _Dispatch_type_(IRP_MJ_SET_EA)
4205 _Function_class_(DRIVER_DISPATCH)
4206 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4207     device_extension* Vcb = DeviceObject->DeviceExtension;
4208     NTSTATUS Status;
4209     BOOL top_level;
4210     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4211     PFILE_OBJECT FileObject = IrpSp->FileObject;
4212     fcb* fcb;
4213     ccb* ccb;
4214     file_ref* fileref;
4215     FILE_FULL_EA_INFORMATION* ffei;
4216     ULONG offset;
4217     LIST_ENTRY ealist;
4218     ea_item* item;
4219     FILE_FULL_EA_INFORMATION* ea;
4220     LIST_ENTRY* le;
4221     LARGE_INTEGER time;
4222     BTRFS_TIME now;
4223 
4224     FsRtlEnterFileSystem();
4225 
4226     TRACE("(%p, %p)\n", DeviceObject, Irp);
4227 
4228     top_level = is_top_level(Irp);
4229 
4230     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4231         Status = vol_set_ea(DeviceObject, Irp);
4232         goto end;
4233     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4234         Status = STATUS_INVALID_PARAMETER;
4235         goto end;
4236     }
4237 
4238     if (Vcb->readonly) {
4239         Status = STATUS_MEDIA_WRITE_PROTECTED;
4240         goto end;
4241     }
4242 
4243     ffei = map_user_buffer(Irp, NormalPagePriority);
4244     if (!ffei) {
4245         ERR("could not get output buffer\n");
4246         Status = STATUS_INVALID_PARAMETER;
4247         goto end;
4248     }
4249 
4250     Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
4251     if (!NT_SUCCESS(Status)) {
4252         ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
4253         goto end;
4254     }
4255 
4256     if (!FileObject) {
4257         ERR("no file object\n");
4258         Status = STATUS_INVALID_PARAMETER;
4259         goto end;
4260     }
4261 
4262     fcb = FileObject->FsContext;
4263 
4264     if (!fcb) {
4265         ERR("no fcb\n");
4266         Status = STATUS_INVALID_PARAMETER;
4267         goto end;
4268     }
4269 
4270     ccb = FileObject->FsContext2;
4271 
4272     if (!ccb) {
4273         ERR("no ccb\n");
4274         Status = STATUS_INVALID_PARAMETER;
4275         goto end;
4276     }
4277 
4278     if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
4279         WARN("insufficient privileges\n");
4280         Status = STATUS_ACCESS_DENIED;
4281         goto end;
4282     }
4283 
4284     if (fcb->ads) {
4285         fileref = ccb->fileref->parent;
4286         fcb = fileref->fcb;
4287     } else
4288         fileref = ccb->fileref;
4289 
4290     InitializeListHead(&ealist);
4291 
4292     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4293 
4294     if (fcb->ea_xattr.Length > 0) {
4295         ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4296 
4297         do {
4298             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4299             if (!item) {
4300                 ERR("out of memory\n");
4301                 Status = STATUS_INSUFFICIENT_RESOURCES;
4302                 goto end2;
4303             }
4304 
4305             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4306             item->name.Buffer = ea->EaName;
4307 
4308             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4309             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4310 
4311             item->flags = ea->Flags;
4312 
4313             InsertTailList(&ealist, &item->list_entry);
4314 
4315             if (ea->NextEntryOffset == 0)
4316                 break;
4317 
4318             ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4319         } while (TRUE);
4320     }
4321 
4322     ea = ffei;
4323 
4324     do {
4325         STRING s;
4326         BOOL found = FALSE;
4327 
4328         s.Length = s.MaximumLength = ea->EaNameLength;
4329         s.Buffer = ea->EaName;
4330 
4331         RtlUpperString(&s, &s);
4332 
4333         le = ealist.Flink;
4334         while (le != &ealist) {
4335             item = CONTAINING_RECORD(le, ea_item, list_entry);
4336 
4337             if (item->name.Length == s.Length &&
4338                 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
4339                 item->flags = ea->Flags;
4340                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4341                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4342                 found = TRUE;
4343                 break;
4344             }
4345 
4346             le = le->Flink;
4347         }
4348 
4349         if (!found) {
4350             item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4351             if (!item) {
4352                 ERR("out of memory\n");
4353                 Status = STATUS_INSUFFICIENT_RESOURCES;
4354                 goto end2;
4355             }
4356 
4357             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4358             item->name.Buffer = ea->EaName;
4359 
4360             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4361             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4362 
4363             item->flags = ea->Flags;
4364 
4365             InsertTailList(&ealist, &item->list_entry);
4366         }
4367 
4368         if (ea->NextEntryOffset == 0)
4369             break;
4370 
4371         ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4372     } while (TRUE);
4373 
4374     // remove entries with zero-length value
4375     le = ealist.Flink;
4376     while (le != &ealist) {
4377         LIST_ENTRY* le2 = le->Flink;
4378 
4379         item = CONTAINING_RECORD(le, ea_item, list_entry);
4380 
4381         if (item->value.Length == 0) {
4382             RemoveEntryList(&item->list_entry);
4383             ExFreePool(item);
4384         }
4385 
4386         le = le2;
4387     }
4388 
4389     if (IsListEmpty(&ealist)) {
4390         fcb->ealen = 0;
4391 
4392         if (fcb->ea_xattr.Buffer)
4393             ExFreePool(fcb->ea_xattr.Buffer);
4394 
4395         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4396         fcb->ea_xattr.Buffer = NULL;
4397     } else {
4398         UINT16 size = 0;
4399         char *buf, *oldbuf;
4400 
4401         le = ealist.Flink;
4402         while (le != &ealist) {
4403             item = CONTAINING_RECORD(le, ea_item, list_entry);
4404 
4405             if (size % 4 > 0)
4406                 size += 4 - (size % 4);
4407 
4408             size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
4409 
4410             le = le->Flink;
4411         }
4412 
4413         buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
4414         if (!buf) {
4415             ERR("out of memory\n");
4416             Status = STATUS_INSUFFICIENT_RESOURCES;
4417             goto end2;
4418         }
4419 
4420         oldbuf = fcb->ea_xattr.Buffer;
4421 
4422         fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
4423         fcb->ea_xattr.Buffer = buf;
4424 
4425         fcb->ealen = 4;
4426         ea = NULL;
4427 
4428         le = ealist.Flink;
4429         while (le != &ealist) {
4430             item = CONTAINING_RECORD(le, ea_item, list_entry);
4431 
4432             if (ea) {
4433                 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
4434 
4435                 if (ea->NextEntryOffset % 4 > 0)
4436                     ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
4437 
4438                 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4439             } else
4440                 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4441 
4442             ea->NextEntryOffset = 0;
4443             ea->Flags = item->flags;
4444             ea->EaNameLength = (UCHAR)item->name.Length;
4445             ea->EaValueLength = item->value.Length;
4446 
4447             RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
4448             ea->EaName[item->name.Length] = 0;
4449             RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
4450 
4451             fcb->ealen += 5 + item->name.Length + item->value.Length;
4452 
4453             le = le->Flink;
4454         }
4455 
4456         if (oldbuf)
4457             ExFreePool(oldbuf);
4458     }
4459 
4460     fcb->ea_changed = TRUE;
4461 
4462     KeQuerySystemTime(&time);
4463     win_time_to_unix(time, &now);
4464 
4465     fcb->inode_item.transid = Vcb->superblock.generation;
4466     fcb->inode_item.sequence++;
4467 
4468     if (!ccb->user_set_change_time)
4469         fcb->inode_item.st_ctime = now;
4470 
4471     fcb->inode_item_changed = TRUE;
4472     mark_fcb_dirty(fcb);
4473 
4474     send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
4475 
4476     Status = STATUS_SUCCESS;
4477 
4478 end2:
4479     ExReleaseResourceLite(fcb->Header.Resource);
4480 
4481     while (!IsListEmpty(&ealist)) {
4482         le = RemoveHeadList(&ealist);
4483 
4484         item = CONTAINING_RECORD(le, ea_item, list_entry);
4485 
4486         ExFreePool(item);
4487     }
4488 
4489 end:
4490     TRACE("returning %08x\n", Status);
4491 
4492     Irp->IoStatus.Status = Status;
4493     Irp->IoStatus.Information = 0;
4494 
4495     IoCompleteRequest(Irp, IO_NO_INCREMENT);
4496 
4497     if (top_level)
4498         IoSetTopLevelIrp(NULL);
4499 
4500     FsRtlExitFileSystem();
4501 
4502     return Status;
4503 }
4504