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