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