[BTRFS] Upgrade to 1.0.2
[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 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;