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