[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / create.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 #ifndef __REACTOS__
19 #include <sys/stat.h>
20 #endif /* __REACTOS__ */
21 #include "btrfs_drv.h"
22 #ifndef __REACTOS__
23 #include <winioctl.h>
24 #endif
25
26 extern PDEVICE_OBJECT devobj;
27
28 static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
29 KEY searchkey;
30 traverse_ptr tp;
31 NTSTATUS Status;
32 UINT64 index;
33
34 searchkey.obj_id = inode;
35 searchkey.obj_type = TYPE_INODE_REF;
36 searchkey.offset = parinode;
37
38 Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
39 if (!NT_SUCCESS(Status)) {
40 ERR("error - find_item returned %08x\n", Status);
41 return Status;
42 }
43
44 if (!keycmp(&tp.item->key, &searchkey)) {
45 INODE_REF* ir;
46 ULONG len;
47
48 index = 0;
49
50 ir = (INODE_REF*)tp.item->data;
51 len = tp.item->size;
52
53 do {
54 ULONG itemlen;
55
56 if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
57 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
58 break;
59 }
60
61 itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
62
63 if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
64 index = ir->index;
65 break;
66 }
67
68 if (len > itemlen) {
69 len -= itemlen;
70 ir = (INODE_REF*)&ir->name[ir->n];
71 } else
72 break;
73 } while (len > 0);
74
75 if (index == 0)
76 return STATUS_NOT_FOUND;
77
78 *pindex = index;
79
80 return STATUS_SUCCESS;
81 } else
82 return STATUS_NOT_FOUND;
83 }
84
85 static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
86 KEY searchkey;
87 traverse_ptr tp;
88 NTSTATUS Status;
89 UINT64 index;
90
91 searchkey.obj_id = inode;
92 searchkey.obj_type = TYPE_INODE_EXTREF;
93 searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
94
95 Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
96 if (!NT_SUCCESS(Status)) {
97 ERR("error - find_item returned %08x\n", Status);
98 return Status;
99 }
100
101 if (!keycmp(&tp.item->key, &searchkey)) {
102 INODE_EXTREF* ier;
103 ULONG len;
104
105 index = 0;
106
107 ier = (INODE_EXTREF*)tp.item->data;
108 len = tp.item->size;
109
110 do {
111 ULONG itemlen;
112
113 if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
114 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
115 break;
116 }
117
118 itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
119
120 if (ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
121 index = ier->index;
122 break;
123 }
124
125 if (len > itemlen) {
126 len -= itemlen;
127 ier = (INODE_EXTREF*)&ier->name[ier->n];
128 } else
129 break;
130 } while (len > 0);
131
132 if (index == 0)
133 return STATUS_NOT_FOUND;
134
135 *pindex = index;
136
137 return STATUS_SUCCESS;
138 } else
139 return STATUS_NOT_FOUND;
140 }
141
142 static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
143 KEY searchkey;
144 traverse_ptr tp;
145 NTSTATUS Status;
146 ROOT_REF* rr;
147
148 searchkey.obj_id = r->id;
149 searchkey.obj_type = TYPE_ROOT_REF;
150 searchkey.offset = subvolid;
151
152 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
153 if (!NT_SUCCESS(Status)) {
154 ERR("error - find_item returned %08x\n", Status);
155 return Status;
156 }
157
158 if (keycmp(&tp.item->key, &searchkey)) {
159 ERR("couldn't find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
160 return STATUS_INTERNAL_ERROR;
161 }
162
163 if (tp.item->size < sizeof(ROOT_REF)) {
164 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,
165 tp.item->size, sizeof(ROOT_REF));
166 return STATUS_INTERNAL_ERROR;
167 }
168
169 rr = (ROOT_REF*)tp.item->data;
170
171 if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) {
172 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
173 tp.item->size, sizeof(ROOT_REF) - 1 + rr->n);
174 return STATUS_INTERNAL_ERROR;
175 }
176
177 if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(utf8->Buffer, rr->name, rr->n) == rr->n) {
178 *pindex = rr->index;
179 return STATUS_SUCCESS;
180 } else
181 return STATUS_NOT_FOUND;
182 }
183
184 static NTSTATUS load_index_list(fcb* fcb) {
185 KEY searchkey;
186 traverse_ptr tp, next_tp;
187 NTSTATUS Status;
188 BOOL b;
189
190 searchkey.obj_id = fcb->inode;
191 searchkey.obj_type = TYPE_DIR_INDEX;
192 searchkey.offset = 2;
193
194 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
195 if (!NT_SUCCESS(Status)) {
196 ERR("error - find_item returned %08x\n", Status);
197 return Status;
198 }
199
200 if (keycmp(&tp.item->key, &searchkey) == -1) {
201 if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) {
202 tp = next_tp;
203
204 TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
205 }
206 }
207
208 if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
209 Status = STATUS_SUCCESS;
210 goto end;
211 }
212
213 do {
214 DIR_ITEM* di;
215
216 TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
217 di = (DIR_ITEM*)tp.item->data;
218
219 if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
220 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
221 } else {
222 index_entry* ie;
223 ULONG stringlen;
224 UNICODE_STRING us;
225 LIST_ENTRY* le;
226 BOOL inserted;
227
228 ie = ExAllocatePoolWithTag(PagedPool, sizeof(index_entry), ALLOC_TAG);
229 if (!ie) {
230 ERR("out of memory\n");
231 Status = STATUS_INSUFFICIENT_RESOURCES;
232 goto end;
233 }
234
235 ie->utf8.Length = ie->utf8.MaximumLength = di->n;
236
237 if (di->n > 0) {
238 ie->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, ie->utf8.MaximumLength, ALLOC_TAG);
239 if (!ie->utf8.Buffer) {
240 ERR("out of memory\n");
241 ExFreePool(ie);
242 Status = STATUS_INSUFFICIENT_RESOURCES;
243 goto end;
244 }
245
246 RtlCopyMemory(ie->utf8.Buffer, di->name, di->n);
247 } else
248 ie->utf8.Buffer = NULL;
249
250 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
251 if (!NT_SUCCESS(Status)) {
252 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
253 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
254 ExFreePool(ie);
255 goto nextitem;
256 }
257
258 if (stringlen == 0) {
259 ERR("UTF8 length was 0\n");
260 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
261 ExFreePool(ie);
262 goto nextitem;
263 }
264
265 us.Length = us.MaximumLength = stringlen;
266 us.Buffer = ExAllocatePoolWithTag(PagedPool, us.MaximumLength, ALLOC_TAG);
267
268 if (!us.Buffer) {
269 ERR("out of memory\n");
270 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
271 ExFreePool(ie);
272 return STATUS_INSUFFICIENT_RESOURCES;
273 }
274
275 Status = RtlUTF8ToUnicodeN(us.Buffer, stringlen, &stringlen, di->name, di->n);
276 if (!NT_SUCCESS(Status)) {
277 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
278 ExFreePool(us.Buffer);
279 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
280 ExFreePool(ie);
281 goto nextitem;
282 }
283
284 Status = RtlUpcaseUnicodeString(&ie->filepart_uc, &us, TRUE);
285 if (!NT_SUCCESS(Status)) {
286 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
287 ExFreePool(us.Buffer);
288 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
289 ExFreePool(ie);
290 goto nextitem;
291 }
292
293 ie->key = di->key;
294 ie->type = di->type;
295 ie->index = tp.item->key.offset;
296
297 ie->hash = calc_crc32c(0xfffffffe, (UINT8*)ie->filepart_uc.Buffer, (ULONG)ie->filepart_uc.Length);
298 inserted = FALSE;
299
300 le = fcb->index_list.Flink;
301 while (le != &fcb->index_list) {
302 index_entry* ie2 = CONTAINING_RECORD(le, index_entry, list_entry);
303
304 if (ie2->hash >= ie->hash) {
305 InsertHeadList(le->Blink, &ie->list_entry);
306 inserted = TRUE;
307 break;
308 }
309
310 le = le->Flink;
311 }
312
313 if (!inserted)
314 InsertTailList(&fcb->index_list, &ie->list_entry);
315 }
316
317 nextitem:
318 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
319
320 if (b) {
321 tp = next_tp;
322
323 b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX;
324 }
325 } while (b);
326
327 Status = STATUS_SUCCESS;
328
329 end:
330 if (!NT_SUCCESS(Status)) {
331 while (!IsListEmpty(&fcb->index_list)) {
332 LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
333 index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
334
335 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
336 if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
337 ExFreePool(ie);
338 }
339 } else
340 mark_fcb_dirty(fcb);
341
342 return Status;
343 }
344
345 static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
346 UINT64* pindex, PANSI_STRING utf8) {
347 LIST_ENTRY* le;
348 NTSTATUS Status;
349 UNICODE_STRING us;
350 UINT32 hash;
351
352 Status = RtlUpcaseUnicodeString(&us, filename, TRUE);
353 if (!NT_SUCCESS(Status)) {
354 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
355 return Status;
356 }
357
358 hash = calc_crc32c(0xfffffffe, (UINT8*)us.Buffer, (ULONG)us.Length);
359
360 ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
361
362 if (!fr->fcb->index_loaded) {
363 Status = load_index_list(fr->fcb);
364 if (!NT_SUCCESS(Status)) {
365 ERR("load_index_list returned %08x\n", Status);
366 goto end;
367 }
368
369 fr->fcb->index_loaded = TRUE;
370 }
371
372 ExConvertExclusiveToSharedLite(&fr->fcb->nonpaged->index_lock);
373
374 le = fr->fcb->index_list.Flink;
375 while (le != &fr->fcb->index_list) {
376 index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
377
378 if (ie->hash == hash && ie->filepart_uc.Length == us.Length && RtlCompareMemory(ie->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) {
379 LIST_ENTRY* le;
380 BOOL ignore_entry = FALSE;
381
382 ExAcquireResourceSharedLite(&fr->nonpaged->children_lock, TRUE);
383
384 le = fr->children.Flink;
385 while (le != &fr->children) {
386 file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
387
388 if (fr2->index == ie->index) {
389 if (fr2->deleted || fr2->filepart_uc.Length != us.Length ||
390 RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) != us.Length) {
391 ignore_entry = TRUE;
392 break;
393 }
394 break;
395 } else if (fr2->index > ie->index)
396 break;
397
398 le = le->Flink;
399 }
400
401 ExReleaseResourceLite(&fr->nonpaged->children_lock);
402
403 if (ignore_entry)
404 goto nextitem;
405
406 if (ie->key.obj_type == TYPE_ROOT_ITEM) {
407 if (subvol) {
408 *subvol = NULL;
409
410 le = fr->fcb->Vcb->roots.Flink;
411 while (le != &fr->fcb->Vcb->roots) {
412 root* r2 = CONTAINING_RECORD(le, root, list_entry);
413
414 if (r2->id == ie->key.obj_id) {
415 *subvol = r2;
416 break;
417 }
418
419 le = le->Flink;
420 }
421 }
422
423 if (inode)
424 *inode = SUBVOL_ROOT_INODE;
425
426 if (type)
427 *type = BTRFS_TYPE_DIRECTORY;
428 } else {
429 if (subvol)
430 *subvol = fr->fcb->subvol;
431
432 if (inode)
433 *inode = ie->key.obj_id;
434
435 if (type)
436 *type = ie->type;
437 }
438
439 if (utf8) {
440 utf8->MaximumLength = utf8->Length = ie->utf8.Length;
441 utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
442 if (!utf8->Buffer) {
443 ERR("out of memory\n");
444 Status = STATUS_INSUFFICIENT_RESOURCES;
445 goto end;
446 }
447
448 RtlCopyMemory(utf8->Buffer, ie->utf8.Buffer, ie->utf8.Length);
449 }
450
451 if (pindex)
452 *pindex = ie->index;
453
454 Status = STATUS_SUCCESS;
455 goto end;
456 } else if (ie->hash > hash) {
457 Status = STATUS_OBJECT_NAME_NOT_FOUND;
458 goto end;
459 }
460
461 nextitem:
462 le = le->Flink;
463 }
464
465 Status = STATUS_OBJECT_NAME_NOT_FOUND;
466
467 end:
468 ExReleaseResourceLite(&fr->fcb->nonpaged->index_lock);
469
470 ExFreePool(us.Buffer);
471
472 return Status;
473 }
474
475 NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
476 root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) {
477 DIR_ITEM* di;
478 KEY searchkey;
479 traverse_ptr tp;
480 NTSTATUS Status;
481 ULONG stringlen;
482
483 TRACE("(%p, %.*S, %08x, (%llx, %llx), %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32,
484 fr->fcb->subvol->id, fr->fcb->inode, subvol, inode, type);
485
486 searchkey.obj_id = fr->fcb->inode;
487 searchkey.obj_type = TYPE_DIR_ITEM;
488 searchkey.offset = crc32;
489
490 Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE);
491 if (!NT_SUCCESS(Status)) {
492 ERR("error - find_item returned %08x\n", Status);
493 return Status;
494 }
495
496 TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
497
498 if (!keycmp(&searchkey, &tp.item->key)) {
499 UINT32 size = tp.item->size;
500
501 // found by hash
502
503 if (tp.item->size < sizeof(DIR_ITEM)) {
504 WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", fr->fcb->subvol->id, tp.item->key.obj_id, tp.item->key.obj_type,
505 tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
506 } else {
507 di = (DIR_ITEM*)tp.item->data;
508
509 while (size > 0) {
510 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
511 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
512 break;
513 }
514
515 size -= sizeof(DIR_ITEM) - sizeof(char);
516 size -= di->n;
517 size -= di->m;
518
519 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
520 if (!NT_SUCCESS(Status)) {
521 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
522 } else {
523 WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
524 UNICODE_STRING us;
525
526 if (!utf16) {
527 ERR("out of memory\n");
528 return STATUS_INSUFFICIENT_RESOURCES;
529 }
530
531 Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
532
533 if (!NT_SUCCESS(Status)) {
534 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
535 } else {
536 us.Buffer = utf16;
537 us.Length = us.MaximumLength = (USHORT)stringlen;
538
539 if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
540 UINT64 index;
541
542 if (di->key.obj_type == TYPE_ROOT_ITEM) {
543 LIST_ENTRY* le = Vcb->roots.Flink;
544
545 if (subvol) {
546 *subvol = NULL;
547
548 while (le != &Vcb->roots) {
549 root* r2 = CONTAINING_RECORD(le, root, list_entry);
550
551 if (r2->id == di->key.obj_id) {
552 *subvol = r2;
553 break;
554 }
555
556 le = le->Flink;
557 }
558 }
559
560 if (inode)
561 *inode = SUBVOL_ROOT_INODE;
562
563 if (type)
564 *type = BTRFS_TYPE_DIRECTORY;
565 } else {
566 if (subvol)
567 *subvol = fr->fcb->subvol;
568
569 if (inode)
570 *inode = di->key.obj_id;
571
572 if (type)
573 *type = di->type;
574 }
575
576 if (utf8) {
577 utf8->MaximumLength = di->n;
578 utf8->Length = utf8->MaximumLength;
579 utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
580 if (!utf8->Buffer) {
581 ERR("out of memory\n");
582 ExFreePool(utf16);
583 return STATUS_INSUFFICIENT_RESOURCES;
584 }
585
586 RtlCopyMemory(utf8->Buffer, di->name, di->n);
587 }
588
589 ExFreePool(utf16);
590
591 index = 0;
592
593 if (fr->fcb->subvol != Vcb->root_root) {
594 if (di->key.obj_type == TYPE_ROOT_ITEM) {
595 Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
596 if (!NT_SUCCESS(Status)) {
597 ERR("find_subvol_dir_index returned %08x\n", Status);
598 return Status;
599 }
600 } else {
601 Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
602 if (!NT_SUCCESS(Status)) {
603 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
604 Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
605
606 if (!NT_SUCCESS(Status)) {
607 ERR("find_file_dir_index_extref returned %08x\n", Status);
608 return Status;
609 }
610 } else {
611 ERR("find_file_dir_index returned %08x\n", Status);
612 return Status;
613 }
614 }
615 }
616 }
617
618 if (index != 0) {
619 LIST_ENTRY* le = fr->children.Flink;
620
621 while (le != &fr->children) {
622 file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
623
624 if (fr2->index == index) {
625 if (fr2->deleted || !FsRtlAreNamesEqual(&fr2->filepart, filename, TRUE, NULL)) {
626 goto byindex;
627 }
628 break;
629 } else if (fr2->index > index)
630 break;
631
632 le = le->Flink;
633 }
634 }
635
636 // TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
637
638 if (pindex)
639 *pindex = index;
640
641 return STATUS_SUCCESS;
642 }
643 }
644
645 ExFreePool(utf16);
646 }
647
648 di = (DIR_ITEM*)&di->name[di->n + di->m];
649 }
650 }
651 }
652
653 byindex:
654 Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8);
655 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
656 ERR("find_file_in_dir_index returned %08x\n", Status);
657 return Status;
658 }
659
660 return Status;
661 }
662
663 fcb* create_fcb() {
664 fcb* fcb;
665
666 fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
667 if (!fcb) {
668 ERR("out of memory\n");
669 return NULL;
670 }
671
672 #ifdef DEBUG_FCB_REFCOUNTS
673 WARN("allocating fcb %p\n", fcb);
674 #endif
675 RtlZeroMemory(fcb, sizeof(struct _fcb));
676
677 fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
678 fcb->Header.NodeByteSize = sizeof(struct _fcb);
679
680 fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
681 if (!fcb->nonpaged) {
682 ERR("out of memory\n");
683 ExFreePool(fcb);
684 return NULL;
685 }
686 RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
687
688 ExInitializeResourceLite(&fcb->nonpaged->paging_resource);
689 fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
690
691 ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
692 FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
693
694 fcb->refcount = 1;
695 #ifdef DEBUG_FCB_REFCOUNTS
696 WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
697 #endif
698
699 ExInitializeResourceLite(&fcb->nonpaged->resource);
700 fcb->Header.Resource = &fcb->nonpaged->resource;
701
702 ExInitializeResourceLite(&fcb->nonpaged->index_lock);
703
704 FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
705
706 InitializeListHead(&fcb->extents);
707 InitializeListHead(&fcb->index_list);
708 InitializeListHead(&fcb->hardlinks);
709
710 return fcb;
711 }
712
713 file_ref* create_fileref() {
714 file_ref* fr;
715
716 fr = ExAllocatePoolWithTag(PagedPool, sizeof(file_ref), ALLOC_TAG);
717 if (!fr) {
718 ERR("out of memory\n");
719 return NULL;
720 }
721
722 RtlZeroMemory(fr, sizeof(file_ref));
723
724 fr->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(file_ref_nonpaged), ALLOC_TAG);
725 if (!fr->nonpaged) {
726 ERR("out of memory\n");
727 ExFreePool(fr);
728 return NULL;
729 }
730
731 fr->refcount = 1;
732
733 #ifdef DEBUG_FCB_REFCOUNTS
734 WARN("fileref %p: refcount now 1\n", fr);
735 #endif
736
737 InitializeListHead(&fr->children);
738
739 ExInitializeResourceLite(&fr->nonpaged->children_lock);
740
741 return fr;
742 }
743
744 NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
745 root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) {
746 char* fn;
747 UINT32 crc32;
748 ULONG utf8len;
749 NTSTATUS Status;
750
751 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
752 if (!NT_SUCCESS(Status)) {
753 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
754 return Status;
755 }
756
757 fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
758 if (!fn) {
759 ERR("out of memory\n");
760 return STATUS_INSUFFICIENT_RESOURCES;
761 }
762
763 Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
764 if (!NT_SUCCESS(Status)) {
765 ExFreePool(fn);
766 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
767 return Status;
768 }
769
770 TRACE("%.*s\n", utf8len, fn);
771
772 crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
773 TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
774
775 return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8);
776 }
777
778 static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) {
779 NTSTATUS Status;
780 ULONG utf8len;
781 char* utf8;
782 UINT32 crc32;
783 KEY searchkey;
784 traverse_ptr tp, next_tp;
785 BOOL success = FALSE, b;
786
787 static char xapref[] = "user.";
788 ULONG xapreflen = strlen(xapref);
789
790 TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
791
792 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
793 if (!NT_SUCCESS(Status)) {
794 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
795 return FALSE;
796 }
797
798 TRACE("utf8len = %u\n", utf8len);
799
800 utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
801 if (!utf8) {
802 ERR("out of memory\n");
803 goto end;
804 }
805
806 RtlCopyMemory(utf8, xapref, xapreflen);
807
808 Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
809 if (!NT_SUCCESS(Status)) {
810 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
811 goto end;
812 }
813
814 utf8len += xapreflen;
815 utf8[utf8len] = 0;
816
817 TRACE("utf8 = %s\n", utf8);
818
819 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
820 TRACE("crc32 = %08x\n", crc32);
821
822 searchkey.obj_id = fcb->inode;
823 searchkey.obj_type = TYPE_XATTR_ITEM;
824 searchkey.offset = crc32;
825
826 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
827 if (!NT_SUCCESS(Status)) {
828 ERR("error - find_item returned %08x\n", Status);
829 goto end;
830 }
831
832 if (!keycmp(&tp.item->key, &searchkey)) {
833 if (tp.item->size < sizeof(DIR_ITEM)) {
834 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));
835 } else {
836 ULONG len = tp.item->size, xasize;
837 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
838
839 TRACE("found match on hash\n");
840
841 while (len > 0) {
842 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
843 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
844 break;
845 }
846
847 if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
848 TRACE("found exact match for %s\n", utf8);
849
850 *hash = tp.item->key.offset;
851
852 xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
853 if (!xattr->Buffer) {
854 ERR("out of memory\n");
855 goto end;
856 }
857
858 xattr->Length = xattr->MaximumLength = di->n;
859 RtlCopyMemory(xattr->Buffer, di->name, di->n);
860 xattr->Buffer[di->n] = 0;
861
862 success = TRUE;
863 goto end;
864 }
865
866 xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
867
868 if (len > xasize) {
869 len -= xasize;
870 di = (DIR_ITEM*)&di->name[di->m + di->n];
871 } else
872 break;
873 }
874 }
875 }
876
877 searchkey.offset = 0;
878
879 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
880 if (!NT_SUCCESS(Status)) {
881 ERR("error - find_item returned %08x\n", Status);
882 goto end;
883 }
884
885 do {
886 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
887 if (tp.item->size < sizeof(DIR_ITEM)) {
888 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));
889 } else {
890 ULONG len = tp.item->size, xasize;
891 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
892 ULONG utf16len;
893
894 TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
895
896 while (len > 0) {
897 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
898 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
899 break;
900 }
901
902 if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
903 TRACE("found potential xattr %.*s\n", di->n, di->name);
904 }
905
906 Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
907 if (!NT_SUCCESS(Status)) {
908 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
909 } else {
910 WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
911 if (!utf16) {
912 ERR("out of memory\n");
913 goto end;
914 }
915
916 Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
917
918 if (!NT_SUCCESS(Status)) {
919 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
920 } else {
921 UNICODE_STRING us;
922
923 us.Buffer = utf16;
924 us.Length = us.MaximumLength = (USHORT)utf16len;
925
926 if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
927 TRACE("found case-insensitive match for %s\n", utf8);
928
929 *newstreamname = us;
930 *hash = tp.item->key.offset;
931
932 xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
933 if (!xattr->Buffer) {
934 ERR("out of memory\n");
935 ExFreePool(utf16);
936 goto end;
937 }
938
939 xattr->Length = xattr->MaximumLength = di->n;
940 RtlCopyMemory(xattr->Buffer, di->name, di->n);
941 xattr->Buffer[di->n] = 0;
942
943 success = TRUE;
944 goto end;
945 }
946 }
947
948 ExFreePool(utf16);
949 }
950
951 xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
952
953 if (len > xasize) {
954 len -= xasize;
955 di = (DIR_ITEM*)&di->name[di->m + di->n];
956 } else
957 break;
958 }
959 }
960 }
961
962 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
963 if (b) {
964 tp = next_tp;
965
966 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
967 break;
968 }
969 } while (b);
970
971 end:
972 ExFreePool(utf8);
973
974 return success;
975 }
976
977 static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
978 ULONG len, i, j, np;
979 BOOL has_stream;
980 UNICODE_STRING* ps;
981 WCHAR* buf;
982
983 np = 1;
984
985 len = path->Length / sizeof(WCHAR);
986 if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
987 len--;
988
989 has_stream = FALSE;
990 for (i = 0; i < len; i++) {
991 if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
992 np++;
993 has_stream = FALSE;
994 } else if (path->Buffer[i] == ':') {
995 has_stream = TRUE;
996 }
997 }
998
999 if (has_stream)
1000 np++;
1001
1002 ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
1003 if (!ps) {
1004 ERR("out of memory\n");
1005 return STATUS_INSUFFICIENT_RESOURCES;
1006 }
1007
1008 RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
1009
1010 buf = path->Buffer;
1011
1012 j = 0;
1013 for (i = 0; i < len; i++) {
1014 if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
1015 ps[j].Buffer = buf;
1016 ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
1017 ps[j].MaximumLength = ps[j].Length;
1018
1019 buf = &path->Buffer[i+1];
1020 j++;
1021 }
1022 }
1023
1024 ps[j].Buffer = buf;
1025 ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
1026 ps[j].MaximumLength = ps[j].Length;
1027
1028 if (has_stream) {
1029 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
1030 UNICODE_STRING dsus;
1031
1032 dsus.Buffer = datasuf;
1033 dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
1034
1035 for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
1036 if (ps[j].Buffer[i] == ':') {
1037 ps[j+1].Buffer = &ps[j].Buffer[i+1];
1038 ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
1039
1040 ps[j].Length = i * sizeof(WCHAR);
1041 ps[j].MaximumLength = ps[j].Length;
1042
1043 j++;
1044
1045 break;
1046 }
1047 }
1048
1049 // FIXME - should comparison be case-insensitive?
1050 // remove :$DATA suffix
1051 if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
1052 ps[j].Length -= dsus.Length;
1053
1054 if (ps[j].Length == 0) {
1055 np--;
1056 has_stream = FALSE;
1057 }
1058 }
1059
1060 // if path is just stream name, remove first empty item
1061 if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
1062 ps[0] = ps[1];
1063 np--;
1064 }
1065
1066 // for (i = 0; i < np; i++) {
1067 // ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
1068 // }
1069
1070 *num_parts = np;
1071 *parts = ps;
1072 *stream = has_stream;
1073
1074 return STATUS_SUCCESS;
1075 }
1076
1077 // #ifdef DEBUG_FCB_REFCOUNTS
1078 // static void print_fcbs(device_extension* Vcb) {
1079 // fcb* fcb = Vcb->fcbs;
1080 //
1081 // while (fcb) {
1082 // ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
1083 //
1084 // fcb = fcb->next;
1085 // }
1086 // }
1087 // #endif
1088
1089 static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
1090 LIST_ENTRY* le;
1091 file_ref *c, *deleted = NULL;
1092 NTSTATUS Status;
1093 UNICODE_STRING ucus;
1094 #ifdef DEBUG_FCB_REFCOUNTS
1095 ULONG rc;
1096 #endif
1097
1098 Status = RtlUpcaseUnicodeString(&ucus, name, TRUE);
1099 if (!NT_SUCCESS(Status)) {
1100 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1101 return NULL;
1102 }
1103
1104 le = dir->children.Flink;
1105 while (le != &dir->children) {
1106 c = CONTAINING_RECORD(le, file_ref, list_entry);
1107
1108 if (c->refcount > 0 && c->filepart_uc.Length == ucus.Length &&
1109 RtlCompareMemory(c->filepart_uc.Buffer, ucus.Buffer, ucus.Length) == ucus.Length) {
1110 if (c->deleted) {
1111 deleted = c;
1112 } else {
1113 #ifdef DEBUG_FCB_REFCOUNTS
1114 rc = InterlockedIncrement(&c->refcount);
1115 WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
1116 #else
1117 InterlockedIncrement(&c->refcount);
1118 #endif
1119 ExFreePool(ucus.Buffer);
1120
1121 return c;
1122 }
1123 }
1124
1125 le = le->Flink;
1126 }
1127
1128 if (deleted)
1129 increase_fileref_refcount(deleted);
1130
1131 ExFreePool(ucus.Buffer);
1132
1133 return deleted;
1134 }
1135
1136 static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) {
1137 KEY searchkey;
1138 traverse_ptr tp;
1139 NTSTATUS Status;
1140 EXTENT_ITEM* ei;
1141
1142 searchkey.obj_id = address;
1143 searchkey.obj_type = TYPE_EXTENT_ITEM;
1144 searchkey.offset = size;
1145
1146 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
1147 if (!NT_SUCCESS(Status)) {
1148 ERR("error - find_item returned %08x\n", Status);
1149 return 0;
1150 }
1151
1152 if (keycmp(&searchkey, &tp.item->key)) {
1153 ERR("couldn't find (%llx,%x,%llx) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
1154 return 0;
1155 }
1156
1157 if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
1158 EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
1159
1160 return eiv0->refcount;
1161 } else if (tp.item->size < sizeof(EXTENT_ITEM)) {
1162 ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type,
1163 tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
1164 return 0;
1165 }
1166
1167 ei = (EXTENT_ITEM*)tp.item->data;
1168
1169 return ei->refcount;
1170 }
1171
1172 NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
1173 KEY searchkey;
1174 traverse_ptr tp;
1175 NTSTATUS Status;
1176 fcb* fcb;
1177 BOOL b;
1178
1179 if (!IsListEmpty(&subvol->fcbs)) {
1180 LIST_ENTRY* le = subvol->fcbs.Flink;
1181
1182 while (le != &subvol->fcbs) {
1183 fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
1184
1185 if (fcb->inode == inode && !fcb->ads) {
1186 #ifdef DEBUG_FCB_REFCOUNTS
1187 LONG rc = InterlockedIncrement(&fcb->refcount);
1188
1189 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
1190 #else
1191 InterlockedIncrement(&fcb->refcount);
1192 #endif
1193
1194 *pfcb = fcb;
1195 return STATUS_SUCCESS;
1196 }
1197
1198 le = le->Flink;
1199 }
1200 }
1201
1202 fcb = create_fcb();
1203 if (!fcb) {
1204 ERR("out of memory\n");
1205 return STATUS_INSUFFICIENT_RESOURCES;
1206 }
1207
1208 fcb->Vcb = Vcb;
1209
1210 fcb->subvol = subvol;
1211 fcb->inode = inode;
1212 fcb->type = type;
1213
1214 searchkey.obj_id = inode;
1215 searchkey.obj_type = TYPE_INODE_ITEM;
1216 searchkey.offset = 0xffffffffffffffff;
1217
1218 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
1219 if (!NT_SUCCESS(Status)) {
1220 ERR("error - find_item returned %08x\n", Status);
1221 free_fcb(fcb);
1222 return Status;
1223 }
1224
1225 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1226 WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
1227 free_fcb(fcb);
1228 return STATUS_INVALID_PARAMETER;
1229 }
1230
1231 if (tp.item->size > 0)
1232 RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
1233
1234 if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
1235 if (fcb->inode_item.st_mode & __S_IFDIR)
1236 fcb->type = BTRFS_TYPE_DIRECTORY;
1237 else if (fcb->inode_item.st_mode & __S_IFCHR)
1238 fcb->type = BTRFS_TYPE_CHARDEV;
1239 else if (fcb->inode_item.st_mode & __S_IFBLK)
1240 fcb->type = BTRFS_TYPE_BLOCKDEV;
1241 else if (fcb->inode_item.st_mode & __S_IFIFO)
1242 fcb->type = BTRFS_TYPE_FIFO;
1243 else if (fcb->inode_item.st_mode & __S_IFLNK)
1244 fcb->type = BTRFS_TYPE_SYMLINK;
1245 else if (fcb->inode_item.st_mode & __S_IFSOCK)
1246 fcb->type = BTRFS_TYPE_SOCKET;
1247 else
1248 fcb->type = BTRFS_TYPE_FILE;
1249 }
1250
1251 fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE);
1252
1253 fcb_get_sd(fcb, parent);
1254
1255 if (fcb->type == BTRFS_TYPE_DIRECTORY) {
1256 UINT8* xattrdata;
1257 UINT16 xattrlen;
1258
1259 if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) {
1260 fcb->reparse_xattr.Buffer = (char*)xattrdata;
1261 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
1262 }
1263 }
1264
1265 InsertTailList(&subvol->fcbs, &fcb->list_entry);
1266 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1267
1268 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1269
1270 if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
1271 fcb->Header.AllocationSize.QuadPart = 0;
1272 fcb->Header.FileSize.QuadPart = 0;
1273 fcb->Header.ValidDataLength.QuadPart = 0;
1274 } else {
1275 EXTENT_DATA* ed = NULL;
1276 traverse_ptr next_tp;
1277
1278 searchkey.obj_id = fcb->inode;
1279 searchkey.obj_type = TYPE_EXTENT_DATA;
1280 searchkey.offset = 0;
1281
1282 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1283 if (!NT_SUCCESS(Status)) {
1284 ERR("error - find_item returned %08x\n", Status);
1285 free_fcb(fcb);
1286 return Status;
1287 }
1288
1289 do {
1290 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1291 extent* ext;
1292 BOOL unique = FALSE;
1293
1294 ed = (EXTENT_DATA*)tp.item->data;
1295
1296 if (tp.item->size < sizeof(EXTENT_DATA)) {
1297 ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1298 tp.item->size, sizeof(EXTENT_DATA));
1299
1300 free_fcb(fcb);
1301 return STATUS_INTERNAL_ERROR;
1302 }
1303
1304 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
1305 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
1306
1307 if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
1308 ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1309 tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
1310
1311 free_fcb(fcb);
1312 return STATUS_INTERNAL_ERROR;
1313 }
1314
1315 if (ed2->size != 0)
1316 unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1;
1317 }
1318
1319 ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
1320 if (!ext) {
1321 ERR("out of memory\n");
1322 free_fcb(fcb);
1323 return STATUS_INSUFFICIENT_RESOURCES;
1324 }
1325
1326 ext->data = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
1327 if (!ext->data) {
1328 ERR("out of memory\n");
1329 ExFreePool(ext);
1330 free_fcb(fcb);
1331 return STATUS_INSUFFICIENT_RESOURCES;
1332 }
1333
1334 ext->offset = tp.item->key.offset;
1335 RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
1336 ext->datalen = tp.item->size;
1337 ext->unique = unique;
1338 ext->ignore = FALSE;
1339
1340 InsertTailList(&fcb->extents, &ext->list_entry);
1341 }
1342
1343 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
1344
1345 if (b) {
1346 tp = next_tp;
1347
1348 if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
1349 break;
1350 }
1351 } while (b);
1352
1353 if (ed && ed->type == EXTENT_TYPE_INLINE)
1354 fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
1355 else
1356 fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
1357
1358 fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
1359 fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
1360 }
1361
1362 // FIXME - only do if st_nlink > 1?
1363
1364 searchkey.obj_id = inode;
1365 searchkey.obj_type = TYPE_INODE_REF;
1366 searchkey.offset = 0;
1367
1368 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1369 if (!NT_SUCCESS(Status)) {
1370 ERR("error - find_item returned %08x\n", Status);
1371 free_fcb(fcb);
1372 return Status;
1373 }
1374
1375 do {
1376 traverse_ptr next_tp;
1377
1378 if (tp.item->key.obj_id == searchkey.obj_id) {
1379 if (tp.item->key.obj_type == TYPE_INODE_REF) {
1380 ULONG len;
1381 INODE_REF* ir;
1382
1383 len = tp.item->size;
1384 ir = (INODE_REF*)tp.item->data;
1385
1386 while (len >= sizeof(INODE_REF) - 1) {
1387 hardlink* hl;
1388 ULONG stringlen;
1389
1390 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1391 if (!hl) {
1392 ERR("out of memory\n");
1393 free_fcb(fcb);
1394 return STATUS_INSUFFICIENT_RESOURCES;
1395 }
1396
1397 hl->parent = tp.item->key.offset;
1398 hl->index = ir->index;
1399
1400 hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
1401
1402 if (hl->utf8.Length > 0) {
1403 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1404 RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
1405 }
1406
1407 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
1408 if (!NT_SUCCESS(Status)) {
1409 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
1410 ExFreePool(hl);
1411 free_fcb(fcb);
1412 return Status;
1413 }
1414
1415 hl->name.Length = hl->name.MaximumLength = stringlen;
1416
1417 if (stringlen == 0)
1418 hl->name.Buffer = NULL;
1419 else {
1420 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1421
1422 if (!hl->name.Buffer) {
1423 ERR("out of memory\n");
1424 ExFreePool(hl);
1425 free_fcb(fcb);
1426 return STATUS_INSUFFICIENT_RESOURCES;
1427 }
1428
1429 Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
1430 if (!NT_SUCCESS(Status)) {
1431 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
1432 ExFreePool(hl->name.Buffer);
1433 ExFreePool(hl);
1434 free_fcb(fcb);
1435 return Status;
1436 }
1437 }
1438
1439 InsertTailList(&fcb->hardlinks, &hl->list_entry);
1440
1441 len -= sizeof(INODE_REF) - 1 + ir->n;
1442 ir = (INODE_REF*)&ir->name[ir->n];
1443 }
1444 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
1445 ULONG len;
1446 INODE_EXTREF* ier;
1447
1448 len = tp.item->size;
1449 ier = (INODE_EXTREF*)tp.item->data;
1450
1451 while (len >= sizeof(INODE_EXTREF) - 1) {
1452 hardlink* hl;
1453 ULONG stringlen;
1454
1455 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1456 if (!hl) {
1457 ERR("out of memory\n");
1458 free_fcb(fcb);
1459 return STATUS_INSUFFICIENT_RESOURCES;
1460 }
1461
1462 hl->parent = ier->dir;
1463 hl->index = ier->index;
1464
1465 hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
1466
1467 if (hl->utf8.Length > 0) {
1468 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1469 RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
1470 }
1471
1472 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
1473 if (!NT_SUCCESS(Status)) {
1474 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
1475 ExFreePool(hl);
1476 free_fcb(fcb);
1477 return Status;
1478 }
1479
1480 hl->name.Length = hl->name.MaximumLength = stringlen;
1481
1482 if (stringlen == 0)
1483 hl->name.Buffer = NULL;
1484 else {
1485 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1486
1487 if (!hl->name.Buffer) {
1488 ERR("out of memory\n");
1489 ExFreePool(hl);
1490 free_fcb(fcb);
1491 return STATUS_INSUFFICIENT_RESOURCES;
1492 }
1493
1494 Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
1495 if (!NT_SUCCESS(Status)) {
1496 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
1497 ExFreePool(hl->name.Buffer);
1498 ExFreePool(hl);
1499 free_fcb(fcb);
1500 return Status;
1501 }
1502 }
1503
1504 InsertTailList(&fcb->hardlinks, &hl->list_entry);
1505
1506 len -= sizeof(INODE_EXTREF) - 1 + ier->n;
1507 ier = (INODE_EXTREF*)&ier->name[ier->n];
1508 }
1509 }
1510 }
1511
1512 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
1513
1514 if (b) {
1515 tp = next_tp;
1516
1517 if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > TYPE_INODE_EXTREF))
1518 break;
1519 }
1520 } while (b);
1521
1522 *pfcb = fcb;
1523 return STATUS_SUCCESS;
1524 }
1525
1526 NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
1527 UINT32 streamhash, fcb* parent, fcb** pfcb) {
1528 fcb* fcb;
1529 UINT8* xattrdata;
1530 UINT16 xattrlen;
1531
1532 if (!IsListEmpty(&subvol->fcbs)) {
1533 LIST_ENTRY* le = subvol->fcbs.Flink;
1534
1535 while (le != &subvol->fcbs) {
1536 fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
1537
1538 if (fcb->inode == inode && fcb->ads && fcb->adsxattr.Length == xattr->Length &&
1539 RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
1540 #ifdef DEBUG_FCB_REFCOUNTS
1541 LONG rc = InterlockedIncrement(&fcb->refcount);
1542
1543 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
1544 #else
1545 InterlockedIncrement(&fcb->refcount);
1546 #endif
1547
1548 *pfcb = fcb;
1549 return STATUS_SUCCESS;
1550 }
1551
1552 le = le->Flink;
1553 }
1554 }
1555
1556 fcb = create_fcb();
1557 if (!fcb) {
1558 ERR("out of memory\n");
1559 return STATUS_INSUFFICIENT_RESOURCES;
1560 }
1561
1562 if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) {
1563 ERR("get_xattr failed\n");
1564 free_fcb(fcb);
1565 return STATUS_INTERNAL_ERROR;
1566 }
1567
1568 fcb->Vcb = Vcb;
1569
1570 fcb->subvol = parent->subvol;
1571 fcb->inode = parent->inode;
1572 fcb->type = parent->type;
1573 fcb->ads = TRUE;
1574 fcb->adshash = streamhash;
1575 fcb->adsxattr = *xattr;
1576
1577 fcb->adsdata.Buffer = (char*)xattrdata;
1578 fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
1579
1580 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1581 fcb->Header.AllocationSize.QuadPart = xattrlen;
1582 fcb->Header.FileSize.QuadPart = xattrlen;
1583 fcb->Header.ValidDataLength.QuadPart = xattrlen;
1584
1585 TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
1586
1587 InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
1588 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1589
1590 *pfcb = fcb;
1591
1592 return STATUS_SUCCESS;
1593 }
1594
1595 void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
1596 if (do_lock)
1597 ExAcquireResourceExclusiveLite(&parent->nonpaged->children_lock, TRUE);
1598
1599 if (IsListEmpty(&parent->children))
1600 InsertTailList(&parent->children, &child->list_entry);
1601 else {
1602 LIST_ENTRY* le = parent->children.Flink;
1603 file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
1604
1605 if (child->index < fr1->index)
1606 InsertHeadList(&parent->children, &child->list_entry);
1607 else {
1608 while (le != &parent->children) {
1609 file_ref* fr2 = (le->Flink == &parent->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
1610
1611 if (child->index >= fr1->index && (!fr2 || fr2->index > child->index)) {
1612 InsertHeadList(&fr1->list_entry, &child->list_entry);
1613 break;
1614 }
1615
1616 fr1 = fr2;
1617 le = le->Flink;
1618 }
1619 }
1620 }
1621
1622 if (do_lock)
1623 ExReleaseResourceLite(&parent->nonpaged->children_lock);
1624 }
1625
1626 NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
1627 UNICODE_STRING fnus2;
1628 file_ref *dir, *sf, *sf2;
1629 ULONG i, num_parts;
1630 UNICODE_STRING* parts = NULL;
1631 BOOL has_stream;
1632 NTSTATUS Status;
1633
1634 TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
1635
1636 if (Vcb->removing || Vcb->locked)
1637 return STATUS_ACCESS_DENIED;
1638
1639 fnus2 = *fnus;
1640
1641 if (fnus2.Length < sizeof(WCHAR) && !related) {
1642 ERR("error - fnus was too short\n");
1643 return STATUS_INTERNAL_ERROR;
1644 }
1645
1646 if (related && fnus->Length == 0) {
1647 increase_fileref_refcount(related);
1648
1649 *pfr = related;
1650 return STATUS_SUCCESS;
1651 }
1652
1653 if (related) {
1654 dir = related;
1655 } else {
1656 if (fnus2.Buffer[0] != '\\') {
1657 ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
1658 return STATUS_OBJECT_PATH_NOT_FOUND;
1659 }
1660
1661 if (fnus2.Length == sizeof(WCHAR)) {
1662 if (Vcb->root_fileref->fcb->open_count == 0) { // don't allow root to be opened on unmounted FS
1663 ULONG cc;
1664 IO_STATUS_BLOCK iosb;
1665
1666 Status = dev_ioctl(Vcb->devices[0].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
1667
1668 if (!NT_SUCCESS(Status))
1669 return Status;
1670 }
1671
1672 increase_fileref_refcount(Vcb->root_fileref);
1673 *pfr = Vcb->root_fileref;
1674 return STATUS_SUCCESS;
1675 }
1676
1677 dir = Vcb->root_fileref;
1678
1679 fnus2.Buffer++;
1680 fnus2.Length -= sizeof(WCHAR);
1681 fnus2.MaximumLength -= sizeof(WCHAR);
1682 }
1683
1684 if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
1685 WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
1686 file_desc_fileref(related), fnus->Length / sizeof(WCHAR), fnus->Buffer);
1687 return STATUS_OBJECT_PATH_NOT_FOUND;
1688 }
1689
1690 if (fnus->Length == 0) {
1691 num_parts = 0;
1692 } else {
1693 Status = split_path(&fnus2, &parts, &num_parts, &has_stream);
1694 if (!NT_SUCCESS(Status)) {
1695 ERR("split_path returned %08x\n", Status);
1696 return Status;
1697 }
1698 }
1699
1700 sf = dir;
1701 increase_fileref_refcount(dir);
1702
1703 if (parent) {
1704 num_parts--;
1705
1706 if (has_stream && num_parts > 0) {
1707 num_parts--;
1708 has_stream = FALSE;
1709 }
1710 }
1711
1712 if (num_parts == 0) {
1713 Status = STATUS_SUCCESS;
1714 *pfr = dir;
1715 goto end2;
1716 }
1717
1718 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1719
1720 for (i = 0; i < num_parts; i++) {
1721 BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
1722
1723 sf2 = search_fileref_children(sf, &parts[i]);
1724
1725 if (sf2 && sf2->fcb->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
1726 WARN("passed path including file as subdirectory\n");
1727 free_fileref(sf2);
1728
1729 Status = STATUS_OBJECT_PATH_NOT_FOUND;
1730 goto end;
1731 }
1732
1733 if (!sf2) {
1734 if (has_stream && i == num_parts - 1) {
1735 UNICODE_STRING streamname;
1736 ANSI_STRING xattr;
1737 UINT32 streamhash;
1738
1739 streamname.Buffer = NULL;
1740 streamname.Length = streamname.MaximumLength = 0;
1741 xattr.Buffer = NULL;
1742 xattr.Length = xattr.MaximumLength = 0;
1743
1744 // FIXME - check if already opened
1745
1746 if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) {
1747 TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
1748
1749 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1750 goto end;
1751 } else {
1752 fcb* fcb;
1753
1754 if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
1755 RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
1756 WARN("not allowing user.DOSATTRIB to be opened as stream\n");
1757
1758 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1759 goto end;
1760 }
1761
1762 Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb);
1763 if (!NT_SUCCESS(Status)) {
1764 ERR("open_fcb_stream returned %08x\n", Status);
1765 goto end;
1766 }
1767
1768 sf2 = create_fileref();
1769 if (!sf2) {
1770 ERR("out of memory\n");
1771 free_fcb(fcb);
1772 Status = STATUS_INSUFFICIENT_RESOURCES;
1773 goto end;
1774 }
1775
1776 sf2->fcb = fcb;
1777
1778 if (streamname.Buffer) // case has changed
1779 sf2->filepart = streamname;
1780 else {
1781 sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
1782 sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
1783 if (!sf2->filepart.Buffer) {
1784 ERR("out of memory\n");
1785 free_fileref(sf2);
1786 Status = STATUS_INSUFFICIENT_RESOURCES;
1787 goto end;
1788 }
1789
1790 RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
1791 }
1792
1793 Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
1794 if (!NT_SUCCESS(Status)) {
1795 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1796 free_fileref(sf2);
1797 goto end;
1798 }
1799
1800 // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
1801
1802 sf2->parent = (struct _file_ref*)sf;
1803 insert_fileref_child(sf, sf2, TRUE);
1804
1805 increase_fileref_refcount(sf);
1806 }
1807 } else {
1808 root* subvol;
1809 UINT64 inode, index;
1810 UINT8 type;
1811 ANSI_STRING utf8;
1812
1813 Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8);
1814 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
1815 TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
1816
1817 Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
1818 goto end;
1819 } else if (!NT_SUCCESS(Status)) {
1820 ERR("find_file_in_dir returned %08x\n", Status);
1821 goto end;
1822 } else {
1823 fcb* fcb;
1824 ULONG strlen;
1825
1826 if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
1827 WARN("passed path including file as subdirectory\n");
1828
1829 Status = STATUS_OBJECT_PATH_NOT_FOUND;
1830 goto end;
1831 }
1832
1833 Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb);
1834 if (!NT_SUCCESS(Status)) {
1835 ERR("open_fcb returned %08x\n", Status);
1836 goto end;
1837 }
1838
1839 sf2 = create_fileref();
1840 if (!sf2) {
1841 ERR("out of memory\n");
1842 free_fcb(fcb);
1843 Status = STATUS_INSUFFICIENT_RESOURCES;
1844 goto end;
1845 }
1846
1847 sf2->fcb = fcb;
1848
1849 if (type == BTRFS_TYPE_DIRECTORY)
1850 fcb->fileref = sf2;
1851
1852 sf2->index = index;
1853 sf2->utf8 = utf8;
1854
1855 Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
1856 if (!NT_SUCCESS(Status)) {
1857 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
1858 free_fileref(sf2);
1859 goto end;
1860 }
1861
1862 sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
1863 sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
1864 if (!sf2->filepart.Buffer) {
1865 ERR("out of memory\n");
1866 free_fileref(sf2);
1867 Status = STATUS_INSUFFICIENT_RESOURCES;
1868 goto end;
1869 }
1870
1871 Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
1872 if (!NT_SUCCESS(Status)) {
1873 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
1874 free_fileref(sf2);
1875 goto end;
1876 }
1877
1878 Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
1879 if (!NT_SUCCESS(Status)) {
1880 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1881 free_fileref(sf2);
1882 goto end;
1883 }
1884
1885 sf2->parent = (struct _file_ref*)sf;
1886
1887 insert_fileref_child(sf, sf2, TRUE);
1888
1889 increase_fileref_refcount(sf);
1890 }
1891 }
1892 }
1893
1894 if (i == num_parts - 1)
1895 break;
1896
1897 if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
1898 Status = STATUS_REPARSE;
1899
1900 if (unparsed)
1901 *unparsed = fnus->Length - ((parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR));
1902
1903 break;
1904 }
1905
1906 free_fileref(sf);
1907 sf = sf2;
1908 }
1909
1910 if (Status != STATUS_REPARSE)
1911 Status = STATUS_SUCCESS;
1912 *pfr = sf2;
1913
1914 end:
1915 ExReleaseResourceLite(&Vcb->fcb_lock);
1916 free_fileref(sf);
1917
1918 end2:
1919 if (parts)
1920 ExFreePool(parts);
1921
1922 TRACE("returning %08x\n", Status);
1923
1924 return Status;
1925 }
1926
1927 NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
1928 KEY searchkey;
1929 traverse_ptr tp, prev_tp;
1930 NTSTATUS Status;
1931
1932 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1933
1934 if (fcb->last_dir_index != 0) {
1935 *index = fcb->last_dir_index;
1936 fcb->last_dir_index++;
1937 Status = STATUS_SUCCESS;
1938 goto end;
1939 }
1940
1941 searchkey.obj_id = fcb->inode;
1942 searchkey.obj_type = TYPE_DIR_INDEX + 1;
1943 searchkey.offset = 0;
1944
1945 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1946 if (!NT_SUCCESS(Status)) {
1947 ERR("error - find_item returned %08x\n", Status);
1948 goto end;
1949 }
1950
1951 if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) {
1952 if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE))
1953 tp = prev_tp;
1954 }
1955
1956 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
1957 fcb->last_dir_index = tp.item->key.offset + 1;
1958 } else
1959 fcb->last_dir_index = 2;
1960
1961 *index = fcb->last_dir_index;
1962 fcb->last_dir_index++;
1963
1964 Status = STATUS_SUCCESS;
1965
1966 end:
1967 ExReleaseResourceLite(fcb->Header.Resource);
1968
1969 return Status;
1970 }
1971
1972 static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options, file_ref** pfr, LIST_ENTRY* rollback) {
1973 NTSTATUS Status;
1974 fcb* fcb;
1975 ULONG utf8len;
1976 char* utf8 = NULL;
1977 UINT64 dirpos, inode;
1978 UINT8 type;
1979 LARGE_INTEGER time;
1980 BTRFS_TIME now;
1981 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1982 ULONG defda;
1983 file_ref* fileref;
1984 hardlink* hl;
1985 #ifdef DEBUG_FCB_REFCOUNTS
1986 LONG rc;
1987 #endif
1988
1989 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
1990 if (!NT_SUCCESS(Status))
1991 return Status;
1992
1993 utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1994 if (!utf8) {
1995 ERR("out of memory\n");
1996 return STATUS_INSUFFICIENT_RESOURCES;
1997 }
1998
1999 Status = RtlUnicodeToUTF8N(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
2000 if (!NT_SUCCESS(Status)) {
2001 ExFreePool(utf8);
2002 return Status;
2003 }
2004
2005 utf8[utf8len] = 0;
2006
2007 Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos);
2008 if (!NT_SUCCESS(Status)) {
2009 ERR("fcb_get_last_dir_index returned %08x\n", Status);
2010 ExFreePool(utf8);
2011 return Status;
2012 }
2013
2014 KeQuerySystemTime(&time);
2015 win_time_to_unix(time, &now);
2016
2017 TRACE("create file %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
2018 ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
2019 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2020 parfileref->fcb->inode_item.st_size += utf8len * 2;
2021 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2022 parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2023 parfileref->fcb->inode_item.sequence++;
2024 parfileref->fcb->inode_item.st_ctime = now;
2025 parfileref->fcb->inode_item.st_mtime = now;
2026 ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2027
2028 mark_fcb_dirty(parfileref->fcb);
2029
2030 if (parfileref->fcb->subvol->lastinode == 0)
2031 get_last_inode(Vcb, parfileref->fcb->subvol);
2032
2033 inode = parfileref->fcb->subvol->lastinode + 1;
2034
2035 type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
2036
2037 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
2038
2039 TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
2040
2041 IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
2042
2043 defda = 0;
2044
2045 if (utf8[0] == '.')
2046 defda |= FILE_ATTRIBUTE_HIDDEN;
2047
2048 if (options & FILE_DIRECTORY_FILE) {
2049 defda |= FILE_ATTRIBUTE_DIRECTORY;
2050 IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2051 }
2052
2053 TRACE("defda = %x\n", defda);
2054
2055 if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
2056 IrpSp->Parameters.Create.FileAttributes = defda;
2057
2058 parfileref->fcb->subvol->lastinode++;
2059
2060 fcb = create_fcb();
2061 if (!fcb) {
2062 ERR("out of memory\n");
2063 ExFreePool(utf8);
2064 return STATUS_INSUFFICIENT_RESOURCES;
2065 }
2066
2067 fcb->Vcb = Vcb;
2068
2069 fcb->inode_item.generation = Vcb->superblock.generation;
2070 fcb->inode_item.transid = Vcb->superblock.generation;
2071 fcb->inode_item.st_size = 0;
2072 fcb->inode_item.st_blocks = 0;
2073 fcb->inode_item.block_group = 0;
2074 fcb->inode_item.st_nlink = 1;
2075 // fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
2076 fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
2077 fcb->inode_item.st_mode = parfileref->fcb ? (parfileref->fcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
2078 fcb->inode_item.st_rdev = 0;
2079 fcb->inode_item.flags = 0;
2080 fcb->inode_item.sequence = 1;
2081 fcb->inode_item.st_atime = now;
2082 fcb->inode_item.st_ctime = now;
2083 fcb->inode_item.st_mtime = now;
2084 fcb->inode_item.otime = now;
2085
2086 if (type == BTRFS_TYPE_DIRECTORY)
2087 fcb->inode_item.st_mode |= S_IFDIR;
2088 else {
2089 fcb->inode_item.st_mode |= S_IFREG;
2090 fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
2091 }
2092
2093 // inherit nodatacow flag from parent directory
2094 if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
2095 fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
2096
2097 if (type != BTRFS_TYPE_DIRECTORY)
2098 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
2099 }
2100
2101 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2102 fcb->Header.AllocationSize.QuadPart = 0;
2103 fcb->Header.FileSize.QuadPart = 0;
2104 fcb->Header.ValidDataLength.QuadPart = 0;
2105
2106 fcb->atts = IrpSp->Parameters.Create.FileAttributes;
2107 fcb->atts_changed = fcb->atts != defda;
2108
2109 #ifdef DEBUG_FCB_REFCOUNTS
2110 rc = InterlockedIncrement(&parfileref->fcb->refcount);
2111 WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
2112 #else
2113 InterlockedIncrement(&parfileref->fcb->refcount);
2114 #endif
2115 fcb->subvol = parfileref->fcb->subvol;
2116 fcb->inode = inode;
2117 fcb->type = type;
2118
2119 Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
2120
2121 if (!NT_SUCCESS(Status)) {
2122 ERR("fcb_get_new_sd returned %08x\n", Status);
2123 free_fcb(fcb);
2124 return Status;
2125 }
2126
2127 fcb->sd_dirty = TRUE;
2128
2129 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2130 if (!hl) {
2131 ERR("out of memory\n");
2132 free_fcb(fcb);
2133 return STATUS_INSUFFICIENT_RESOURCES;
2134 }
2135
2136 hl->parent = parfileref->fcb->inode;
2137 hl->index = dirpos;
2138
2139 hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
2140 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
2141
2142 if (!hl->utf8.Buffer) {
2143 ERR("out of memory\n");
2144 ExFreePool(hl);
2145 free_fcb(fcb);
2146 return STATUS_INSUFFICIENT_RESOURCES;
2147 }
2148 RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
2149
2150 hl->name.Length = hl->name.MaximumLength = fpus->Length;
2151 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG);
2152
2153 if (!hl->name.Buffer) {
2154 ERR("out of memory\n");
2155 ExFreePool(hl->utf8.Buffer);
2156 ExFreePool(hl);
2157 free_fcb(fcb);
2158 return STATUS_INSUFFICIENT_RESOURCES;
2159 }
2160
2161 RtlCopyMemory(hl->name.Buffer, fpus->Buffer, fpus->Length);
2162
2163 InsertTailList(&fcb->hardlinks, &hl->list_entry);
2164
2165 fileref = create_fileref();
2166 if (!fileref) {
2167 ERR("out of memory\n");
2168 free_fcb(fcb);
2169 return STATUS_INSUFFICIENT_RESOURCES;
2170 }
2171
2172 fileref->fcb = fcb;
2173 fileref->index = dirpos;
2174
2175 fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
2176 fileref->utf8.Buffer = utf8;
2177
2178 fileref->filepart = *fpus;
2179
2180 Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
2181 if (!NT_SUCCESS(Status)) {
2182 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
2183 free_fileref(fileref);
2184 return Status;
2185 }
2186
2187 if (Irp->Overlay.AllocationSize.QuadPart > 0) {
2188 Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
2189
2190 if (!NT_SUCCESS(Status)) {
2191 ERR("extend_file returned %08x\n", Status);
2192 free_fileref(fileref);
2193 return Status;
2194 }
2195 }
2196
2197 fcb->created = TRUE;
2198 mark_fcb_dirty(fcb);
2199
2200 fileref->created = TRUE;
2201 mark_fileref_dirty(fileref);
2202
2203 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2204 fcb->subvol->root_item.ctime = now;
2205
2206 fileref->parent = parfileref;
2207
2208 ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
2209
2210 if (IsListEmpty(&parfileref->children))
2211 InsertTailList(&parfileref->children, &fileref->list_entry);
2212 else {
2213 LIST_ENTRY* le = parfileref->children.Flink;
2214 file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
2215
2216 while (le != &parfileref->children) {
2217 file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
2218
2219 if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) {
2220 InsertHeadList(&fr1->list_entry, &fileref->list_entry);
2221 break;
2222 }
2223
2224 fr1 = fr2;
2225 le = le->Flink;
2226 }
2227 }
2228
2229 ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
2230
2231 increase_fileref_refcount(parfileref);
2232
2233 InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
2234 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2235
2236 *pfr = fileref;
2237
2238 if (type == BTRFS_TYPE_DIRECTORY)
2239 fileref->fcb->fileref = fileref;
2240
2241 TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
2242
2243 return STATUS_SUCCESS;
2244 }
2245
2246 static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
2247 NTSTATUS Status;
2248 // fcb *fcb, *parfcb = NULL;
2249 file_ref *fileref, *parfileref = NULL, *related;
2250 ULONG i, j;
2251 // ULONG utf8len;
2252 ccb* ccb;
2253 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
2254 UNICODE_STRING dsus, fpus, stream;
2255 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2256 PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
2257 #ifdef DEBUG_FCB_REFCOUNTS
2258 LONG oc;
2259 #endif
2260
2261 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
2262
2263 if (Vcb->readonly)
2264 return STATUS_MEDIA_WRITE_PROTECTED;
2265
2266 dsus.Buffer = datasuf;
2267 dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
2268 fpus.Buffer = NULL;
2269
2270 if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
2271 struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
2272
2273 related = relatedccb->fileref;
2274 } else
2275 related = NULL;
2276
2277 Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
2278 if (!NT_SUCCESS(Status))
2279 goto end;
2280
2281 if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
2282 Status = STATUS_OBJECT_PATH_NOT_FOUND;
2283 goto end;
2284 }
2285
2286 if (parfileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
2287 Status = STATUS_ACCESS_DENIED;
2288 goto end;
2289 }
2290
2291 i = (fnus->Length / sizeof(WCHAR))-1;
2292 while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
2293
2294 j = i;
2295
2296 while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
2297
2298 fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
2299 fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
2300 if (!fpus.Buffer) {
2301 ERR("out of memory\n");
2302 Status = STATUS_INSUFFICIENT_RESOURCES;
2303 goto end;
2304 }
2305
2306 fpus.Length = (j - i + 1) * sizeof(WCHAR);
2307
2308 RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
2309 fpus.Buffer[j - i + 1] = 0;
2310
2311 if (fpus.Length > dsus.Length) { // check for :$DATA suffix
2312 UNICODE_STRING lb;
2313
2314 lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
2315 lb.Length = lb.MaximumLength = dsus.Length;
2316
2317 TRACE("lb = %.*S\n", lb.Length/sizeof(WCHAR), lb.Buffer);
2318
2319 if (FsRtlAreNamesEqual(&dsus, &lb, TRUE, NULL)) {
2320 TRACE("ignoring :$DATA suffix\n");
2321
2322 fpus.Length -= lb.Length;
2323
2324 if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
2325 fpus.Length -= sizeof(WCHAR);
2326
2327 TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
2328 }
2329 }
2330
2331 stream.Length = 0;
2332
2333 for (i = 0; i < fpus.Length/sizeof(WCHAR); i++) {
2334 if (fpus.Buffer[i] == ':') {
2335 stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
2336 stream.Buffer = &fpus.Buffer[i+1];
2337 fpus.Buffer[i] = 0;
2338 fpus.Length = i * sizeof(WCHAR);
2339 break;
2340 }
2341 }
2342
2343 if (stream.Length > 0) {
2344 file_ref* newpar;
2345 fcb* fcb;
2346 static char xapref[] = "user.";
2347 ULONG xapreflen = strlen(xapref);
2348 LARGE_INTEGER time;
2349 BTRFS_TIME now;
2350 ULONG utf8len;
2351 #ifdef DEBUG_FCB_REFCOUNTS
2352 LONG rc;
2353 #endif
2354
2355 TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
2356 TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
2357
2358 Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
2359
2360 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
2361 UNICODE_STRING fpus2;
2362
2363 if (!is_file_name_valid(&fpus))
2364 return STATUS_OBJECT_NAME_INVALID;
2365
2366 fpus2.Length = fpus2.MaximumLength = fpus.Length;
2367 fpus2.Buffer = ExAllocatePoolWithTag(PagedPool, fpus2.MaximumLength, ALLOC_TAG);
2368
2369 if (!fpus2.Buffer) {
2370 ERR("out of memory\n");
2371 Status = STATUS_INSUFFICIENT_RESOURCES;
2372 goto end;
2373 }
2374
2375 RtlCopyMemory(fpus2.Buffer, fpus.Buffer, fpus2.Length);
2376
2377 Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
2378
2379 if (!NT_SUCCESS(Status)) {
2380 ERR("file_create2 returned %08x\n", Status);
2381 ExFreePool(fpus2.Buffer);
2382 goto end;
2383 }
2384
2385 send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
2386 send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
2387 } else if (!NT_SUCCESS(Status)) {
2388 ERR("open_fileref returned %08x\n", Status);
2389 goto end;
2390 }
2391
2392 free_fileref(parfileref);
2393 parfileref = newpar;
2394
2395 if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
2396 WARN("parent not file or symlink\n");
2397 Status = STATUS_INVALID_PARAMETER;
2398 goto end;
2399 }
2400
2401 if (options & FILE_DIRECTORY_FILE) {
2402 WARN("tried to create directory as stream\n");
2403 Status = STATUS_INVALID_PARAMETER;
2404 goto end;
2405 }
2406
2407 fcb = create_fcb();
2408 if (!fcb) {
2409 ERR("out of memory\n");
2410 Status = STATUS_INSUFFICIENT_RESOURCES;
2411 goto end;
2412 }
2413
2414 fcb->Vcb = Vcb;
2415
2416 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2417 fcb->Header.AllocationSize.QuadPart = 0;
2418 fcb->Header.FileSize.QuadPart = 0;
2419 fcb->Header.ValidDataLength.QuadPart = 0;
2420
2421 #ifdef DEBUG_FCB_REFCOUNTS
2422 rc = InterlockedIncrement(&parfileref->fcb->refcount);
2423 WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
2424 #else
2425 InterlockedIncrement(&parfileref->fcb->refcount);
2426 #endif
2427 fcb->subvol = parfileref->fcb->subvol;
2428 fcb->inode = parfileref->fcb->inode;
2429 fcb->type = parfileref->fcb->type;
2430
2431 fcb->ads = TRUE;
2432
2433 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
2434 if (!NT_SUCCESS(Status)) {
2435 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
2436 free_fcb(fcb);
2437 goto end;
2438 }
2439
2440 fcb->adsxattr.Length = utf8len + xapreflen;
2441 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
2442 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
2443 if (!fcb->adsxattr.Buffer) {
2444 ERR("out of memory\n");
2445 free_fcb(fcb);
2446 Status = STATUS_INSUFFICIENT_RESOURCES;
2447 goto end;
2448 }
2449
2450 RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
2451
2452 Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
2453 if (!NT_SUCCESS(Status)) {
2454 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
2455 free_fcb(fcb);
2456 goto end;
2457 }
2458
2459 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
2460
2461 TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
2462
2463 fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
2464 TRACE("adshash = %08x\n", fcb->adshash);
2465
2466 fileref = create_fileref();
2467 if (!fileref) {
2468 ERR("out of memory\n");
2469 free_fcb(fcb);
2470 Status = STATUS_INSUFFICIENT_RESOURCES;
2471 goto end;
2472 }
2473
2474 fileref->fcb = fcb;
2475
2476 fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length;
2477 fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
2478 if (!fileref->filepart.Buffer) {
2479 ERR("out of memory\n");
2480 free_fileref(fileref);
2481 Status = STATUS_INSUFFICIENT_RESOURCES;
2482 goto end;
2483 }
2484
2485 RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
2486
2487 Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
2488 if (!NT_SUCCESS(Status)) {
2489 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
2490 free_fileref(fileref);
2491 goto end;
2492 }
2493
2494 mark_fcb_dirty(fcb);
2495
2496 InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
2497 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2498
2499 KeQuerySystemTime(&time);
2500 win_time_to_unix(time, &now);
2501
2502 parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2503 parfileref->fcb->inode_item.sequence++;
2504 parfileref->fcb->inode_item.st_ctime = now;
2505
2506 mark_fcb_dirty(parfileref->fcb);
2507
2508 parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2509 parfileref->fcb->subvol->root_item.ctime = now;
2510
2511 fileref->parent = (struct _file_ref*)parfileref;
2512
2513 ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
2514 InsertTailList(&parfileref->children, &fileref->list_entry);
2515 ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
2516
2517 increase_fileref_refcount(parfileref);
2518
2519 ExFreePool(fpus.Buffer);
2520 fpus.Buffer = NULL;
2521 } else {
2522 if (!is_file_name_valid(&fpus)) {
2523 Status = STATUS_OBJECT_NAME_INVALID;
2524 goto end;
2525 }
2526
2527 Status = file_create2(Irp, Vcb, &fpus, parfileref, options, &fileref, rollback);
2528
2529 if (!NT_SUCCESS(Status)) {
2530 ERR("file_create2 returned %08x\n", Status);
2531 goto end;
2532 }
2533
2534 send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
2535 send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
2536 }
2537
2538 FileObject->FsContext = fileref->fcb;
2539
2540 ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
2541 if (!ccb) {
2542 ERR("out of memory\n");
2543 Status = STATUS_INSUFFICIENT_RESOURCES;
2544 free_fileref(fileref);
2545 goto end;
2546 }
2547
2548 RtlZeroMemory(ccb, sizeof(*ccb));
2549
2550 ccb->fileref = fileref;
2551
2552 ccb->NodeType = BTRFS_NODE_TYPE_CCB;
2553 ccb->NodeSize = sizeof(ccb);
2554 ccb->disposition = disposition;
2555 ccb->options = options;
2556 ccb->query_dir_offset = 0;
2557 RtlInitUnicodeString(&ccb->query_string, NULL);
2558 ccb->has_wildcard = FALSE;
2559 ccb->specific_file = FALSE;
2560 ccb->access = access_state->OriginalDesiredAccess;
2561
2562 #ifdef DEBUG_FCB_REFCOUNTS
2563 oc = InterlockedIncrement(&fileref->fcb->open_count);
2564 ERR("fcb %p: open_count now %i\n", fileref->fcb, oc);
2565 #else
2566 InterlockedIncrement(&fileref->fcb->open_count);
2567 #endif
2568
2569 FileObject->FsContext2 = ccb;
2570
2571 FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
2572
2573 // TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
2574
2575 // ULONG fnlen;
2576 //
2577 // fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
2578 //
2579 // if (fcb->par != Vcb->root_fcb)
2580 // fcb->name_offset++;
2581 //
2582 // fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
2583 //
2584 // fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
2585 // if (!fcb->full_filename.Buffer) {
2586 // ERR("out of memory\n");
2587 // Status = STATUS_INSUFFICIENT_RESOURCES;
2588 // goto end;
2589 // }
2590 //
2591 // fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
2592 // RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
2593 //
2594 // if (fcb->par != Vcb->root_fcb)
2595 // fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
2596 //
2597 // RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
2598
2599 goto end2;
2600
2601 end:
2602 if (fpus.Buffer)
2603 ExFreePool(fpus.Buffer);
2604
2605 end2:
2606 if (parfileref)
2607 free_fileref(parfileref);
2608
2609 return Status;
2610 }
2611
2612 static __inline void debug_create_options(ULONG RequestedOptions) {
2613 if (RequestedOptions != 0) {
2614 ULONG options = RequestedOptions;
2615
2616 TRACE("requested options:\n");
2617
2618 if (options & FILE_DIRECTORY_FILE) {
2619 TRACE(" FILE_DIRECTORY_FILE\n");
2620 options &= ~FILE_DIRECTORY_FILE;
2621 }
2622
2623 if (options & FILE_WRITE_THROUGH) {
2624 TRACE(" FILE_WRITE_THROUGH\n");
2625 options &= ~FILE_WRITE_THROUGH;
2626 }
2627
2628 if (options & FILE_SEQUENTIAL_ONLY) {
2629 TRACE(" FILE_SEQUENTIAL_ONLY\n");
2630 options &= ~FILE_SEQUENTIAL_ONLY;
2631 }
2632
2633 if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
2634 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
2635 options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
2636 }
2637
2638 if (options & FILE_SYNCHRONOUS_IO_ALERT) {
2639 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
2640 options &= ~FILE_SYNCHRONOUS_IO_ALERT;
2641 }
2642
2643 if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
2644 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
2645 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
2646 }
2647
2648 if (options & FILE_NON_DIRECTORY_FILE) {
2649 TRACE(" FILE_NON_DIRECTORY_FILE\n");
2650 options &= ~FILE_NON_DIRECTORY_FILE;
2651 }
2652
2653 if (options & FILE_CREATE_TREE_CONNECTION) {
2654 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
2655 options &= ~FILE_CREATE_TREE_CONNECTION;
2656 }
2657
2658 if (options & FILE_COMPLETE_IF_OPLOCKED) {
2659 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
2660 options &= ~FILE_COMPLETE_IF_OPLOCKED;
2661 }
2662
2663 if (options & FILE_NO_EA_KNOWLEDGE) {
2664 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
2665 options &= ~FILE_NO_EA_KNOWLEDGE;
2666 }
2667
2668 if (options & FILE_OPEN_REMOTE_INSTANCE) {
2669 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
2670 options &= ~FILE_OPEN_REMOTE_INSTANCE;
2671 }
2672
2673 if (options & FILE_RANDOM_ACCESS) {
2674 TRACE(" FILE_RANDOM_ACCESS\n");
2675 options &= ~FILE_RANDOM_ACCESS;
2676 }
2677
2678 if (options & FILE_DELETE_ON_CLOSE) {
2679 TRACE(" FILE_DELETE_ON_CLOSE\n");
2680 options &= ~FILE_DELETE_ON_CLOSE;
2681 }
2682
2683 if (options & FILE_OPEN_BY_FILE_ID) {
2684 TRACE(" FILE_OPEN_BY_FILE_ID\n");
2685 options &= ~FILE_OPEN_BY_FILE_ID;
2686 }
2687
2688 if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
2689 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
2690 options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
2691 }
2692
2693 if (options & FILE_NO_COMPRESSION) {
2694 TRACE(" FILE_NO_COMPRESSION\n");
2695 options &= ~FILE_NO_COMPRESSION;
2696 }
2697
2698 #if NTDDI_VERSION >= NTDDI_WIN7
2699 if (options & FILE_OPEN_REQUIRING_OPLOCK) {
2700 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
2701 options &= ~FILE_OPEN_REQUIRING_OPLOCK;
2702 }
2703
2704 if (options & FILE_DISALLOW_EXCLUSIVE) {
2705 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
2706 options &= ~FILE_DISALLOW_EXCLUSIVE;
2707 }
2708 #endif
2709
2710 if (options & FILE_RESERVE_OPFILTER) {
2711 TRACE(" FILE_RESERVE_OPFILTER\n");
2712 options &= ~FILE_RESERVE_OPFILTER;
2713 }
2714
2715 if (options & FILE_OPEN_REPARSE_POINT) {
2716 TRACE(" FILE_OPEN_REPARSE_POINT\n");
2717 options &= ~FILE_OPEN_REPARSE_POINT;
2718 }
2719
2720 if (options & FILE_OPEN_NO_RECALL) {
2721 TRACE(" FILE_OPEN_NO_RECALL\n");
2722 options &= ~FILE_OPEN_NO_RECALL;
2723 }
2724
2725 if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
2726 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
2727 options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
2728 }
2729
2730 if (options)
2731 TRACE(" unknown options: %x\n", options);
2732 } else {
2733 TRACE("requested options: (none)\n");
2734 }
2735 }
2736
2737 static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
2738 NTSTATUS Status;
2739
2740 if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
2741 ULONG size, bytes_read, i;
2742
2743 if (fcb->inode_item.st_size < sizeof(ULONG)) {
2744 WARN("file was too short to be a reparse point\n");
2745 return STATUS_INVALID_PARAMETER;
2746 }
2747
2748 // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
2749 size = min(0x10007, fcb->inode_item.st_size);
2750
2751 *data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
2752 if (!*data) {
2753 ERR("out of memory\n");
2754 return STATUS_INSUFFICIENT_RESOURCES;
2755 }
2756
2757 Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
2758 if (!NT_SUCCESS(Status)) {
2759 ERR("read_file_fcb returned %08x\n", Status);
2760 ExFreePool(*data);
2761 return Status;
2762 }
2763
2764 if (fcb->type == BTRFS_TYPE_SYMLINK) {
2765 ULONG stringlen, subnamelen, printnamelen, reqlen;
2766 REPARSE_DATA_BUFFER* rdb;
2767
2768 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)*data, bytes_read);
2769 if (!NT_SUCCESS(Status)) {
2770 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
2771 ExFreePool(*data);
2772 return Status;
2773 }
2774
2775 subnamelen = stringlen;
2776 printnamelen = stringlen;
2777
2778 reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
2779
2780 rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
2781
2782 if (!rdb) {
2783 ERR("out of memory\n");
2784 ExFreePool(*data);
2785 return STATUS_INSUFFICIENT_RESOURCES;
2786 }
2787
2788 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
2789 rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
2790 rdb->Reserved = 0;
2791
2792 rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
2793 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
2794 rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
2795 rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
2796 rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
2797
2798 Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
2799 stringlen, &stringlen, (char*)*data, size);
2800
2801 if (!NT_SUCCESS(Status)) {
2802 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
2803 ExFreePool(rdb);
2804 ExFreePool(*data);
2805 return Status;
2806 }
2807
2808 for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
2809 if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
2810 rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
2811 }
2812
2813 RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
2814 &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
2815 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
2816
2817 ExFreePool(*data);
2818
2819 *data = (UINT8*)rdb;
2820 } else {
2821 Status = FsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data);
2822 if (!NT_SUCCESS(Status)) {
2823 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
2824 ExFreePool(*data);
2825 return Status;
2826 }
2827 }
2828 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2829 if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0)
2830 return STATUS_INTERNAL_ERROR;
2831
2832 if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
2833 WARN("xattr was too short to be a reparse point\n");
2834 return STATUS_INTERNAL_ERROR;
2835 }
2836
2837 Status = FsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer);
2838 if (!NT_SUCCESS(Status)) {
2839 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
2840 return Status;
2841 }
2842
2843 *data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG);
2844 if (!*data) {
2845 ERR("out of memory\n");
2846 return STATUS_INSUFFICIENT_RESOURCES;
2847 }
2848
2849 RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
2850 }
2851
2852 return STATUS_SUCCESS;
2853 }
2854
2855 static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
2856 PFILE_OBJECT FileObject;
2857 ULONG RequestedDisposition;
2858 ULONG options;
2859 NTSTATUS Status;
2860 ccb* ccb;
2861 device_extension* Vcb = DeviceObject->DeviceExtension;
2862 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
2863 USHORT unparsed;
2864 file_ref *related, *fileref;
2865 #ifdef DEBUG_FCB_REFCOUNTS
2866 LONG oc;
2867 #endif
2868
2869 Irp->IoStatus.Information = 0;
2870
2871 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
2872 options = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
2873
2874 if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
2875 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");