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