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