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