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