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