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