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