xref: /reactos/drivers/filesystems/btrfs/create.c (revision 29d19382)
1c2c66affSColin Finck /* Copyright (c) Mark Harmstone 2016-17
2c2c66affSColin Finck  *
3c2c66affSColin Finck  * This file is part of WinBtrfs.
4c2c66affSColin Finck  *
5c2c66affSColin Finck  * WinBtrfs is free software: you can redistribute it and/or modify
6c2c66affSColin Finck  * it under the terms of the GNU Lesser General Public Licence as published by
7c2c66affSColin Finck  * the Free Software Foundation, either version 3 of the Licence, or
8c2c66affSColin Finck  * (at your option) any later version.
9c2c66affSColin Finck  *
10c2c66affSColin Finck  * WinBtrfs is distributed in the hope that it will be useful,
11c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13c2c66affSColin Finck  * GNU Lesser General Public Licence for more details.
14c2c66affSColin Finck  *
15c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public Licence
16c2c66affSColin Finck  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17c2c66affSColin Finck 
18c2c66affSColin Finck #ifndef __REACTOS__
19c2c66affSColin Finck #include <sys/stat.h>
20c2c66affSColin Finck #endif /* __REACTOS__ */
21c2c66affSColin Finck #include "btrfs_drv.h"
22194ea909SVictor Perevertkin #include "crc32c.h"
23c2c66affSColin Finck #include <ntddstor.h>
24c2c66affSColin Finck 
25c2c66affSColin Finck extern PDEVICE_OBJECT master_devobj;
26318da0c1SPierre Schweitzer extern tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;
27318da0c1SPierre Schweitzer extern tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;
28318da0c1SPierre Schweitzer extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
29c2c66affSColin Finck 
30eb7fbc25SPierre Schweitzer static const WCHAR datastring[] = L"::$DATA";
31eb7fbc25SPierre Schweitzer 
3262e630deSPierre Schweitzer static const char root_dir[] = "$Root";
3362e630deSPierre Schweitzer static const WCHAR root_dir_utf16[] = L"$Root";
3462e630deSPierre Schweitzer 
35eb7fbc25SPierre Schweitzer // Windows 10
36eb7fbc25SPierre Schweitzer #define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED   0x0002
37194ea909SVictor Perevertkin #define ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED        0x0080
38eb7fbc25SPierre Schweitzer #define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT               0x0100
39194ea909SVictor Perevertkin 
40eb7fbc25SPierre Schweitzer #define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET        0x0002
41194ea909SVictor Perevertkin #define ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED         0x0080
42194ea909SVictor Perevertkin 
43194ea909SVictor Perevertkin #define ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED       1
44194ea909SVictor Perevertkin #define ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET            1
45194ea909SVictor Perevertkin 
46174dfab6SVincent Franchomme #ifndef SL_IGNORE_READONLY_ATTRIBUTE
47174dfab6SVincent Franchomme #define SL_IGNORE_READONLY_ATTRIBUTE 0x40 // introduced in Windows 10, not in mingw
48174dfab6SVincent Franchomme #endif
49174dfab6SVincent Franchomme 
50194ea909SVictor Perevertkin typedef struct _FILE_TIMESTAMPS {
51194ea909SVictor Perevertkin     LARGE_INTEGER CreationTime;
52194ea909SVictor Perevertkin     LARGE_INTEGER LastAccessTime;
53194ea909SVictor Perevertkin     LARGE_INTEGER LastWriteTime;
54194ea909SVictor Perevertkin     LARGE_INTEGER ChangeTime;
55194ea909SVictor Perevertkin } FILE_TIMESTAMPS, *PFILE_TIMESTAMPS;
56eb7fbc25SPierre Schweitzer 
57eb7fbc25SPierre Schweitzer typedef struct _ATOMIC_CREATE_ECP_CONTEXT {
58eb7fbc25SPierre Schweitzer     USHORT Size;
59eb7fbc25SPierre Schweitzer     USHORT InFlags;
60eb7fbc25SPierre Schweitzer     USHORT OutFlags;
61eb7fbc25SPierre Schweitzer     USHORT ReparseBufferLength;
62eb7fbc25SPierre Schweitzer     PREPARSE_DATA_BUFFER ReparseBuffer;
63eb7fbc25SPierre Schweitzer     LONGLONG FileSize;
64eb7fbc25SPierre Schweitzer     LONGLONG ValidDataLength;
65194ea909SVictor Perevertkin     PFILE_TIMESTAMPS FileTimestamps;
66194ea909SVictor Perevertkin     ULONG FileAttributes;
67194ea909SVictor Perevertkin     ULONG UsnSourceInfo;
68194ea909SVictor Perevertkin     USN Usn;
69194ea909SVictor Perevertkin     ULONG SuppressFileAttributeInheritanceMask;
70194ea909SVictor Perevertkin     ULONG InOpFlags;
71194ea909SVictor Perevertkin     ULONG OutOpFlags;
72194ea909SVictor Perevertkin     ULONG InGenFlags;
73194ea909SVictor Perevertkin     ULONG OutGenFlags;
74194ea909SVictor Perevertkin     ULONG CaseSensitiveFlagsMask;
75194ea909SVictor Perevertkin     ULONG InCaseSensitiveFlags;
76194ea909SVictor Perevertkin     ULONG OutCaseSensitiveFlags;
77eb7fbc25SPierre Schweitzer } ATOMIC_CREATE_ECP_CONTEXT, *PATOMIC_CREATE_ECP_CONTEXT;
78eb7fbc25SPierre Schweitzer 
79eb7fbc25SPierre Schweitzer static const GUID GUID_ECP_ATOMIC_CREATE = { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30, 0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };
80194ea909SVictor Perevertkin static const GUID GUID_ECP_QUERY_ON_CREATE = { 0x1aca62e9, 0xabb4, 0x4ff2, { 0xbb, 0x5c, 0x1c, 0x79, 0x02, 0x5e, 0x41, 0x7f } };
81194ea909SVictor Perevertkin static const GUID GUID_ECP_CREATE_REDIRECTION = { 0x188d6bd6, 0xa126, 0x4fa8, { 0xbd, 0xf2, 0x1c, 0xcd, 0xf8, 0x96, 0xf3, 0xe0 } };
82c2c66affSColin Finck 
83c982533eSVincent Franchomme typedef struct {
84c982533eSVincent Franchomme     device_extension* Vcb;
85c982533eSVincent Franchomme     ACCESS_MASK granted_access;
86c982533eSVincent Franchomme     file_ref* fileref;
876e0cf03dSVincent Franchomme     NTSTATUS Status;
886e0cf03dSVincent Franchomme     KEVENT event;
89c982533eSVincent Franchomme } oplock_context;
90c982533eSVincent Franchomme 
create_fcb(device_extension * Vcb,POOL_TYPE pool_type)91c2c66affSColin Finck fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
92c2c66affSColin Finck     fcb* fcb;
93c2c66affSColin Finck 
94c2c66affSColin Finck     if (pool_type == NonPagedPool) {
95c2c66affSColin Finck         fcb = ExAllocatePoolWithTag(pool_type, sizeof(struct _fcb), ALLOC_TAG);
96c2c66affSColin Finck         if (!fcb) {
97c2c66affSColin Finck             ERR("out of memory\n");
98c2c66affSColin Finck             return NULL;
99c2c66affSColin Finck         }
100c2c66affSColin Finck     } else {
101c2c66affSColin Finck         fcb = ExAllocateFromPagedLookasideList(&Vcb->fcb_lookaside);
102c2c66affSColin Finck         if (!fcb) {
103c2c66affSColin Finck             ERR("out of memory\n");
104c2c66affSColin Finck             return NULL;
105c2c66affSColin Finck         }
106c2c66affSColin Finck     }
107c2c66affSColin Finck 
108c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
109c2c66affSColin Finck     WARN("allocating fcb %p\n", fcb);
110c2c66affSColin Finck #endif
111c2c66affSColin Finck     RtlZeroMemory(fcb, sizeof(struct _fcb));
112c2c66affSColin Finck     fcb->pool_type = pool_type;
113c2c66affSColin Finck 
114c2c66affSColin Finck     fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
115c2c66affSColin Finck     fcb->Header.NodeByteSize = sizeof(struct _fcb);
116c2c66affSColin Finck 
117c2c66affSColin Finck     fcb->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fcb_np_lookaside);
118c2c66affSColin Finck     if (!fcb->nonpaged) {
119c2c66affSColin Finck         ERR("out of memory\n");
120c2c66affSColin Finck 
121c2c66affSColin Finck         if (pool_type == NonPagedPool)
122c2c66affSColin Finck             ExFreePool(fcb);
123c2c66affSColin Finck         else
124c2c66affSColin Finck             ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
125c2c66affSColin Finck 
126c2c66affSColin Finck         return NULL;
127c2c66affSColin Finck     }
128c2c66affSColin Finck     RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
129c2c66affSColin Finck 
130c2c66affSColin Finck     ExInitializeResourceLite(&fcb->nonpaged->paging_resource);
131c2c66affSColin Finck     fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
132c2c66affSColin Finck 
133c2c66affSColin Finck     ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
134c2c66affSColin Finck     FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
135c2c66affSColin Finck 
136c2c66affSColin Finck     fcb->refcount = 1;
137c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
138c2c66affSColin Finck     WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
139c2c66affSColin Finck #endif
140c2c66affSColin Finck 
141c2c66affSColin Finck     ExInitializeResourceLite(&fcb->nonpaged->resource);
142c2c66affSColin Finck     fcb->Header.Resource = &fcb->nonpaged->resource;
143c2c66affSColin Finck 
144c2c66affSColin Finck     ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
145c2c66affSColin Finck 
146c2c66affSColin Finck     FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
14762e630deSPierre Schweitzer     FsRtlInitializeOplock(fcb_oplock(fcb));
148c2c66affSColin Finck 
149c2c66affSColin Finck     InitializeListHead(&fcb->extents);
150c2c66affSColin Finck     InitializeListHead(&fcb->hardlinks);
151c2c66affSColin Finck     InitializeListHead(&fcb->xattrs);
152c2c66affSColin Finck 
153c2c66affSColin Finck     InitializeListHead(&fcb->dir_children_index);
154c2c66affSColin Finck     InitializeListHead(&fcb->dir_children_hash);
155c2c66affSColin Finck     InitializeListHead(&fcb->dir_children_hash_uc);
156c2c66affSColin Finck 
157c2c66affSColin Finck     return fcb;
158c2c66affSColin Finck }
159c2c66affSColin Finck 
create_fileref(device_extension * Vcb)160c2c66affSColin Finck file_ref* create_fileref(device_extension* Vcb) {
161c2c66affSColin Finck     file_ref* fr;
162c2c66affSColin Finck 
163c2c66affSColin Finck     fr = ExAllocateFromPagedLookasideList(&Vcb->fileref_lookaside);
164c2c66affSColin Finck     if (!fr) {
165c2c66affSColin Finck         ERR("out of memory\n");
166c2c66affSColin Finck         return NULL;
167c2c66affSColin Finck     }
168c2c66affSColin Finck 
169c2c66affSColin Finck     RtlZeroMemory(fr, sizeof(file_ref));
170c2c66affSColin Finck 
171c2c66affSColin Finck     fr->refcount = 1;
172c2c66affSColin Finck 
173c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
174c2c66affSColin Finck     WARN("fileref %p: refcount now 1\n", fr);
175c2c66affSColin Finck #endif
176c2c66affSColin Finck 
177c2c66affSColin Finck     InitializeListHead(&fr->children);
178c2c66affSColin Finck 
179c2c66affSColin Finck     return fr;
180c2c66affSColin Finck }
181c2c66affSColin Finck 
find_file_in_dir(PUNICODE_STRING filename,fcb * fcb,root ** subvol,uint64_t * inode,dir_child ** pdc,bool case_sensitive)182318da0c1SPierre Schweitzer NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, uint64_t* inode, dir_child** pdc, bool case_sensitive) {
183c2c66affSColin Finck     NTSTATUS Status;
184c2c66affSColin Finck     UNICODE_STRING fnus;
185318da0c1SPierre Schweitzer     uint32_t hash;
186c2c66affSColin Finck     LIST_ENTRY* le;
187318da0c1SPierre Schweitzer     uint8_t c;
188318da0c1SPierre Schweitzer     bool locked = false;
189c2c66affSColin Finck 
190c2c66affSColin Finck     if (!case_sensitive) {
191318da0c1SPierre Schweitzer         Status = RtlUpcaseUnicodeString(&fnus, filename, true);
192c2c66affSColin Finck 
193c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
194194ea909SVictor Perevertkin             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
195c2c66affSColin Finck             return Status;
196c2c66affSColin Finck         }
197c2c66affSColin Finck     } else
198c2c66affSColin Finck         fnus = *filename;
199c2c66affSColin Finck 
20098654b54SVincent Franchomme     Status = check_file_name_valid(filename, false, false);
20198654b54SVincent Franchomme     if (!NT_SUCCESS(Status))
20298654b54SVincent Franchomme         return Status;
20398654b54SVincent Franchomme 
204318da0c1SPierre Schweitzer     hash = calc_crc32c(0xffffffff, (uint8_t*)fnus.Buffer, fnus.Length);
205c2c66affSColin Finck 
206c2c66affSColin Finck     c = hash >> 24;
207c2c66affSColin Finck 
208c2c66affSColin Finck     if (!ExIsResourceAcquiredSharedLite(&fcb->nonpaged->dir_children_lock)) {
209318da0c1SPierre Schweitzer         ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, true);
210318da0c1SPierre Schweitzer         locked = true;
211c2c66affSColin Finck     }
212c2c66affSColin Finck 
213c2c66affSColin Finck     if (case_sensitive) {
214c2c66affSColin Finck         if (!fcb->hash_ptrs[c]) {
215c2c66affSColin Finck             Status = STATUS_OBJECT_NAME_NOT_FOUND;
216c2c66affSColin Finck             goto end;
217c2c66affSColin Finck         }
218c2c66affSColin Finck 
219c2c66affSColin Finck         le = fcb->hash_ptrs[c];
220c2c66affSColin Finck         while (le != &fcb->dir_children_hash) {
221c2c66affSColin Finck             dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
222c2c66affSColin Finck 
223c2c66affSColin Finck             if (dc->hash == hash) {
224c2c66affSColin Finck                 if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
225c2c66affSColin Finck                     if (dc->key.obj_type == TYPE_ROOT_ITEM) {
226c2c66affSColin Finck                         LIST_ENTRY* le2;
227c2c66affSColin Finck 
228c2c66affSColin Finck                         *subvol = NULL;
229c2c66affSColin Finck 
230c2c66affSColin Finck                         le2 = fcb->Vcb->roots.Flink;
231c2c66affSColin Finck                         while (le2 != &fcb->Vcb->roots) {
232c2c66affSColin Finck                             root* r2 = CONTAINING_RECORD(le2, root, list_entry);
233c2c66affSColin Finck 
234c2c66affSColin Finck                             if (r2->id == dc->key.obj_id) {
235c2c66affSColin Finck                                 *subvol = r2;
236c2c66affSColin Finck                                 break;
237c2c66affSColin Finck                             }
238c2c66affSColin Finck 
239c2c66affSColin Finck                             le2 = le2->Flink;
240c2c66affSColin Finck                         }
241c2c66affSColin Finck 
242c2c66affSColin Finck                         *inode = SUBVOL_ROOT_INODE;
243c2c66affSColin Finck                     } else {
244c2c66affSColin Finck                         *subvol = fcb->subvol;
245c2c66affSColin Finck                         *inode = dc->key.obj_id;
246c2c66affSColin Finck                     }
247c2c66affSColin Finck 
248c2c66affSColin Finck                     *pdc = dc;
249c2c66affSColin Finck 
250c2c66affSColin Finck                     Status = STATUS_SUCCESS;
251c2c66affSColin Finck                     goto end;
252c2c66affSColin Finck                 }
253c2c66affSColin Finck             } else if (dc->hash > hash) {
254c2c66affSColin Finck                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
255c2c66affSColin Finck                 goto end;
256c2c66affSColin Finck             }
257c2c66affSColin Finck 
258c2c66affSColin Finck             le = le->Flink;
259c2c66affSColin Finck         }
260c2c66affSColin Finck     } else {
261c2c66affSColin Finck         if (!fcb->hash_ptrs_uc[c]) {
262c2c66affSColin Finck             Status = STATUS_OBJECT_NAME_NOT_FOUND;
263c2c66affSColin Finck             goto end;
264c2c66affSColin Finck         }
265c2c66affSColin Finck 
266c2c66affSColin Finck         le = fcb->hash_ptrs_uc[c];
267c2c66affSColin Finck         while (le != &fcb->dir_children_hash_uc) {
268c2c66affSColin Finck             dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
269c2c66affSColin Finck 
270c2c66affSColin Finck             if (dc->hash_uc == hash) {
271c2c66affSColin Finck                 if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
272c2c66affSColin Finck                     if (dc->key.obj_type == TYPE_ROOT_ITEM) {
273c2c66affSColin Finck                         LIST_ENTRY* le2;
274c2c66affSColin Finck 
275c2c66affSColin Finck                         *subvol = NULL;
276c2c66affSColin Finck 
277c2c66affSColin Finck                         le2 = fcb->Vcb->roots.Flink;
278c2c66affSColin Finck                         while (le2 != &fcb->Vcb->roots) {
279c2c66affSColin Finck                             root* r2 = CONTAINING_RECORD(le2, root, list_entry);
280c2c66affSColin Finck 
281c2c66affSColin Finck                             if (r2->id == dc->key.obj_id) {
282c2c66affSColin Finck                                 *subvol = r2;
283c2c66affSColin Finck                                 break;
284c2c66affSColin Finck                             }
285c2c66affSColin Finck 
286c2c66affSColin Finck                             le2 = le2->Flink;
287c2c66affSColin Finck                         }
288c2c66affSColin Finck 
289c2c66affSColin Finck                         *inode = SUBVOL_ROOT_INODE;
290c2c66affSColin Finck                     } else {
291c2c66affSColin Finck                         *subvol = fcb->subvol;
292c2c66affSColin Finck                         *inode = dc->key.obj_id;
293c2c66affSColin Finck                     }
294c2c66affSColin Finck 
295c2c66affSColin Finck                     *pdc = dc;
296c2c66affSColin Finck 
297c2c66affSColin Finck                     Status = STATUS_SUCCESS;
298c2c66affSColin Finck                     goto end;
299c2c66affSColin Finck                 }
300c2c66affSColin Finck             } else if (dc->hash_uc > hash) {
301c2c66affSColin Finck                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
302c2c66affSColin Finck                 goto end;
303c2c66affSColin Finck             }
304c2c66affSColin Finck 
305c2c66affSColin Finck             le = le->Flink;
306c2c66affSColin Finck         }
307c2c66affSColin Finck     }
308c2c66affSColin Finck 
309c2c66affSColin Finck     Status = STATUS_OBJECT_NAME_NOT_FOUND;
310c2c66affSColin Finck 
311c2c66affSColin Finck end:
312c2c66affSColin Finck     if (locked)
313c2c66affSColin Finck         ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
314c2c66affSColin Finck 
315c2c66affSColin Finck     if (!case_sensitive)
316c2c66affSColin Finck         ExFreePool(fnus.Buffer);
317c2c66affSColin Finck 
318c2c66affSColin Finck     return Status;
319c2c66affSColin Finck }
320c2c66affSColin Finck 
split_path(device_extension * Vcb,PUNICODE_STRING path,LIST_ENTRY * parts,bool * stream)321318da0c1SPierre Schweitzer static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENTRY* parts, bool* stream) {
322c2c66affSColin Finck     ULONG len, i;
323318da0c1SPierre Schweitzer     bool has_stream;
324c2c66affSColin Finck     WCHAR* buf;
325c2c66affSColin Finck     name_bit* nb;
326883b1f31SPierre Schweitzer     NTSTATUS Status;
327c2c66affSColin Finck 
328c2c66affSColin Finck     len = path->Length / sizeof(WCHAR);
329c2c66affSColin Finck     if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
330c2c66affSColin Finck         len--;
331c2c66affSColin Finck 
332883b1f31SPierre Schweitzer     if (len == 0 || (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) {
333883b1f31SPierre Schweitzer         WARN("zero-length filename part\n");
334883b1f31SPierre Schweitzer         return STATUS_OBJECT_NAME_INVALID;
335883b1f31SPierre Schweitzer     }
336883b1f31SPierre Schweitzer 
337318da0c1SPierre Schweitzer     has_stream = false;
338c2c66affSColin Finck     for (i = 0; i < len; i++) {
339c2c66affSColin Finck         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
340318da0c1SPierre Schweitzer             has_stream = false;
341c2c66affSColin Finck         } else if (path->Buffer[i] == ':') {
342318da0c1SPierre Schweitzer             has_stream = true;
343c2c66affSColin Finck         }
344c2c66affSColin Finck     }
345c2c66affSColin Finck 
346c2c66affSColin Finck     buf = path->Buffer;
347c2c66affSColin Finck 
348c2c66affSColin Finck     for (i = 0; i < len; i++) {
349c2c66affSColin Finck         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
350883b1f31SPierre Schweitzer             if (buf[0] == '/' || buf[0] == '\\') {
351883b1f31SPierre Schweitzer                 WARN("zero-length filename part\n");
352883b1f31SPierre Schweitzer                 Status = STATUS_OBJECT_NAME_INVALID;
353883b1f31SPierre Schweitzer                 goto cleanup;
354883b1f31SPierre Schweitzer             }
355883b1f31SPierre Schweitzer 
356c2c66affSColin Finck             nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
357c2c66affSColin Finck             if (!nb) {
358c2c66affSColin Finck                 ERR("out of memory\n");
359883b1f31SPierre Schweitzer                 Status = STATUS_INSUFFICIENT_RESOURCES;
360883b1f31SPierre Schweitzer                 goto cleanup;
361c2c66affSColin Finck             }
362c2c66affSColin Finck 
363c2c66affSColin Finck             nb->us.Buffer = buf;
364c2c66affSColin Finck             nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
365c2c66affSColin Finck             InsertTailList(parts, &nb->list_entry);
366c2c66affSColin Finck 
367c2c66affSColin Finck             buf = &path->Buffer[i+1];
368c2c66affSColin Finck         }
369c2c66affSColin Finck     }
370c2c66affSColin Finck 
371c2c66affSColin Finck     nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
372c2c66affSColin Finck     if (!nb) {
373c2c66affSColin Finck         ERR("out of memory\n");
374883b1f31SPierre Schweitzer         Status = STATUS_INSUFFICIENT_RESOURCES;
375883b1f31SPierre Schweitzer         goto cleanup;
376c2c66affSColin Finck     }
377c2c66affSColin Finck 
378c2c66affSColin Finck     nb->us.Buffer = buf;
379c2c66affSColin Finck     nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
380c2c66affSColin Finck     InsertTailList(parts, &nb->list_entry);
381c2c66affSColin Finck 
382c2c66affSColin Finck     if (has_stream) {
383eb7fbc25SPierre Schweitzer         static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
384c2c66affSColin Finck         UNICODE_STRING dsus;
385c2c66affSColin Finck 
386eb7fbc25SPierre Schweitzer         dsus.Buffer = (WCHAR*)datasuf;
387eb7fbc25SPierre Schweitzer         dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
388c2c66affSColin Finck 
389c2c66affSColin Finck         for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) {
390c2c66affSColin Finck             if (nb->us.Buffer[i] == ':') {
391c2c66affSColin Finck                 name_bit* nb2;
392c2c66affSColin Finck 
39306042735SVincent Franchomme                 if (i + 1 == nb->us.Length / sizeof(WCHAR)) {
394883b1f31SPierre Schweitzer                     WARN("zero-length stream name\n");
395883b1f31SPierre Schweitzer                     Status = STATUS_OBJECT_NAME_INVALID;
396883b1f31SPierre Schweitzer                     goto cleanup;
397883b1f31SPierre Schweitzer                 }
398883b1f31SPierre Schweitzer 
399c2c66affSColin Finck                 nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
400c2c66affSColin Finck                 if (!nb2) {
401c2c66affSColin Finck                     ERR("out of memory\n");
402883b1f31SPierre Schweitzer                     Status = STATUS_INSUFFICIENT_RESOURCES;
403883b1f31SPierre Schweitzer                     goto cleanup;
404c2c66affSColin Finck                 }
405c2c66affSColin Finck 
406c2c66affSColin Finck                 nb2->us.Buffer = &nb->us.Buffer[i+1];
407318da0c1SPierre Schweitzer                 nb2->us.Length = nb2->us.MaximumLength = (uint16_t)(nb->us.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
408c2c66affSColin Finck                 InsertTailList(parts, &nb2->list_entry);
409c2c66affSColin Finck 
410318da0c1SPierre Schweitzer                 nb->us.Length = (uint16_t)i * sizeof(WCHAR);
411c2c66affSColin Finck                 nb->us.MaximumLength = nb->us.Length;
412c2c66affSColin Finck 
413c2c66affSColin Finck                 nb = nb2;
414c2c66affSColin Finck 
415c2c66affSColin Finck                 break;
416c2c66affSColin Finck             }
417c2c66affSColin Finck         }
418c2c66affSColin Finck 
419c2c66affSColin Finck         // FIXME - should comparison be case-insensitive?
420c2c66affSColin Finck         // remove :$DATA suffix
421c2c66affSColin Finck         if (nb->us.Length >= dsus.Length && RtlCompareMemory(&nb->us.Buffer[(nb->us.Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
422c2c66affSColin Finck             nb->us.Length -= dsus.Length;
423c2c66affSColin Finck 
424c2c66affSColin Finck         if (nb->us.Length == 0) {
425c2c66affSColin Finck             RemoveTailList(parts);
426c2c66affSColin Finck             ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
427c2c66affSColin Finck 
428318da0c1SPierre Schweitzer             has_stream = false;
429c2c66affSColin Finck         }
430c2c66affSColin Finck     }
431c2c66affSColin Finck 
432c2c66affSColin Finck     // if path is just stream name, remove first empty item
433c2c66affSColin Finck     if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
434c2c66affSColin Finck         name_bit *nb1 = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
435c2c66affSColin Finck 
436c2c66affSColin Finck         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1);
437c2c66affSColin Finck     }
438c2c66affSColin Finck 
439c2c66affSColin Finck     *stream = has_stream;
440c2c66affSColin Finck 
441c2c66affSColin Finck     return STATUS_SUCCESS;
442883b1f31SPierre Schweitzer 
443883b1f31SPierre Schweitzer cleanup:
444883b1f31SPierre Schweitzer     while (!IsListEmpty(parts)) {
445883b1f31SPierre Schweitzer         nb = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
446883b1f31SPierre Schweitzer 
447883b1f31SPierre Schweitzer         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
448883b1f31SPierre Schweitzer     }
449883b1f31SPierre Schweitzer 
450883b1f31SPierre Schweitzer     return Status;
451c2c66affSColin Finck }
452c2c66affSColin Finck 
453194ea909SVictor Perevertkin NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, void* csum, uint64_t start, uint64_t length, PIRP Irp) {
454c2c66affSColin Finck     NTSTATUS Status;
455c2c66affSColin Finck     KEY searchkey;
456c2c66affSColin Finck     traverse_ptr tp, next_tp;
457318da0c1SPierre Schweitzer     uint64_t i, j;
458318da0c1SPierre Schweitzer     bool b;
459194ea909SVictor Perevertkin     void* ptr = csum;
460c2c66affSColin Finck 
461c2c66affSColin Finck     searchkey.obj_id = EXTENT_CSUM_ID;
462c2c66affSColin Finck     searchkey.obj_type = TYPE_EXTENT_CSUM;
463c2c66affSColin Finck     searchkey.offset = start;
464c2c66affSColin Finck 
465318da0c1SPierre Schweitzer     Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp);
466c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
467194ea909SVictor Perevertkin         ERR("error - find_item returned %08lx\n", Status);
468c2c66affSColin Finck         return Status;
469c2c66affSColin Finck     }
470c2c66affSColin Finck 
471c2c66affSColin Finck     i = 0;
472c2c66affSColin Finck     do {
473c2c66affSColin Finck         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
474c2c66affSColin Finck             ULONG readlen;
475c2c66affSColin Finck 
476c2c66affSColin Finck             if (start < tp.item->key.offset)
477c2c66affSColin Finck                 j = 0;
478c2c66affSColin Finck             else
479174dfab6SVincent Franchomme                 j = ((start - tp.item->key.offset) >> Vcb->sector_shift) + i;
480c2c66affSColin Finck 
481174dfab6SVincent Franchomme             if (j * Vcb->csum_size > tp.item->size || tp.item->key.offset > start + (i << Vcb->sector_shift)) {
482174dfab6SVincent Franchomme                 ERR("checksum not found for %I64x\n", start + (i << Vcb->sector_shift));
483c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
484c2c66affSColin Finck             }
485c2c66affSColin Finck 
486194ea909SVictor Perevertkin             readlen = (ULONG)min((tp.item->size / Vcb->csum_size) - j, length - i);
487194ea909SVictor Perevertkin             RtlCopyMemory(ptr, tp.item->data + (j * Vcb->csum_size), readlen * Vcb->csum_size);
488194ea909SVictor Perevertkin 
489194ea909SVictor Perevertkin             ptr = (uint8_t*)ptr + (readlen * Vcb->csum_size);
490c2c66affSColin Finck             i += readlen;
491c2c66affSColin Finck 
492c2c66affSColin Finck             if (i == length)
493c2c66affSColin Finck                 break;
494c2c66affSColin Finck         }
495c2c66affSColin Finck 
496318da0c1SPierre Schweitzer         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
497c2c66affSColin Finck 
498c2c66affSColin Finck         if (b)
499c2c66affSColin Finck             tp = next_tp;
500c2c66affSColin Finck     } while (b);
501c2c66affSColin Finck 
502c2c66affSColin Finck     if (i < length) {
503318da0c1SPierre Schweitzer         ERR("could not read checksums: offset %I64x, length %I64x sectors\n", start, length);
504c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
505c2c66affSColin Finck     }
506c2c66affSColin Finck 
507c2c66affSColin Finck     return STATUS_SUCCESS;
508c2c66affSColin Finck }
509c2c66affSColin Finck 
510318da0c1SPierre Schweitzer NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, bool ignore_size, PIRP Irp) {
511c2c66affSColin Finck     KEY searchkey;
512c2c66affSColin Finck     traverse_ptr tp, next_tp;
513c2c66affSColin Finck     NTSTATUS Status;
514c2c66affSColin Finck     ULONG num_children = 0;
51562e630deSPierre Schweitzer     uint64_t max_index = 2;
516c2c66affSColin Finck 
517c2c66affSColin Finck     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
518c2c66affSColin Finck     if (!fcb->hash_ptrs) {
519c2c66affSColin Finck         ERR("out of memory\n");
520c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
521c2c66affSColin Finck     }
522c2c66affSColin Finck 
523c2c66affSColin Finck     RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
524c2c66affSColin Finck 
525c2c66affSColin Finck     fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
526c2c66affSColin Finck     if (!fcb->hash_ptrs_uc) {
527c2c66affSColin Finck         ERR("out of memory\n");
528c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
529c2c66affSColin Finck     }
530c2c66affSColin Finck 
531c2c66affSColin Finck     RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
532c2c66affSColin Finck 
533c2c66affSColin Finck     if (!ignore_size && fcb->inode_item.st_size == 0)
534c2c66affSColin Finck         return STATUS_SUCCESS;
535c2c66affSColin Finck 
536c2c66affSColin Finck     searchkey.obj_id = fcb->inode;
537c2c66affSColin Finck     searchkey.obj_type = TYPE_DIR_INDEX;
538c2c66affSColin Finck     searchkey.offset = 2;
539c2c66affSColin Finck 
540318da0c1SPierre Schweitzer     Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, false, Irp);
541c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
542194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
543c2c66affSColin Finck         return Status;
544c2c66affSColin Finck     }
545c2c66affSColin Finck 
546c2c66affSColin Finck     if (keycmp(tp.item->key, searchkey) == -1) {
547318da0c1SPierre Schweitzer         if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) {
548c2c66affSColin Finck             tp = next_tp;
549318da0c1SPierre Schweitzer             TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
550c2c66affSColin Finck         }
551c2c66affSColin Finck     }
552c2c66affSColin Finck 
553c2c66affSColin Finck     while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
554c2c66affSColin Finck         DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
555c2c66affSColin Finck         dir_child* dc;
556c2c66affSColin Finck         ULONG utf16len;
557c2c66affSColin Finck 
558c2c66affSColin Finck         if (tp.item->size < sizeof(DIR_ITEM)) {
559194ea909SVictor Perevertkin             WARN("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
560c2c66affSColin Finck             goto cont;
561c2c66affSColin Finck         }
562c2c66affSColin Finck 
563c2c66affSColin Finck         if (di->n == 0) {
564318da0c1SPierre Schweitzer             WARN("(%I64x,%x,%I64x): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
565c2c66affSColin Finck             goto cont;
566c2c66affSColin Finck         }
567c2c66affSColin Finck 
568318da0c1SPierre Schweitzer         Status = utf8_to_utf16(NULL, 0, &utf16len, di->name, di->n);
569c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
570194ea909SVictor Perevertkin             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
571c2c66affSColin Finck             goto cont;
572c2c66affSColin Finck         }
573c2c66affSColin Finck 
574c2c66affSColin Finck         dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
575c2c66affSColin Finck         if (!dc) {
576c2c66affSColin Finck             ERR("out of memory\n");
577c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
578c2c66affSColin Finck         }
579c2c66affSColin Finck 
580c2c66affSColin Finck         dc->key = di->key;
581c2c66affSColin Finck         dc->index = tp.item->key.offset;
582c2c66affSColin Finck         dc->type = di->type;
583c2c66affSColin Finck         dc->fileref = NULL;
58462e630deSPierre Schweitzer         dc->root_dir = false;
58562e630deSPierre Schweitzer 
58662e630deSPierre Schweitzer         max_index = dc->index;
587c2c66affSColin Finck 
588c2c66affSColin Finck         dc->utf8.MaximumLength = dc->utf8.Length = di->n;
589c2c66affSColin Finck         dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
590c2c66affSColin Finck         if (!dc->utf8.Buffer) {
591c2c66affSColin Finck             ERR("out of memory\n");
592c2c66affSColin Finck             ExFreePool(dc);
593c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
594c2c66affSColin Finck         }
595c2c66affSColin Finck 
596c2c66affSColin Finck         RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
597c2c66affSColin Finck 
598318da0c1SPierre Schweitzer         dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len;
599c2c66affSColin Finck         dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
600c2c66affSColin Finck         if (!dc->name.Buffer) {
601c2c66affSColin Finck             ERR("out of memory\n");
602c2c66affSColin Finck             ExFreePool(dc->utf8.Buffer);
603c2c66affSColin Finck             ExFreePool(dc);
604c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
605c2c66affSColin Finck         }
606c2c66affSColin Finck 
607318da0c1SPierre Schweitzer         Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
608c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
609194ea909SVictor Perevertkin             ERR("utf8_to_utf16 2 returned %08lx\n", Status);
610c2c66affSColin Finck             ExFreePool(dc->utf8.Buffer);
611c2c66affSColin Finck             ExFreePool(dc->name.Buffer);
612c2c66affSColin Finck             ExFreePool(dc);
613c2c66affSColin Finck             goto cont;
614c2c66affSColin Finck         }
615c2c66affSColin Finck 
616318da0c1SPierre Schweitzer         Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
617c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
618194ea909SVictor Perevertkin             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
619c2c66affSColin Finck             ExFreePool(dc->utf8.Buffer);
620c2c66affSColin Finck             ExFreePool(dc->name.Buffer);
621c2c66affSColin Finck             ExFreePool(dc);
622c2c66affSColin Finck             goto cont;
623c2c66affSColin Finck         }
624c2c66affSColin Finck 
625318da0c1SPierre Schweitzer         dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
626318da0c1SPierre Schweitzer         dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
627c2c66affSColin Finck 
628c2c66affSColin Finck         InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
629c2c66affSColin Finck 
630c2c66affSColin Finck         insert_dir_child_into_hash_lists(fcb, dc);
631c2c66affSColin Finck 
632c2c66affSColin Finck         num_children++;
633c2c66affSColin Finck 
634c2c66affSColin Finck cont:
635318da0c1SPierre Schweitzer         if (find_next_item(Vcb, &tp, &next_tp, false, Irp))
636c2c66affSColin Finck             tp = next_tp;
637c2c66affSColin Finck         else
638c2c66affSColin Finck             break;
639c2c66affSColin Finck     }
640c2c66affSColin Finck 
64162e630deSPierre Schweitzer     if (!Vcb->options.no_root_dir && fcb->inode == SUBVOL_ROOT_INODE) {
64262e630deSPierre Schweitzer         root* top_subvol;
64362e630deSPierre Schweitzer 
64462e630deSPierre Schweitzer         if (Vcb->root_fileref && Vcb->root_fileref->fcb)
64562e630deSPierre Schweitzer             top_subvol = Vcb->root_fileref->fcb->subvol;
64662e630deSPierre Schweitzer         else
64762e630deSPierre Schweitzer             top_subvol = find_default_subvol(Vcb, NULL);
64862e630deSPierre Schweitzer 
64962e630deSPierre Schweitzer         if (fcb->subvol == top_subvol && top_subvol->id != BTRFS_ROOT_FSTREE) {
65062e630deSPierre Schweitzer             dir_child* dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
65162e630deSPierre Schweitzer             if (!dc) {
65262e630deSPierre Schweitzer                 ERR("out of memory\n");
65362e630deSPierre Schweitzer                 return STATUS_INSUFFICIENT_RESOURCES;
65462e630deSPierre Schweitzer             }
65562e630deSPierre Schweitzer 
65662e630deSPierre Schweitzer             dc->key.obj_id = BTRFS_ROOT_FSTREE;
65762e630deSPierre Schweitzer             dc->key.obj_type = TYPE_ROOT_ITEM;
65862e630deSPierre Schweitzer             dc->key.offset = 0;
65962e630deSPierre Schweitzer             dc->index = max_index + 1;
66062e630deSPierre Schweitzer             dc->type = BTRFS_TYPE_DIRECTORY;
66162e630deSPierre Schweitzer             dc->fileref = NULL;
66262e630deSPierre Schweitzer             dc->root_dir = true;
66362e630deSPierre Schweitzer 
66462e630deSPierre Schweitzer             dc->utf8.MaximumLength = dc->utf8.Length = sizeof(root_dir) - sizeof(char);
66562e630deSPierre Schweitzer             dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir) - sizeof(char), ALLOC_TAG);
66662e630deSPierre Schweitzer             if (!dc->utf8.Buffer) {
66762e630deSPierre Schweitzer                 ERR("out of memory\n");
66862e630deSPierre Schweitzer                 ExFreePool(dc);
66962e630deSPierre Schweitzer                 return STATUS_INSUFFICIENT_RESOURCES;
67062e630deSPierre Schweitzer             }
67162e630deSPierre Schweitzer 
67262e630deSPierre Schweitzer             RtlCopyMemory(dc->utf8.Buffer, root_dir, sizeof(root_dir) - sizeof(char));
67362e630deSPierre Schweitzer 
67462e630deSPierre Schweitzer             dc->name.MaximumLength = dc->name.Length = sizeof(root_dir_utf16) - sizeof(WCHAR);
67562e630deSPierre Schweitzer             dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir_utf16) - sizeof(WCHAR), ALLOC_TAG);
67662e630deSPierre Schweitzer             if (!dc->name.Buffer) {
67762e630deSPierre Schweitzer                 ERR("out of memory\n");
67862e630deSPierre Schweitzer                 ExFreePool(dc->utf8.Buffer);
67962e630deSPierre Schweitzer                 ExFreePool(dc);
68062e630deSPierre Schweitzer                 return STATUS_INSUFFICIENT_RESOURCES;
68162e630deSPierre Schweitzer             }
68262e630deSPierre Schweitzer 
68362e630deSPierre Schweitzer             RtlCopyMemory(dc->name.Buffer, root_dir_utf16, sizeof(root_dir_utf16) - sizeof(WCHAR));
68462e630deSPierre Schweitzer 
68562e630deSPierre Schweitzer             Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
68662e630deSPierre Schweitzer             if (!NT_SUCCESS(Status)) {
687194ea909SVictor Perevertkin                 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
68862e630deSPierre Schweitzer                 ExFreePool(dc->utf8.Buffer);
68962e630deSPierre Schweitzer                 ExFreePool(dc->name.Buffer);
69062e630deSPierre Schweitzer                 ExFreePool(dc);
69162e630deSPierre Schweitzer                 goto cont;
69262e630deSPierre Schweitzer             }
69362e630deSPierre Schweitzer 
69462e630deSPierre Schweitzer             dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
69562e630deSPierre Schweitzer             dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
69662e630deSPierre Schweitzer 
69762e630deSPierre Schweitzer             InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
69862e630deSPierre Schweitzer 
69962e630deSPierre Schweitzer             insert_dir_child_into_hash_lists(fcb, dc);
70062e630deSPierre Schweitzer         }
70162e630deSPierre Schweitzer     }
70262e630deSPierre Schweitzer 
703c2c66affSColin Finck     return STATUS_SUCCESS;
704c2c66affSColin Finck }
705c2c66affSColin Finck 
706c2c66affSColin Finck NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
707318da0c1SPierre Schweitzer                   root* subvol, uint64_t inode, uint8_t type, PANSI_STRING utf8, bool always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
708c2c66affSColin Finck     KEY searchkey;
709c2c66affSColin Finck     traverse_ptr tp, next_tp;
710c2c66affSColin Finck     NTSTATUS Status;
711c2c66affSColin Finck     fcb *fcb, *deleted_fcb = NULL;
712318da0c1SPierre Schweitzer     bool atts_set = false, sd_set = false, no_data;
713c2c66affSColin Finck     LIST_ENTRY* lastle = NULL;
714c2c66affSColin Finck     EXTENT_DATA* ed = NULL;
71506042735SVincent Franchomme     uint64_t fcbs_version = 0;
716318da0c1SPierre Schweitzer     uint32_t hash;
717c2c66affSColin Finck 
718318da0c1SPierre Schweitzer     hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
719883b1f31SPierre Schweitzer 
720883b1f31SPierre Schweitzer     acquire_fcb_lock_shared(Vcb);
721883b1f31SPierre Schweitzer 
722883b1f31SPierre Schweitzer     if (subvol->fcbs_ptrs[hash >> 24]) {
723883b1f31SPierre Schweitzer         LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
724c2c66affSColin Finck 
725c2c66affSColin Finck         while (le != &subvol->fcbs) {
726c2c66affSColin Finck             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
727c2c66affSColin Finck 
728c2c66affSColin Finck             if (fcb->inode == inode) {
729c2c66affSColin Finck                 if (!fcb->ads) {
730c2c66affSColin Finck                     if (fcb->deleted)
731c2c66affSColin Finck                         deleted_fcb = fcb;
732c2c66affSColin Finck                     else {
733c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
734c2c66affSColin Finck                         LONG rc = InterlockedIncrement(&fcb->refcount);
735c2c66affSColin Finck 
736318da0c1SPierre Schweitzer                         WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb, rc, fcb->subvol->id, fcb->inode);
737c2c66affSColin Finck #else
738c2c66affSColin Finck                         InterlockedIncrement(&fcb->refcount);
739c2c66affSColin Finck #endif
740c2c66affSColin Finck 
741c2c66affSColin Finck                         *pfcb = fcb;
742883b1f31SPierre Schweitzer                         release_fcb_lock(Vcb);
743c2c66affSColin Finck                         return STATUS_SUCCESS;
744c2c66affSColin Finck                     }
745c2c66affSColin Finck                 }
746883b1f31SPierre Schweitzer             } else if (fcb->hash > hash) {
747c2c66affSColin Finck                 if (deleted_fcb) {
748c2c66affSColin Finck                     InterlockedIncrement(&deleted_fcb->refcount);
749c2c66affSColin Finck                     *pfcb = deleted_fcb;
750883b1f31SPierre Schweitzer                     release_fcb_lock(Vcb);
751c2c66affSColin Finck                     return STATUS_SUCCESS;
752c2c66affSColin Finck                 }
753c2c66affSColin Finck 
754c2c66affSColin Finck                 lastle = le->Blink;
755883b1f31SPierre Schweitzer                 fcbs_version = subvol->fcbs_version;
756883b1f31SPierre Schweitzer 
757c2c66affSColin Finck                 break;
758c2c66affSColin Finck             }
759c2c66affSColin Finck 
760c2c66affSColin Finck             le = le->Flink;
761c2c66affSColin Finck         }
762c2c66affSColin Finck     }
763c2c66affSColin Finck 
764883b1f31SPierre Schweitzer     release_fcb_lock(Vcb);
765883b1f31SPierre Schweitzer 
766c2c66affSColin Finck     if (deleted_fcb) {
767c2c66affSColin Finck         InterlockedIncrement(&deleted_fcb->refcount);
768c2c66affSColin Finck         *pfcb = deleted_fcb;
769c2c66affSColin Finck         return STATUS_SUCCESS;
770c2c66affSColin Finck     }
771c2c66affSColin Finck 
772c2c66affSColin Finck     fcb = create_fcb(Vcb, pooltype);
773c2c66affSColin Finck     if (!fcb) {
774c2c66affSColin Finck         ERR("out of memory\n");
775c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
776c2c66affSColin Finck     }
777c2c66affSColin Finck 
778c2c66affSColin Finck     fcb->Vcb = Vcb;
779c2c66affSColin Finck 
780c2c66affSColin Finck     fcb->subvol = subvol;
781c2c66affSColin Finck     fcb->inode = inode;
782883b1f31SPierre Schweitzer     fcb->hash = hash;
783c2c66affSColin Finck     fcb->type = type;
784c2c66affSColin Finck 
785c2c66affSColin Finck     searchkey.obj_id = inode;
786c2c66affSColin Finck     searchkey.obj_type = TYPE_INODE_ITEM;
787c2c66affSColin Finck     searchkey.offset = 0xffffffffffffffff;
788c2c66affSColin Finck 
789318da0c1SPierre Schweitzer     Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
790c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
791194ea909SVictor Perevertkin         ERR("error - find_item returned %08lx\n", Status);
792883b1f31SPierre Schweitzer         reap_fcb(fcb);
793c2c66affSColin Finck         return Status;
794c2c66affSColin Finck     }
795c2c66affSColin Finck 
796c2c66affSColin Finck     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
797318da0c1SPierre Schweitzer         WARN("couldn't find INODE_ITEM for inode %I64x in subvol %I64x\n", inode, subvol->id);
798883b1f31SPierre Schweitzer         reap_fcb(fcb);
799c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
800c2c66affSColin Finck     }
801c2c66affSColin Finck 
802c2c66affSColin Finck     if (tp.item->size > 0)
803c2c66affSColin Finck         RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
804c2c66affSColin Finck 
805c2c66affSColin Finck     if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
806c2c66affSColin Finck         if ((fcb->inode_item.st_mode & __S_IFDIR) == __S_IFDIR)
807c2c66affSColin Finck             fcb->type = BTRFS_TYPE_DIRECTORY;
808c2c66affSColin Finck         else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
809c2c66affSColin Finck             fcb->type = BTRFS_TYPE_CHARDEV;
810c2c66affSColin Finck         else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
811c2c66affSColin Finck             fcb->type = BTRFS_TYPE_BLOCKDEV;
812c2c66affSColin Finck         else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
813c2c66affSColin Finck             fcb->type = BTRFS_TYPE_FIFO;
814c2c66affSColin Finck         else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK)
815c2c66affSColin Finck             fcb->type = BTRFS_TYPE_SYMLINK;
816c2c66affSColin Finck         else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
817c2c66affSColin Finck             fcb->type = BTRFS_TYPE_SOCKET;
818c2c66affSColin Finck         else
819c2c66affSColin Finck             fcb->type = BTRFS_TYPE_FILE;
820c2c66affSColin Finck     }
821c2c66affSColin Finck 
822c2c66affSColin Finck     no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
823c2c66affSColin Finck 
824318da0c1SPierre Schweitzer     while (find_next_item(Vcb, &tp, &next_tp, false, Irp)) {
825c2c66affSColin Finck         tp = next_tp;
826c2c66affSColin Finck 
827c2c66affSColin Finck         if (tp.item->key.obj_id > inode)
828c2c66affSColin Finck             break;
829c2c66affSColin Finck 
830c2c66affSColin Finck         if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
831c2c66affSColin Finck             break;
832c2c66affSColin Finck 
833f381137cSPierre Schweitzer         if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_REF) {
834c2c66affSColin Finck             ULONG len;
835c2c66affSColin Finck             INODE_REF* ir;
836c2c66affSColin Finck 
837c2c66affSColin Finck             len = tp.item->size;
838c2c66affSColin Finck             ir = (INODE_REF*)tp.item->data;
839c2c66affSColin Finck 
840c2c66affSColin Finck             while (len >= sizeof(INODE_REF) - 1) {
841c2c66affSColin Finck                 hardlink* hl;
842c2c66affSColin Finck                 ULONG stringlen;
843c2c66affSColin Finck 
844c2c66affSColin Finck                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
845c2c66affSColin Finck                 if (!hl) {
846c2c66affSColin Finck                     ERR("out of memory\n");
847883b1f31SPierre Schweitzer                     reap_fcb(fcb);
848c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
849c2c66affSColin Finck                 }
850c2c66affSColin Finck 
851c2c66affSColin Finck                 hl->parent = tp.item->key.offset;
852c2c66affSColin Finck                 hl->index = ir->index;
853c2c66affSColin Finck 
854c2c66affSColin Finck                 hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
855c2c66affSColin Finck 
856c2c66affSColin Finck                 if (hl->utf8.Length > 0) {
857c2c66affSColin Finck                     hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
858c2c66affSColin Finck                     RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
859c2c66affSColin Finck                 }
860c2c66affSColin Finck 
861318da0c1SPierre Schweitzer                 Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n);
862c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
863194ea909SVictor Perevertkin                     ERR("utf8_to_utf16 1 returned %08lx\n", Status);
864c2c66affSColin Finck                     ExFreePool(hl);
865883b1f31SPierre Schweitzer                     reap_fcb(fcb);
866c2c66affSColin Finck                     return Status;
867c2c66affSColin Finck                 }
868c2c66affSColin Finck 
869318da0c1SPierre Schweitzer                 hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
870c2c66affSColin Finck 
871c2c66affSColin Finck                 if (stringlen == 0)
872c2c66affSColin Finck                     hl->name.Buffer = NULL;
873c2c66affSColin Finck                 else {
874c2c66affSColin Finck                     hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
875c2c66affSColin Finck 
876c2c66affSColin Finck                     if (!hl->name.Buffer) {
877c2c66affSColin Finck                         ERR("out of memory\n");
878c2c66affSColin Finck                         ExFreePool(hl);
879883b1f31SPierre Schweitzer                         reap_fcb(fcb);
880c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
881c2c66affSColin Finck                     }
882c2c66affSColin Finck 
883318da0c1SPierre Schweitzer                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
884c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
885194ea909SVictor Perevertkin                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
886c2c66affSColin Finck                         ExFreePool(hl->name.Buffer);
887c2c66affSColin Finck                         ExFreePool(hl);
888883b1f31SPierre Schweitzer                         reap_fcb(fcb);
889c2c66affSColin Finck                         return Status;
890c2c66affSColin Finck                     }
891c2c66affSColin Finck                 }
892c2c66affSColin Finck 
893c2c66affSColin Finck                 InsertTailList(&fcb->hardlinks, &hl->list_entry);
894c2c66affSColin Finck 
895c2c66affSColin Finck                 len -= sizeof(INODE_REF) - 1 + ir->n;
896c2c66affSColin Finck                 ir = (INODE_REF*)&ir->name[ir->n];
897c2c66affSColin Finck             }
898f381137cSPierre Schweitzer         } else if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
899c2c66affSColin Finck             ULONG len;
900c2c66affSColin Finck             INODE_EXTREF* ier;
901c2c66affSColin Finck 
902c2c66affSColin Finck             len = tp.item->size;
903c2c66affSColin Finck             ier = (INODE_EXTREF*)tp.item->data;
904c2c66affSColin Finck 
905c2c66affSColin Finck             while (len >= sizeof(INODE_EXTREF) - 1) {
906c2c66affSColin Finck                 hardlink* hl;
907c2c66affSColin Finck                 ULONG stringlen;
908c2c66affSColin Finck 
909c2c66affSColin Finck                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
910c2c66affSColin Finck                 if (!hl) {
911c2c66affSColin Finck                     ERR("out of memory\n");
912883b1f31SPierre Schweitzer                     reap_fcb(fcb);
913c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
914c2c66affSColin Finck                 }
915c2c66affSColin Finck 
916c2c66affSColin Finck                 hl->parent = ier->dir;
917c2c66affSColin Finck                 hl->index = ier->index;
918c2c66affSColin Finck 
919c2c66affSColin Finck                 hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
920c2c66affSColin Finck 
921c2c66affSColin Finck                 if (hl->utf8.Length > 0) {
922c2c66affSColin Finck                     hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
923c2c66affSColin Finck                     RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
924c2c66affSColin Finck                 }
925c2c66affSColin Finck 
926318da0c1SPierre Schweitzer                 Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n);
927c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
928194ea909SVictor Perevertkin                     ERR("utf8_to_utf16 1 returned %08lx\n", Status);
929c2c66affSColin Finck                     ExFreePool(hl);
930883b1f31SPierre Schweitzer                     reap_fcb(fcb);
931c2c66affSColin Finck                     return Status;
932c2c66affSColin Finck                 }
933c2c66affSColin Finck 
934318da0c1SPierre Schweitzer                 hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
935c2c66affSColin Finck 
936c2c66affSColin Finck                 if (stringlen == 0)
937c2c66affSColin Finck                     hl->name.Buffer = NULL;
938c2c66affSColin Finck                 else {
939c2c66affSColin Finck                     hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
940c2c66affSColin Finck 
941c2c66affSColin Finck                     if (!hl->name.Buffer) {
942c2c66affSColin Finck                         ERR("out of memory\n");
943c2c66affSColin Finck                         ExFreePool(hl);
944883b1f31SPierre Schweitzer                         reap_fcb(fcb);
945c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
946c2c66affSColin Finck                     }
947c2c66affSColin Finck 
948318da0c1SPierre Schweitzer                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
949c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
950194ea909SVictor Perevertkin                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
951c2c66affSColin Finck                         ExFreePool(hl->name.Buffer);
952c2c66affSColin Finck                         ExFreePool(hl);
953883b1f31SPierre Schweitzer                         reap_fcb(fcb);
954c2c66affSColin Finck                         return Status;
955c2c66affSColin Finck                     }
956c2c66affSColin Finck                 }
957c2c66affSColin Finck 
958c2c66affSColin Finck                 InsertTailList(&fcb->hardlinks, &hl->list_entry);
959c2c66affSColin Finck 
960c2c66affSColin Finck                 len -= sizeof(INODE_EXTREF) - 1 + ier->n;
961c2c66affSColin Finck                 ier = (INODE_EXTREF*)&ier->name[ier->n];
962c2c66affSColin Finck             }
963c2c66affSColin Finck         } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
964c2c66affSColin Finck             ULONG len;
965c2c66affSColin Finck             DIR_ITEM* di;
966c2c66affSColin Finck 
967eb7fbc25SPierre Schweitzer             static const char xapref[] = "user.";
968c2c66affSColin Finck 
969c2c66affSColin Finck             if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
970194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0]));
971c2c66affSColin Finck                 continue;
972c2c66affSColin Finck             }
973c2c66affSColin Finck 
974c2c66affSColin Finck             len = tp.item->size;
975c2c66affSColin Finck             di = (DIR_ITEM*)tp.item->data;
976c2c66affSColin Finck 
977c2c66affSColin Finck             do {
978c2c66affSColin Finck                 if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n)
979c2c66affSColin Finck                     break;
980c2c66affSColin Finck 
981eb7fbc25SPierre Schweitzer                 if (tp.item->key.offset == EA_REPARSE_HASH && di->n == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(EA_REPARSE, di->name, di->n) == di->n) {
982c2c66affSColin Finck                     if (di->m > 0) {
983c2c66affSColin Finck                         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
984c2c66affSColin Finck                         if (!fcb->reparse_xattr.Buffer) {
985c2c66affSColin Finck                             ERR("out of memory\n");
986883b1f31SPierre Schweitzer                             reap_fcb(fcb);
987c2c66affSColin Finck                             return STATUS_INSUFFICIENT_RESOURCES;
988c2c66affSColin Finck                         }
989c2c66affSColin Finck 
990c2c66affSColin Finck                         RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m);
991c2c66affSColin Finck                     } else
992c2c66affSColin Finck                         fcb->reparse_xattr.Buffer = NULL;
993c2c66affSColin Finck 
994c2c66affSColin Finck                     fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = di->m;
995eb7fbc25SPierre Schweitzer                 } else if (tp.item->key.offset == EA_EA_HASH && di->n == sizeof(EA_EA) - 1 && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
996c2c66affSColin Finck                     if (di->m > 0) {
997c2c66affSColin Finck                         ULONG offset;
998c2c66affSColin Finck 
999c2c66affSColin Finck                         Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
1000c2c66affSColin Finck 
1001c2c66affSColin Finck                         if (!NT_SUCCESS(Status))
1002194ea909SVictor Perevertkin                             WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
1003c2c66affSColin Finck                         else {
1004c2c66affSColin Finck                             FILE_FULL_EA_INFORMATION* eainfo;
1005c2c66affSColin Finck 
1006c2c66affSColin Finck                             fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
1007c2c66affSColin Finck                             if (!fcb->ea_xattr.Buffer) {
1008c2c66affSColin Finck                                 ERR("out of memory\n");
1009883b1f31SPierre Schweitzer                                 reap_fcb(fcb);
1010c2c66affSColin Finck                                 return STATUS_INSUFFICIENT_RESOURCES;
1011c2c66affSColin Finck                             }
1012c2c66affSColin Finck 
1013c2c66affSColin Finck                             RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
1014c2c66affSColin Finck 
1015c2c66affSColin Finck                             fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = di->m;
1016c2c66affSColin Finck 
1017c2c66affSColin Finck                             fcb->ealen = 4;
1018c2c66affSColin Finck 
1019c2c66affSColin Finck                             // calculate ealen
1020c2c66affSColin Finck                             eainfo = (FILE_FULL_EA_INFORMATION*)&di->name[di->n];
1021c2c66affSColin Finck                             do {
1022c2c66affSColin Finck                                 fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
1023c2c66affSColin Finck 
1024c2c66affSColin Finck                                 if (eainfo->NextEntryOffset == 0)
1025c2c66affSColin Finck                                     break;
1026c2c66affSColin Finck 
1027318da0c1SPierre Schweitzer                                 eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset);
1028318da0c1SPierre Schweitzer                             } while (true);
1029c2c66affSColin Finck                         }
1030c2c66affSColin Finck                     }
1031eb7fbc25SPierre Schweitzer                 } else if (tp.item->key.offset == EA_DOSATTRIB_HASH && di->n == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(EA_DOSATTRIB, di->name, di->n) == di->n) {
1032c2c66affSColin Finck                     if (di->m > 0) {
1033c2c66affSColin Finck                         if (get_file_attributes_from_xattr(&di->name[di->n], di->m, &fcb->atts)) {
1034318da0c1SPierre Schweitzer                             atts_set = true;
1035c2c66affSColin Finck 
1036c2c66affSColin Finck                             if (fcb->type == BTRFS_TYPE_DIRECTORY)
1037c2c66affSColin Finck                                 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
1038c2c66affSColin Finck                             else if (fcb->type == BTRFS_TYPE_SYMLINK)
1039c2c66affSColin Finck                                 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
1040c2c66affSColin Finck 
1041c2c66affSColin Finck                             if (fcb->type != BTRFS_TYPE_DIRECTORY)
1042c2c66affSColin Finck                                 fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
1043c2c66affSColin Finck 
1044c2c66affSColin Finck                             if (inode == SUBVOL_ROOT_INODE) {
1045c2c66affSColin Finck                                 if (subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
1046c2c66affSColin Finck                                     fcb->atts |= FILE_ATTRIBUTE_READONLY;
1047c2c66affSColin Finck                                 else
1048c2c66affSColin Finck                                     fcb->atts &= ~FILE_ATTRIBUTE_READONLY;
1049c2c66affSColin Finck                             }
1050c2c66affSColin Finck                         }
1051c2c66affSColin Finck                     }
1052eb7fbc25SPierre Schweitzer                 } else if (tp.item->key.offset == EA_NTACL_HASH && di->n == sizeof(EA_NTACL) - 1 && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n) {
1053c2c66affSColin Finck                     if (di->m > 0) {
1054c2c66affSColin Finck                         fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
1055c2c66affSColin Finck                         if (!fcb->sd) {
1056c2c66affSColin Finck                             ERR("out of memory\n");
1057883b1f31SPierre Schweitzer                             reap_fcb(fcb);
1058c2c66affSColin Finck                             return STATUS_INSUFFICIENT_RESOURCES;
1059c2c66affSColin Finck                         }
1060c2c66affSColin Finck 
1061c2c66affSColin Finck                         RtlCopyMemory(fcb->sd, &di->name[di->n], di->m);
1062c2c66affSColin Finck 
1063c2c66affSColin Finck                         // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
1064c2c66affSColin Finck                         // will fail if the ACLs aren't 32-bit aligned.
1065c2c66affSColin Finck                         if (!RtlValidRelativeSecurityDescriptor(fcb->sd, di->m, 0))
1066c2c66affSColin Finck                             ExFreePool(fcb->sd);
1067c2c66affSColin Finck                         else
1068318da0c1SPierre Schweitzer                             sd_set = true;
1069c2c66affSColin Finck                     }
1070eb7fbc25SPierre Schweitzer                 } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH && di->n == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) {
1071c2c66affSColin Finck                     if (di->m > 0) {
1072eb7fbc25SPierre Schweitzer                         static const char lzo[] = "lzo";
1073eb7fbc25SPierre Schweitzer                         static const char zlib[] = "zlib";
1074eb7fbc25SPierre Schweitzer                         static const char zstd[] = "zstd";
1075c2c66affSColin Finck 
1076eb7fbc25SPierre Schweitzer                         if (di->m == sizeof(lzo) - 1 && RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
1077c2c66affSColin Finck                             fcb->prop_compression = PropCompression_LZO;
1078eb7fbc25SPierre Schweitzer                         else if (di->m == sizeof(zlib) - 1 && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
1079c2c66affSColin Finck                             fcb->prop_compression = PropCompression_Zlib;
1080eb7fbc25SPierre Schweitzer                         else if (di->m == sizeof(zstd) - 1 && RtlCompareMemory(&di->name[di->n], zstd, di->m) == di->m)
1081eb7fbc25SPierre Schweitzer                             fcb->prop_compression = PropCompression_ZSTD;
1082c2c66affSColin Finck                         else
1083c2c66affSColin Finck                             fcb->prop_compression = PropCompression_None;
1084c2c66affSColin Finck                     }
1085f381137cSPierre Schweitzer                 } else if (tp.item->key.offset == EA_CASE_SENSITIVE_HASH && di->n == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(EA_CASE_SENSITIVE, di->name, di->n) == di->n) {
1086f381137cSPierre Schweitzer                     if (di->m > 0) {
1087f381137cSPierre Schweitzer                         fcb->case_sensitive = di->m == 1 && di->name[di->n] == '1';
1088318da0c1SPierre Schweitzer                         fcb->case_sensitive_set = true;
1089f381137cSPierre Schweitzer                     }
1090eb7fbc25SPierre Schweitzer                 } else if (di->n > sizeof(xapref) - 1 && RtlCompareMemory(xapref, di->name, sizeof(xapref) - 1) == sizeof(xapref) - 1) {
1091c2c66affSColin Finck                     dir_child* dc;
1092c2c66affSColin Finck                     ULONG utf16len;
1093c2c66affSColin Finck 
1094318da0c1SPierre Schweitzer                     Status = utf8_to_utf16(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
1095c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
1096194ea909SVictor Perevertkin                         ERR("utf8_to_utf16 1 returned %08lx\n", Status);
1097883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1098c2c66affSColin Finck                         return Status;
1099c2c66affSColin Finck                     }
1100c2c66affSColin Finck 
1101c2c66affSColin Finck                     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
1102c2c66affSColin Finck                     if (!dc) {
1103c2c66affSColin Finck                         ERR("out of memory\n");
1104883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1105c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
1106c2c66affSColin Finck                     }
1107c2c66affSColin Finck 
1108c2c66affSColin Finck                     RtlZeroMemory(dc, sizeof(dir_child));
1109c2c66affSColin Finck 
1110eb7fbc25SPierre Schweitzer                     dc->utf8.MaximumLength = dc->utf8.Length = di->n + 1 - sizeof(xapref);
1111c2c66affSColin Finck                     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
1112c2c66affSColin Finck                     if (!dc->utf8.Buffer) {
1113c2c66affSColin Finck                         ERR("out of memory\n");
1114c2c66affSColin Finck                         ExFreePool(dc);
1115883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1116c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
1117c2c66affSColin Finck                     }
1118c2c66affSColin Finck 
1119eb7fbc25SPierre Schweitzer                     RtlCopyMemory(dc->utf8.Buffer, &di->name[sizeof(xapref) - 1], dc->utf8.Length);
1120c2c66affSColin Finck 
1121318da0c1SPierre Schweitzer                     dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len;
1122c2c66affSColin Finck                     dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
1123c2c66affSColin Finck                     if (!dc->name.Buffer) {
1124c2c66affSColin Finck                         ERR("out of memory\n");
1125c2c66affSColin Finck                         ExFreePool(dc->utf8.Buffer);
1126c2c66affSColin Finck                         ExFreePool(dc);
1127883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1128c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
1129c2c66affSColin Finck                     }
1130c2c66affSColin Finck 
1131318da0c1SPierre Schweitzer                     Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length);
1132c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
1133194ea909SVictor Perevertkin                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
1134c2c66affSColin Finck                         ExFreePool(dc->utf8.Buffer);
1135c2c66affSColin Finck                         ExFreePool(dc->name.Buffer);
1136c2c66affSColin Finck                         ExFreePool(dc);
1137883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1138c2c66affSColin Finck                         return Status;
1139c2c66affSColin Finck                     }
1140c2c66affSColin Finck 
1141318da0c1SPierre Schweitzer                     Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
1142c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
1143194ea909SVictor Perevertkin                         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1144c2c66affSColin Finck                         ExFreePool(dc->utf8.Buffer);
1145c2c66affSColin Finck                         ExFreePool(dc->name.Buffer);
1146c2c66affSColin Finck                         ExFreePool(dc);
1147883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1148c2c66affSColin Finck                         return Status;
1149c2c66affSColin Finck                     }
1150c2c66affSColin Finck 
1151c2c66affSColin Finck                     dc->size = di->m;
1152c2c66affSColin Finck 
1153c2c66affSColin Finck                     InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
1154c2c66affSColin Finck                 } else {
1155c2c66affSColin Finck                     xattr* xa;
1156c2c66affSColin Finck 
1157c2c66affSColin Finck                     xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG);
1158c2c66affSColin Finck                     if (!xa) {
1159c2c66affSColin Finck                         ERR("out of memory\n");
1160883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1161c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
1162c2c66affSColin Finck                     }
1163c2c66affSColin Finck 
1164c2c66affSColin Finck                     xa->namelen = di->n;
1165c2c66affSColin Finck                     xa->valuelen = di->m;
1166318da0c1SPierre Schweitzer                     xa->dirty = false;
1167c2c66affSColin Finck                     RtlCopyMemory(xa->data, di->name, di->m + di->n);
1168c2c66affSColin Finck 
1169c2c66affSColin Finck                     InsertTailList(&fcb->xattrs, &xa->list_entry);
1170c2c66affSColin Finck                 }
1171c2c66affSColin Finck 
1172c2c66affSColin Finck                 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
1173c2c66affSColin Finck 
1174c2c66affSColin Finck                 if (len < offsetof(DIR_ITEM, name[0]))
1175c2c66affSColin Finck                     break;
1176c2c66affSColin Finck 
1177c2c66affSColin Finck                 di = (DIR_ITEM*)&di->name[di->m + di->n];
1178318da0c1SPierre Schweitzer             } while (true);
1179c2c66affSColin Finck         } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
1180c2c66affSColin Finck             extent* ext;
1181318da0c1SPierre Schweitzer             bool unique = false;
1182c2c66affSColin Finck 
1183c2c66affSColin Finck             ed = (EXTENT_DATA*)tp.item->data;
1184c2c66affSColin Finck 
1185c2c66affSColin Finck             if (tp.item->size < sizeof(EXTENT_DATA)) {
1186194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1187c2c66affSColin Finck                     tp.item->size, sizeof(EXTENT_DATA));
1188c2c66affSColin Finck 
1189883b1f31SPierre Schweitzer                 reap_fcb(fcb);
1190c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
1191c2c66affSColin Finck             }
1192c2c66affSColin Finck 
1193c2c66affSColin Finck             if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
1194c2c66affSColin Finck                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
1195c2c66affSColin Finck 
1196c2c66affSColin Finck                 if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
1197194ea909SVictor Perevertkin                     ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1198c2c66affSColin Finck                         tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
1199c2c66affSColin Finck 
1200883b1f31SPierre Schweitzer                     reap_fcb(fcb);
1201c2c66affSColin Finck                     return STATUS_INTERNAL_ERROR;
1202c2c66affSColin Finck                 }
1203c2c66affSColin Finck 
1204c2c66affSColin Finck                 if (ed2->address == 0 || ed2->size == 0) // sparse
1205c2c66affSColin Finck                     continue;
1206c2c66affSColin Finck 
1207c2c66affSColin Finck                 if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
1208c2c66affSColin Finck                     unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
1209c2c66affSColin Finck             }
1210c2c66affSColin Finck 
1211c2c66affSColin Finck             ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
1212c2c66affSColin Finck             if (!ext) {
1213c2c66affSColin Finck                 ERR("out of memory\n");
1214883b1f31SPierre Schweitzer                 reap_fcb(fcb);
1215c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1216c2c66affSColin Finck             }
1217c2c66affSColin Finck 
1218c2c66affSColin Finck             ext->offset = tp.item->key.offset;
1219c2c66affSColin Finck             RtlCopyMemory(&ext->extent_data, tp.item->data, tp.item->size);
1220c2c66affSColin Finck             ext->datalen = tp.item->size;
1221c2c66affSColin Finck             ext->unique = unique;
1222318da0c1SPierre Schweitzer             ext->ignore = false;
1223318da0c1SPierre Schweitzer             ext->inserted = false;
1224c2c66affSColin Finck             ext->csum = NULL;
1225c2c66affSColin Finck 
1226c2c66affSColin Finck             InsertTailList(&fcb->extents, &ext->list_entry);
1227c2c66affSColin Finck         }
1228c2c66affSColin Finck     }
1229c2c66affSColin Finck 
1230c2c66affSColin Finck     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
1231318da0c1SPierre Schweitzer         Status = load_dir_children(Vcb, fcb, false, Irp);
1232c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1233194ea909SVictor Perevertkin             ERR("load_dir_children returned %08lx\n", Status);
1234883b1f31SPierre Schweitzer             reap_fcb(fcb);
1235c2c66affSColin Finck             return Status;
1236c2c66affSColin Finck         }
1237c2c66affSColin Finck     }
1238c2c66affSColin Finck 
1239c2c66affSColin Finck     if (no_data) {
1240c2c66affSColin Finck         fcb->Header.AllocationSize.QuadPart = 0;
1241c2c66affSColin Finck         fcb->Header.FileSize.QuadPart = 0;
1242c2c66affSColin Finck         fcb->Header.ValidDataLength.QuadPart = 0;
1243c2c66affSColin Finck     } else {
1244c2c66affSColin Finck         if (ed && ed->type == EXTENT_TYPE_INLINE)
1245c2c66affSColin Finck             fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
1246c2c66affSColin Finck         else
1247c2c66affSColin Finck             fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
1248c2c66affSColin Finck 
1249c2c66affSColin Finck         fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
1250c2c66affSColin Finck         fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
1251c2c66affSColin Finck     }
1252c2c66affSColin Finck 
1253c2c66affSColin Finck     if (!atts_set)
1254318da0c1SPierre Schweitzer         fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', true, Irp);
1255c2c66affSColin Finck 
1256c2c66affSColin Finck     if (!sd_set)
1257318da0c1SPierre Schweitzer         fcb_get_sd(fcb, parent, false, Irp);
1258c2c66affSColin Finck 
1259883b1f31SPierre Schweitzer     acquire_fcb_lock_exclusive(Vcb);
1260883b1f31SPierre Schweitzer 
1261318da0c1SPierre Schweitzer     if (lastle && subvol->fcbs_version == fcbs_version) {
1262883b1f31SPierre Schweitzer         InsertHeadList(lastle, &fcb->list_entry);
1263318da0c1SPierre Schweitzer 
1264318da0c1SPierre Schweitzer         if (!subvol->fcbs_ptrs[hash >> 24] || CONTAINING_RECORD(subvol->fcbs_ptrs[hash >> 24], struct _fcb, list_entry)->hash > hash)
1265318da0c1SPierre Schweitzer             subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1266318da0c1SPierre Schweitzer     } else {
1267318da0c1SPierre Schweitzer         lastle = NULL;
1268318da0c1SPierre Schweitzer 
1269883b1f31SPierre Schweitzer         if (subvol->fcbs_ptrs[hash >> 24]) {
1270883b1f31SPierre Schweitzer             LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
1271883b1f31SPierre Schweitzer 
1272883b1f31SPierre Schweitzer             while (le != &subvol->fcbs) {
1273883b1f31SPierre Schweitzer                 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
1274883b1f31SPierre Schweitzer 
1275883b1f31SPierre Schweitzer                 if (fcb2->inode == inode) {
1276883b1f31SPierre Schweitzer                     if (!fcb2->ads) {
1277883b1f31SPierre Schweitzer                         if (fcb2->deleted)
1278883b1f31SPierre Schweitzer                             deleted_fcb = fcb2;
1279883b1f31SPierre Schweitzer                         else {
1280883b1f31SPierre Schweitzer #ifdef DEBUG_FCB_REFCOUNTS
1281883b1f31SPierre Schweitzer                             LONG rc = InterlockedIncrement(&fcb2->refcount);
1282883b1f31SPierre Schweitzer 
1283318da0c1SPierre Schweitzer                             WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb2, rc, fcb2->subvol->id, fcb2->inode);
1284883b1f31SPierre Schweitzer #else
1285883b1f31SPierre Schweitzer                             InterlockedIncrement(&fcb2->refcount);
1286883b1f31SPierre Schweitzer #endif
1287883b1f31SPierre Schweitzer 
1288883b1f31SPierre Schweitzer                             *pfcb = fcb2;
1289883b1f31SPierre Schweitzer                             reap_fcb(fcb);
1290f381137cSPierre Schweitzer                             release_fcb_lock(Vcb);
1291883b1f31SPierre Schweitzer                             return STATUS_SUCCESS;
1292883b1f31SPierre Schweitzer                         }
1293883b1f31SPierre Schweitzer                     }
1294883b1f31SPierre Schweitzer                 } else if (fcb2->hash > hash) {
1295883b1f31SPierre Schweitzer                     if (deleted_fcb) {
1296883b1f31SPierre Schweitzer                         InterlockedIncrement(&deleted_fcb->refcount);
1297883b1f31SPierre Schweitzer                         *pfcb = deleted_fcb;
1298883b1f31SPierre Schweitzer                         reap_fcb(fcb);
1299f381137cSPierre Schweitzer                         release_fcb_lock(Vcb);
1300883b1f31SPierre Schweitzer                         return STATUS_SUCCESS;
1301883b1f31SPierre Schweitzer                     }
1302883b1f31SPierre Schweitzer 
1303883b1f31SPierre Schweitzer                     lastle = le->Blink;
1304883b1f31SPierre Schweitzer                     break;
1305883b1f31SPierre Schweitzer                 }
1306883b1f31SPierre Schweitzer 
1307883b1f31SPierre Schweitzer                 le = le->Flink;
1308883b1f31SPierre Schweitzer             }
1309883b1f31SPierre Schweitzer         }
1310883b1f31SPierre Schweitzer 
1311c2c66affSColin Finck         if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
1312c2c66affSColin Finck             fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
1313c2c66affSColin Finck 
1314c2c66affSColin Finck             if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
1315318da0c1SPierre Schweitzer                 fcb->atts_changed = true;
1316c2c66affSColin Finck                 mark_fcb_dirty(fcb);
1317c2c66affSColin Finck             }
1318c2c66affSColin Finck         }
1319c2c66affSColin Finck 
1320883b1f31SPierre Schweitzer         if (!lastle) {
1321318da0c1SPierre Schweitzer             uint8_t c = hash >> 24;
1322883b1f31SPierre Schweitzer 
1323883b1f31SPierre Schweitzer             if (c != 0xff) {
1324318da0c1SPierre Schweitzer                 uint8_t d = c + 1;
1325883b1f31SPierre Schweitzer 
1326883b1f31SPierre Schweitzer                 do {
1327883b1f31SPierre Schweitzer                     if (subvol->fcbs_ptrs[d]) {
1328883b1f31SPierre Schweitzer                         lastle = subvol->fcbs_ptrs[d]->Blink;
1329883b1f31SPierre Schweitzer                         break;
1330883b1f31SPierre Schweitzer                     }
1331883b1f31SPierre Schweitzer 
1332883b1f31SPierre Schweitzer                     d++;
1333883b1f31SPierre Schweitzer                 } while (d != 0);
1334883b1f31SPierre Schweitzer             }
1335883b1f31SPierre Schweitzer         }
1336883b1f31SPierre Schweitzer 
1337883b1f31SPierre Schweitzer         if (lastle) {
1338c2c66affSColin Finck             InsertHeadList(lastle, &fcb->list_entry);
1339883b1f31SPierre Schweitzer 
1340883b1f31SPierre Schweitzer             if (lastle == &subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
1341883b1f31SPierre Schweitzer                 subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1342883b1f31SPierre Schweitzer         } else {
1343c2c66affSColin Finck             InsertTailList(&subvol->fcbs, &fcb->list_entry);
1344c2c66affSColin Finck 
1345883b1f31SPierre Schweitzer             if (fcb->list_entry.Blink == &subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
1346883b1f31SPierre Schweitzer                 subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1347883b1f31SPierre Schweitzer         }
1348883b1f31SPierre Schweitzer     }
1349883b1f31SPierre Schweitzer 
1350194ea909SVictor Perevertkin     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE && fcb->subvol != Vcb->root_fileref->fcb->subvol)
135162e630deSPierre Schweitzer         fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
135262e630deSPierre Schweitzer 
1353883b1f31SPierre Schweitzer     subvol->fcbs_version++;
1354883b1f31SPierre Schweitzer 
1355c2c66affSColin Finck     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1356c2c66affSColin Finck 
1357883b1f31SPierre Schweitzer     release_fcb_lock(Vcb);
1358883b1f31SPierre Schweitzer 
1359c2c66affSColin Finck     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1360c2c66affSColin Finck 
1361c2c66affSColin Finck     *pfcb = fcb;
1362883b1f31SPierre Schweitzer 
1363c2c66affSColin Finck     return STATUS_SUCCESS;
1364c2c66affSColin Finck }
1365c2c66affSColin Finck 
1366c2c66affSColin Finck static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
1367c2c66affSColin Finck                                 dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) {
1368c2c66affSColin Finck     fcb* fcb;
1369318da0c1SPierre Schweitzer     uint8_t* xattrdata;
1370318da0c1SPierre Schweitzer     uint16_t xattrlen, overhead;
1371c2c66affSColin Finck     NTSTATUS Status;
1372c2c66affSColin Finck     KEY searchkey;
1373c2c66affSColin Finck     traverse_ptr tp;
1374eb7fbc25SPierre Schweitzer     static const char xapref[] = "user.";
1375c2c66affSColin Finck     ANSI_STRING xattr;
1376318da0c1SPierre Schweitzer     uint32_t crc32;
1377c2c66affSColin Finck 
1378eb7fbc25SPierre Schweitzer     xattr.Length = sizeof(xapref) - 1 + dc->utf8.Length;
1379c2c66affSColin Finck     xattr.MaximumLength = xattr.Length + 1;
1380c2c66affSColin Finck     xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
1381c2c66affSColin Finck     if (!xattr.Buffer) {
1382c2c66affSColin Finck         ERR("out of memory\n");
1383c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1384c2c66affSColin Finck     }
1385c2c66affSColin Finck 
1386eb7fbc25SPierre Schweitzer     RtlCopyMemory(xattr.Buffer, xapref, sizeof(xapref) - 1);
1387eb7fbc25SPierre Schweitzer     RtlCopyMemory(&xattr.Buffer[sizeof(xapref) - 1], dc->utf8.Buffer, dc->utf8.Length);
1388c2c66affSColin Finck     xattr.Buffer[xattr.Length] = 0;
1389c2c66affSColin Finck 
1390c2c66affSColin Finck     fcb = create_fcb(Vcb, PagedPool);
1391c2c66affSColin Finck     if (!fcb) {
1392c2c66affSColin Finck         ERR("out of memory\n");
1393c2c66affSColin Finck         ExFreePool(xattr.Buffer);
1394c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1395c2c66affSColin Finck     }
1396c2c66affSColin Finck 
1397c2c66affSColin Finck     fcb->Vcb = Vcb;
1398c2c66affSColin Finck 
1399318da0c1SPierre Schweitzer     crc32 = calc_crc32c(0xfffffffe, (uint8_t*)xattr.Buffer, xattr.Length);
1400c2c66affSColin Finck 
1401c2c66affSColin Finck     if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
1402c2c66affSColin Finck         ERR("get_xattr failed\n");
1403883b1f31SPierre Schweitzer         reap_fcb(fcb);
1404c2c66affSColin Finck         ExFreePool(xattr.Buffer);
1405c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
1406c2c66affSColin Finck     }
1407c2c66affSColin Finck 
1408c2c66affSColin Finck     fcb->subvol = parent->subvol;
1409c2c66affSColin Finck     fcb->inode = parent->inode;
1410c2c66affSColin Finck     fcb->type = parent->type;
1411318da0c1SPierre Schweitzer     fcb->ads = true;
1412c2c66affSColin Finck     fcb->adshash = crc32;
1413c2c66affSColin Finck     fcb->adsxattr = xattr;
1414c2c66affSColin Finck 
1415c2c66affSColin Finck     // find XATTR_ITEM overhead and hence calculate maximum length
1416c2c66affSColin Finck 
1417c2c66affSColin Finck     searchkey.obj_id = parent->inode;
1418c2c66affSColin Finck     searchkey.obj_type = TYPE_XATTR_ITEM;
1419c2c66affSColin Finck     searchkey.offset = crc32;
1420c2c66affSColin Finck 
1421318da0c1SPierre Schweitzer     Status = find_item(Vcb, parent->subvol, &tp, &searchkey, false, Irp);
1422c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1423194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
1424883b1f31SPierre Schweitzer         reap_fcb(fcb);
1425c2c66affSColin Finck         return Status;
1426c2c66affSColin Finck     }
1427c2c66affSColin Finck 
1428c2c66affSColin Finck     if (keycmp(tp.item->key, searchkey)) {
1429c2c66affSColin Finck         ERR("error - could not find key for xattr\n");
1430883b1f31SPierre Schweitzer         reap_fcb(fcb);
1431c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
1432c2c66affSColin Finck     }
1433c2c66affSColin Finck 
1434c2c66affSColin Finck     if (tp.item->size < xattrlen) {
1435318da0c1SPierre Schweitzer         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
1436883b1f31SPierre Schweitzer         reap_fcb(fcb);
1437c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
1438c2c66affSColin Finck     }
1439c2c66affSColin Finck 
1440c2c66affSColin Finck     overhead = tp.item->size - xattrlen;
1441c2c66affSColin Finck 
1442c2c66affSColin Finck     fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
1443c2c66affSColin Finck 
1444c2c66affSColin Finck     fcb->adsdata.Buffer = (char*)xattrdata;
1445c2c66affSColin Finck     fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
1446c2c66affSColin Finck 
1447c2c66affSColin Finck     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1448c2c66affSColin Finck     fcb->Header.AllocationSize.QuadPart = xattrlen;
1449c2c66affSColin Finck     fcb->Header.FileSize.QuadPart = xattrlen;
1450c2c66affSColin Finck     fcb->Header.ValidDataLength.QuadPart = xattrlen;
1451c2c66affSColin Finck 
1452c2c66affSColin Finck     TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
1453c2c66affSColin Finck 
1454c2c66affSColin Finck     *pfcb = fcb;
1455c2c66affSColin Finck 
1456c2c66affSColin Finck     return STATUS_SUCCESS;
1457c2c66affSColin Finck }
1458c2c66affSColin Finck 
1459c2c66affSColin Finck NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb,
1460318da0c1SPierre Schweitzer                             _In_ file_ref* sf, _In_ PUNICODE_STRING name, _In_ bool case_sensitive, _In_ bool lastpart, _In_ bool streampart,
1461c2c66affSColin Finck                             _In_ POOL_TYPE pooltype, _Out_ file_ref** psf2, _In_opt_ PIRP Irp) {
1462c2c66affSColin Finck     NTSTATUS Status;
1463c2c66affSColin Finck     file_ref* sf2;
1464c2c66affSColin Finck 
1465c2c66affSColin Finck     if (sf->fcb == Vcb->dummy_fcb)
1466c2c66affSColin Finck         return STATUS_OBJECT_NAME_NOT_FOUND;
1467c2c66affSColin Finck 
1468c2c66affSColin Finck     if (streampart) {
1469318da0c1SPierre Schweitzer         bool locked = false;
1470c2c66affSColin Finck         LIST_ENTRY* le;
1471c2c66affSColin Finck         UNICODE_STRING name_uc;
1472c2c66affSColin Finck         dir_child* dc = NULL;
1473c2c66affSColin Finck         fcb* fcb;
1474883b1f31SPierre Schweitzer         struct _fcb* duff_fcb = NULL;
1475883b1f31SPierre Schweitzer         file_ref* duff_fr = NULL;
1476c2c66affSColin Finck 
1477c2c66affSColin Finck         if (!case_sensitive) {
1478318da0c1SPierre Schweitzer             Status = RtlUpcaseUnicodeString(&name_uc, name, true);
1479c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1480194ea909SVictor Perevertkin                 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1481c2c66affSColin Finck                 return Status;
1482c2c66affSColin Finck             }
1483c2c66affSColin Finck         }
1484c2c66affSColin Finck 
1485c2c66affSColin Finck         if (!ExIsResourceAcquiredSharedLite(&sf->fcb->nonpaged->dir_children_lock)) {
1486318da0c1SPierre Schweitzer             ExAcquireResourceSharedLite(&sf->fcb->nonpaged->dir_children_lock, true);
1487318da0c1SPierre Schweitzer             locked = true;
1488c2c66affSColin Finck         }
1489c2c66affSColin Finck 
1490c2c66affSColin Finck         le = sf->fcb->dir_children_index.Flink;
1491c2c66affSColin Finck         while (le != &sf->fcb->dir_children_index) {
1492c2c66affSColin Finck             dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
1493c2c66affSColin Finck 
1494c2c66affSColin Finck             if (dc2->index == 0) {
1495c2c66affSColin Finck                 if ((case_sensitive && dc2->name.Length == name->Length && RtlCompareMemory(dc2->name.Buffer, name->Buffer, dc2->name.Length) == dc2->name.Length) ||
1496c2c66affSColin Finck                     (!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)
1497c2c66affSColin Finck                 ) {
1498c2c66affSColin Finck                     dc = dc2;
1499c2c66affSColin Finck                     break;
1500c2c66affSColin Finck                 }
1501c2c66affSColin Finck             } else
1502c2c66affSColin Finck                 break;
1503c2c66affSColin Finck 
1504c2c66affSColin Finck             le = le->Flink;
1505c2c66affSColin Finck         }
1506c2c66affSColin Finck 
1507883b1f31SPierre Schweitzer         if (!dc) {
1508c2c66affSColin Finck             if (locked)
1509c2c66affSColin Finck                 ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1510c2c66affSColin Finck 
1511883b1f31SPierre Schweitzer             if (!case_sensitive)
1512883b1f31SPierre Schweitzer                 ExFreePool(name_uc.Buffer);
1513883b1f31SPierre Schweitzer 
1514c2c66affSColin Finck             return STATUS_OBJECT_NAME_NOT_FOUND;
1515883b1f31SPierre Schweitzer         }
1516c2c66affSColin Finck 
1517c2c66affSColin Finck         if (dc->fileref) {
1518883b1f31SPierre Schweitzer             if (locked)
1519883b1f31SPierre Schweitzer                 ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1520883b1f31SPierre Schweitzer 
1521883b1f31SPierre Schweitzer             if (!case_sensitive)
1522883b1f31SPierre Schweitzer                 ExFreePool(name_uc.Buffer);
1523883b1f31SPierre Schweitzer 
1524c2c66affSColin Finck             increase_fileref_refcount(dc->fileref);
1525c2c66affSColin Finck             *psf2 = dc->fileref;
1526c2c66affSColin Finck             return STATUS_SUCCESS;
1527c2c66affSColin Finck         }
1528c2c66affSColin Finck 
1529883b1f31SPierre Schweitzer         if (locked)
1530883b1f31SPierre Schweitzer             ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1531883b1f31SPierre Schweitzer 
1532883b1f31SPierre Schweitzer         if (!case_sensitive)
1533883b1f31SPierre Schweitzer             ExFreePool(name_uc.Buffer);
1534883b1f31SPierre Schweitzer 
1535c2c66affSColin Finck         Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
1536c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1537194ea909SVictor Perevertkin             ERR("open_fcb_stream returned %08lx\n", Status);
1538c2c66affSColin Finck             return Status;
1539c2c66affSColin Finck         }
1540c2c66affSColin Finck 
1541883b1f31SPierre Schweitzer         fcb->hash = sf->fcb->hash;
1542883b1f31SPierre Schweitzer 
1543883b1f31SPierre Schweitzer         acquire_fcb_lock_exclusive(Vcb);
1544883b1f31SPierre Schweitzer 
1545883b1f31SPierre Schweitzer         if (sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
1546318da0c1SPierre Schweitzer             le = sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
1547883b1f31SPierre Schweitzer 
1548883b1f31SPierre Schweitzer             while (le != &sf->fcb->subvol->fcbs) {
1549883b1f31SPierre Schweitzer                 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
1550883b1f31SPierre Schweitzer 
1551883b1f31SPierre Schweitzer                 if (fcb2->inode == fcb->inode) {
1552883b1f31SPierre Schweitzer                     if (fcb2->ads && fcb2->adshash == fcb->adshash) { // FIXME - handle hash collisions
1553883b1f31SPierre Schweitzer                         duff_fcb = fcb;
1554883b1f31SPierre Schweitzer                         fcb = fcb2;
1555883b1f31SPierre Schweitzer                         break;
1556883b1f31SPierre Schweitzer                     }
1557883b1f31SPierre Schweitzer                 } else if (fcb2->hash > fcb->hash)
1558883b1f31SPierre Schweitzer                     break;
1559883b1f31SPierre Schweitzer 
1560883b1f31SPierre Schweitzer                 le = le->Flink;
1561883b1f31SPierre Schweitzer             }
1562883b1f31SPierre Schweitzer         }
1563883b1f31SPierre Schweitzer 
1564883b1f31SPierre Schweitzer         if (!duff_fcb) {
1565883b1f31SPierre Schweitzer             InsertHeadList(&sf->fcb->list_entry, &fcb->list_entry);
1566883b1f31SPierre Schweitzer             InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1567883b1f31SPierre Schweitzer             fcb->subvol->fcbs_version++;
1568883b1f31SPierre Schweitzer         }
1569883b1f31SPierre Schweitzer 
1570883b1f31SPierre Schweitzer         release_fcb_lock(Vcb);
1571883b1f31SPierre Schweitzer 
1572883b1f31SPierre Schweitzer         if (duff_fcb) {
1573883b1f31SPierre Schweitzer             reap_fcb(duff_fcb);
1574883b1f31SPierre Schweitzer             InterlockedIncrement(&fcb->refcount);
1575883b1f31SPierre Schweitzer         }
1576883b1f31SPierre Schweitzer 
1577c2c66affSColin Finck         sf2 = create_fileref(Vcb);
1578c2c66affSColin Finck         if (!sf2) {
1579c2c66affSColin Finck             ERR("out of memory\n");
1580883b1f31SPierre Schweitzer             free_fcb(fcb);
1581c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
1582c2c66affSColin Finck         }
1583c2c66affSColin Finck 
1584318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
1585883b1f31SPierre Schweitzer 
1586883b1f31SPierre Schweitzer         if (dc->fileref) {
1587883b1f31SPierre Schweitzer             duff_fr = sf2;
1588883b1f31SPierre Schweitzer             sf2 = dc->fileref;
1589883b1f31SPierre Schweitzer             increase_fileref_refcount(sf2);
1590883b1f31SPierre Schweitzer         } else {
1591c2c66affSColin Finck             sf2->fcb = fcb;
1592c2c66affSColin Finck             sf2->parent = (struct _file_ref*)sf;
1593c2c66affSColin Finck             sf2->dc = dc;
1594c2c66affSColin Finck             dc->fileref = sf2;
1595c2c66affSColin Finck             increase_fileref_refcount(sf);
1596883b1f31SPierre Schweitzer             InsertTailList(&sf->children, &sf2->list_entry);
1597883b1f31SPierre Schweitzer         }
1598883b1f31SPierre Schweitzer 
1599883b1f31SPierre Schweitzer         ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1600883b1f31SPierre Schweitzer 
1601883b1f31SPierre Schweitzer         if (duff_fr)
1602c982533eSVincent Franchomme             ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, duff_fr);
1603c2c66affSColin Finck     } else {
1604c2c66affSColin Finck         root* subvol;
1605318da0c1SPierre Schweitzer         uint64_t inode;
1606c2c66affSColin Finck         dir_child* dc;
1607c2c66affSColin Finck 
1608c2c66affSColin Finck         Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
1609c2c66affSColin Finck         if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
1610194ea909SVictor Perevertkin             TRACE("could not find %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
1611c2c66affSColin Finck 
1612c2c66affSColin Finck             return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
161398654b54SVincent Franchomme         } else if (Status == STATUS_OBJECT_NAME_INVALID) {
161498654b54SVincent Franchomme             TRACE("invalid filename: %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
161598654b54SVincent Franchomme             return Status;
1616c2c66affSColin Finck         } else if (!NT_SUCCESS(Status)) {
1617194ea909SVictor Perevertkin             ERR("find_file_in_dir returned %08lx\n", Status);
1618c2c66affSColin Finck             return Status;
1619c2c66affSColin Finck         } else {
1620c2c66affSColin Finck             fcb* fcb;
1621883b1f31SPierre Schweitzer             file_ref* duff_fr = NULL;
1622c2c66affSColin Finck 
1623c2c66affSColin Finck             if (dc->fileref) {
1624c2c66affSColin Finck                 if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
1625c2c66affSColin Finck                     TRACE("passed path including file as subdirectory\n");
1626c2c66affSColin Finck                     return STATUS_OBJECT_PATH_NOT_FOUND;
1627c2c66affSColin Finck                 }
1628c2c66affSColin Finck 
1629c2c66affSColin Finck                 InterlockedIncrement(&dc->fileref->refcount);
1630c2c66affSColin Finck                 *psf2 = dc->fileref;
1631c2c66affSColin Finck                 return STATUS_SUCCESS;
1632c2c66affSColin Finck             }
1633c2c66affSColin Finck 
163462e630deSPierre Schweitzer             if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id && !dc->root_dir)) {
1635c2c66affSColin Finck                 fcb = Vcb->dummy_fcb;
1636c2c66affSColin Finck                 InterlockedIncrement(&fcb->refcount);
1637c2c66affSColin Finck             } else {
1638318da0c1SPierre Schweitzer                 Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, false, sf->fcb, &fcb, pooltype, Irp);
1639c2c66affSColin Finck 
1640c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1641194ea909SVictor Perevertkin                     ERR("open_fcb returned %08lx\n", Status);
1642c2c66affSColin Finck                     return Status;
1643c2c66affSColin Finck                 }
1644c2c66affSColin Finck             }
1645c2c66affSColin Finck 
1646c2c66affSColin Finck             if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
1647c2c66affSColin Finck                 TRACE("passed path including file as subdirectory\n");
1648883b1f31SPierre Schweitzer                 free_fcb(fcb);
1649c2c66affSColin Finck                 return STATUS_OBJECT_PATH_NOT_FOUND;
1650c2c66affSColin Finck             }
1651c2c66affSColin Finck 
1652c2c66affSColin Finck             sf2 = create_fileref(Vcb);
1653c2c66affSColin Finck             if (!sf2) {
1654c2c66affSColin Finck                 ERR("out of memory\n");
1655883b1f31SPierre Schweitzer                 free_fcb(fcb);
1656c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1657c2c66affSColin Finck             }
1658c2c66affSColin Finck 
1659c2c66affSColin Finck             sf2->fcb = fcb;
1660c2c66affSColin Finck 
1661318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
1662883b1f31SPierre Schweitzer 
1663883b1f31SPierre Schweitzer             if (!dc->fileref) {
1664883b1f31SPierre Schweitzer                 sf2->parent = (struct _file_ref*)sf;
1665c2c66affSColin Finck                 sf2->dc = dc;
1666c2c66affSColin Finck                 dc->fileref = sf2;
1667c2c66affSColin Finck                 InsertTailList(&sf->children, &sf2->list_entry);
1668c2c66affSColin Finck                 increase_fileref_refcount(sf);
1669c982533eSVincent Franchomme 
1670c982533eSVincent Franchomme                 if (dc->type == BTRFS_TYPE_DIRECTORY)
1671c982533eSVincent Franchomme                     fcb->fileref = sf2;
1672883b1f31SPierre Schweitzer             } else {
1673883b1f31SPierre Schweitzer                 duff_fr = sf2;
1674883b1f31SPierre Schweitzer                 sf2 = dc->fileref;
1675883b1f31SPierre Schweitzer                 increase_fileref_refcount(sf2);
1676883b1f31SPierre Schweitzer             }
1677883b1f31SPierre Schweitzer 
1678883b1f31SPierre Schweitzer             ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1679883b1f31SPierre Schweitzer 
1680883b1f31SPierre Schweitzer             if (duff_fr)
1681883b1f31SPierre Schweitzer                 reap_fileref(Vcb, duff_fr);
1682c2c66affSColin Finck         }
1683c2c66affSColin Finck     }
1684c2c66affSColin Finck 
1685c2c66affSColin Finck     *psf2 = sf2;
1686c2c66affSColin Finck 
1687c2c66affSColin Finck     return STATUS_SUCCESS;
1688c2c66affSColin Finck }
1689c2c66affSColin Finck 
1690c2c66affSColin Finck NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Out_ file_ref** pfr,
1691318da0c1SPierre Schweitzer                       _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,
1692318da0c1SPierre Schweitzer                       _In_ bool case_sensitive, _In_opt_ PIRP Irp) {
1693c2c66affSColin Finck     UNICODE_STRING fnus2;
1694c2c66affSColin Finck     file_ref *dir, *sf, *sf2;
1695c2c66affSColin Finck     LIST_ENTRY parts;
169606042735SVincent Franchomme     bool has_stream = false;
1697c2c66affSColin Finck     NTSTATUS Status;
1698c2c66affSColin Finck     LIST_ENTRY* le;
1699c2c66affSColin Finck 
1700c2c66affSColin Finck     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
1701c2c66affSColin Finck 
1702c2c66affSColin Finck     if (Vcb->removing || Vcb->locked)
1703c2c66affSColin Finck         return STATUS_ACCESS_DENIED;
1704c2c66affSColin Finck 
1705c2c66affSColin Finck     fnus2 = *fnus;
1706c2c66affSColin Finck 
1707c2c66affSColin Finck     if (fnus2.Length < sizeof(WCHAR) && !related) {
1708c2c66affSColin Finck         ERR("error - fnus was too short\n");
1709c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
1710c2c66affSColin Finck     }
1711c2c66affSColin Finck 
1712c2c66affSColin Finck     if (related && fnus->Length == 0) {
1713c2c66affSColin Finck         increase_fileref_refcount(related);
1714c2c66affSColin Finck 
1715c2c66affSColin Finck         *pfr = related;
1716c2c66affSColin Finck         return STATUS_SUCCESS;
1717c2c66affSColin Finck     }
1718c2c66affSColin Finck 
1719c2c66affSColin Finck     if (related) {
1720c2c66affSColin Finck         dir = related;
1721c2c66affSColin Finck     } else {
1722c2c66affSColin Finck         if (fnus2.Buffer[0] != '\\') {
1723194ea909SVictor Perevertkin             ERR("error - filename %.*S did not begin with \\\n", (int)(fnus2.Length / sizeof(WCHAR)), fnus2.Buffer);
1724c2c66affSColin Finck             return STATUS_OBJECT_PATH_NOT_FOUND;
1725c2c66affSColin Finck         }
1726c2c66affSColin Finck 
17274672b2baSPierre Schweitzer         // if path starts with two backslashes, ignore one of them
17284672b2baSPierre Schweitzer         if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\') {
17294672b2baSPierre Schweitzer             fnus2.Buffer++;
17304672b2baSPierre Schweitzer             fnus2.Length -= sizeof(WCHAR);
17314672b2baSPierre Schweitzer             fnus2.MaximumLength -= sizeof(WCHAR);
17324672b2baSPierre Schweitzer         }
17334672b2baSPierre Schweitzer 
1734c2c66affSColin Finck         if (fnus2.Length == sizeof(WCHAR)) {
1735c2c66affSColin Finck             if (Vcb->root_fileref->open_count == 0 && !(Vcb->Vpb->Flags & VPB_MOUNTED)) // don't allow root to be opened on unmounted FS
1736c2c66affSColin Finck                 return STATUS_DEVICE_NOT_READY;
1737c2c66affSColin Finck 
1738c2c66affSColin Finck             increase_fileref_refcount(Vcb->root_fileref);
1739c2c66affSColin Finck             *pfr = Vcb->root_fileref;
1740c2c66affSColin Finck 
1741c2c66affSColin Finck             if (fn_offset)
1742c2c66affSColin Finck                 *fn_offset = 0;
1743c2c66affSColin Finck 
1744c2c66affSColin Finck             return STATUS_SUCCESS;
1745883b1f31SPierre Schweitzer         } else if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\')
1746883b1f31SPierre Schweitzer             return STATUS_OBJECT_NAME_INVALID;
1747c2c66affSColin Finck 
1748c2c66affSColin Finck         dir = Vcb->root_fileref;
1749c2c66affSColin Finck 
1750c2c66affSColin Finck         fnus2.Buffer++;
1751c2c66affSColin Finck         fnus2.Length -= sizeof(WCHAR);
1752c2c66affSColin Finck         fnus2.MaximumLength -= sizeof(WCHAR);
1753c2c66affSColin Finck     }
1754c2c66affSColin Finck 
1755c2c66affSColin Finck     if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
175662e630deSPierre Schweitzer         WARN("passed related fileref which isn't a directory (fnus = %.*S)\n",
1757194ea909SVictor Perevertkin              (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer);
1758c2c66affSColin Finck         return STATUS_OBJECT_PATH_NOT_FOUND;
1759c2c66affSColin Finck     }
1760c2c66affSColin Finck 
1761c2c66affSColin Finck     InitializeListHead(&parts);
1762c2c66affSColin Finck 
1763c2c66affSColin Finck     if (fnus->Length != 0 &&
1764eb7fbc25SPierre Schweitzer         (fnus->Length != sizeof(datastring) - sizeof(WCHAR) || RtlCompareMemory(fnus->Buffer, datastring, sizeof(datastring) - sizeof(WCHAR)) != sizeof(datastring) - sizeof(WCHAR))) {
1765c2c66affSColin Finck         Status = split_path(Vcb, &fnus2, &parts, &has_stream);
1766c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1767194ea909SVictor Perevertkin             ERR("split_path returned %08lx\n", Status);
1768c2c66affSColin Finck             return Status;
1769c2c66affSColin Finck         }
1770c2c66affSColin Finck     }
1771c2c66affSColin Finck 
1772c2c66affSColin Finck     sf = dir;
1773c2c66affSColin Finck     increase_fileref_refcount(dir);
1774c2c66affSColin Finck 
1775c2c66affSColin Finck     if (parent && !IsListEmpty(&parts)) {
1776c2c66affSColin Finck         name_bit* nb;
1777c2c66affSColin Finck 
1778c2c66affSColin Finck         nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
1779f5556fdcSVincent Franchomme         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1780c2c66affSColin Finck 
1781c2c66affSColin Finck         if (has_stream && !IsListEmpty(&parts)) {
1782c2c66affSColin Finck             nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
1783f5556fdcSVincent Franchomme             ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1784c2c66affSColin Finck 
1785318da0c1SPierre Schweitzer             has_stream = false;
1786c2c66affSColin Finck         }
1787c2c66affSColin Finck     }
1788c2c66affSColin Finck 
1789c2c66affSColin Finck     if (IsListEmpty(&parts)) {
1790c2c66affSColin Finck         Status = STATUS_SUCCESS;
1791c2c66affSColin Finck         *pfr = dir;
1792c2c66affSColin Finck 
1793c2c66affSColin Finck         if (fn_offset)
1794c2c66affSColin Finck             *fn_offset = 0;
1795c2c66affSColin Finck 
1796c2c66affSColin Finck         goto end2;
1797c2c66affSColin Finck     }
1798c2c66affSColin Finck 
1799c2c66affSColin Finck     le = parts.Flink;
1800c2c66affSColin Finck     do {
1801c2c66affSColin Finck         name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
1802318da0c1SPierre Schweitzer         bool lastpart = le->Flink == &parts || (has_stream && le->Flink->Flink == &parts);
1803318da0c1SPierre Schweitzer         bool streampart = has_stream && le->Flink == &parts;
1804318da0c1SPierre Schweitzer         bool cs = case_sensitive;
1805f381137cSPierre Schweitzer 
1806f381137cSPierre Schweitzer         if (!cs) {
180762e630deSPierre Schweitzer             if (streampart && sf->parent)
1808f381137cSPierre Schweitzer                 cs = sf->parent->fcb->case_sensitive;
1809f381137cSPierre Schweitzer             else
1810f381137cSPierre Schweitzer                 cs = sf->fcb->case_sensitive;
1811f381137cSPierre Schweitzer         }
1812f381137cSPierre Schweitzer 
1813f381137cSPierre Schweitzer         Status = open_fileref_child(Vcb, sf, &nb->us, cs, lastpart, streampart, pooltype, &sf2, Irp);
1814318da0c1SPierre Schweitzer 
1815c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
181698654b54SVincent Franchomme             if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_NAME_INVALID)
1817194ea909SVictor Perevertkin                 TRACE("open_fileref_child returned %08lx\n", Status);
1818c2c66affSColin Finck             else
1819194ea909SVictor Perevertkin                 ERR("open_fileref_child returned %08lx\n", Status);
1820c2c66affSColin Finck 
1821c2c66affSColin Finck             goto end;
1822c2c66affSColin Finck         }
1823c2c66affSColin Finck 
1824c2c66affSColin Finck         if (le->Flink == &parts) { // last entry
1825c2c66affSColin Finck             if (fn_offset) {
1826c2c66affSColin Finck                 if (has_stream)
1827c2c66affSColin Finck                     nb = CONTAINING_RECORD(le->Blink, name_bit, list_entry);
1828c2c66affSColin Finck 
1829c2c66affSColin Finck                 *fn_offset = (ULONG)(nb->us.Buffer - fnus->Buffer);
1830c2c66affSColin Finck             }
1831c2c66affSColin Finck 
1832c2c66affSColin Finck             break;
1833c2c66affSColin Finck         }
1834c2c66affSColin Finck 
1835c2c66affSColin Finck         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
1836c2c66affSColin Finck             Status = STATUS_REPARSE;
1837c2c66affSColin Finck 
1838c2c66affSColin Finck             if (parsed) {
1839c2c66affSColin Finck                 name_bit* nb2 = CONTAINING_RECORD(le->Flink, name_bit, list_entry);
1840c2c66affSColin Finck 
1841c2c66affSColin Finck                 *parsed = (USHORT)(nb2->us.Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
1842c2c66affSColin Finck             }
1843c2c66affSColin Finck 
1844c2c66affSColin Finck             break;
1845c2c66affSColin Finck         }
1846c2c66affSColin Finck 
1847883b1f31SPierre Schweitzer         free_fileref(sf);
1848c2c66affSColin Finck         sf = sf2;
1849c2c66affSColin Finck 
1850c2c66affSColin Finck         le = le->Flink;
1851c2c66affSColin Finck     } while (le != &parts);
1852c2c66affSColin Finck 
1853c2c66affSColin Finck     if (Status != STATUS_REPARSE)
1854c2c66affSColin Finck         Status = STATUS_SUCCESS;
1855c2c66affSColin Finck     *pfr = sf2;
1856c2c66affSColin Finck 
1857c2c66affSColin Finck end:
1858883b1f31SPierre Schweitzer     free_fileref(sf);
1859c2c66affSColin Finck 
1860c2c66affSColin Finck     while (!IsListEmpty(&parts)) {
1861c2c66affSColin Finck         name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
1862c2c66affSColin Finck         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1863c2c66affSColin Finck     }
1864c2c66affSColin Finck 
1865c2c66affSColin Finck end2:
1866194ea909SVictor Perevertkin     TRACE("returning %08lx\n", Status);
1867c2c66affSColin Finck 
1868c2c66affSColin Finck     return Status;
1869c2c66affSColin Finck }
1870c2c66affSColin Finck 
add_dir_child(fcb * fcb,uint64_t inode,bool subvol,PANSI_STRING utf8,PUNICODE_STRING name,uint8_t type,dir_child ** pdc)1871318da0c1SPierre Schweitzer NTSTATUS add_dir_child(fcb* fcb, uint64_t inode, bool subvol, PANSI_STRING utf8, PUNICODE_STRING name, uint8_t type, dir_child** pdc) {
1872c2c66affSColin Finck     NTSTATUS Status;
1873c2c66affSColin Finck     dir_child* dc;
1874318da0c1SPierre Schweitzer     bool locked;
1875c2c66affSColin Finck 
1876c2c66affSColin Finck     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
1877c2c66affSColin Finck     if (!dc) {
1878c2c66affSColin Finck         ERR("out of memory\n");
1879c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1880c2c66affSColin Finck     }
1881c2c66affSColin Finck 
1882f5556fdcSVincent Franchomme     RtlZeroMemory(dc, sizeof(dir_child));
1883f5556fdcSVincent Franchomme 
1884c2c66affSColin Finck     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1885c2c66affSColin Finck     if (!dc->utf8.Buffer) {
1886c2c66affSColin Finck         ERR("out of memory\n");
1887c2c66affSColin Finck         ExFreePool(dc);
1888c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1889c2c66affSColin Finck     }
1890c2c66affSColin Finck 
1891c2c66affSColin Finck     dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
1892c2c66affSColin Finck     if (!dc->name.Buffer) {
1893c2c66affSColin Finck         ERR("out of memory\n");
1894c2c66affSColin Finck         ExFreePool(dc->utf8.Buffer);
1895c2c66affSColin Finck         ExFreePool(dc);
1896c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1897c2c66affSColin Finck     }
1898c2c66affSColin Finck 
1899c2c66affSColin Finck     dc->key.obj_id = inode;
1900c2c66affSColin Finck     dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
1901c2c66affSColin Finck     dc->key.offset = subvol ? 0xffffffffffffffff : 0;
1902c2c66affSColin Finck     dc->type = type;
1903c2c66affSColin Finck     dc->fileref = NULL;
1904c2c66affSColin Finck 
1905c2c66affSColin Finck     dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length;
1906c2c66affSColin Finck     RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1907c2c66affSColin Finck 
1908c2c66affSColin Finck     dc->name.Length = dc->name.MaximumLength = name->Length;
1909c2c66affSColin Finck     RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
1910c2c66affSColin Finck 
1911318da0c1SPierre Schweitzer     Status = RtlUpcaseUnicodeString(&dc->name_uc, name, true);
1912c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1913194ea909SVictor Perevertkin         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1914c2c66affSColin Finck         ExFreePool(dc->utf8.Buffer);
1915c2c66affSColin Finck         ExFreePool(dc->name.Buffer);
1916c2c66affSColin Finck         ExFreePool(dc);
1917c2c66affSColin Finck         return Status;
1918c2c66affSColin Finck     }
1919c2c66affSColin Finck 
1920318da0c1SPierre Schweitzer     dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
1921318da0c1SPierre Schweitzer     dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
1922c2c66affSColin Finck 
1923883b1f31SPierre Schweitzer     locked = ExIsResourceAcquiredExclusive(&fcb->nonpaged->dir_children_lock);
1924883b1f31SPierre Schweitzer 
1925883b1f31SPierre Schweitzer     if (!locked)
1926318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, true);
1927c2c66affSColin Finck 
1928c2c66affSColin Finck     if (IsListEmpty(&fcb->dir_children_index))
1929c2c66affSColin Finck         dc->index = 2;
1930c2c66affSColin Finck     else {
1931c2c66affSColin Finck         dir_child* dc2 = CONTAINING_RECORD(fcb->dir_children_index.Blink, dir_child, list_entry_index);
1932c2c66affSColin Finck 
1933c2c66affSColin Finck         dc->index = max(2, dc2->index + 1);
1934c2c66affSColin Finck     }
1935c2c66affSColin Finck 
1936c2c66affSColin Finck     InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
1937c2c66affSColin Finck 
1938c2c66affSColin Finck     insert_dir_child_into_hash_lists(fcb, dc);
1939c2c66affSColin Finck 
1940883b1f31SPierre Schweitzer     if (!locked)
1941c2c66affSColin Finck         ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
1942c2c66affSColin Finck 
1943c2c66affSColin Finck     *pdc = dc;
1944c2c66affSColin Finck 
1945c2c66affSColin Finck     return STATUS_SUCCESS;
1946c2c66affSColin Finck }
1947c2c66affSColin Finck 
inherit_mode(fcb * parfcb,bool is_dir)1948318da0c1SPierre Schweitzer uint32_t inherit_mode(fcb* parfcb, bool is_dir) {
1949318da0c1SPierre Schweitzer     uint32_t mode;
1950c2c66affSColin Finck 
1951c2c66affSColin Finck     if (!parfcb)
1952c2c66affSColin Finck         return 0755;
1953c2c66affSColin Finck 
1954c2c66affSColin Finck     mode = parfcb->inode_item.st_mode & ~S_IFDIR;
1955c2c66affSColin Finck     mode &= ~S_ISVTX; // clear sticky bit
1956c2c66affSColin Finck     mode &= ~S_ISUID; // clear setuid bit
1957c2c66affSColin Finck 
1958c2c66affSColin Finck     if (!is_dir)
1959c2c66affSColin Finck         mode &= ~S_ISGID; // if not directory, clear setgid bit
1960c2c66affSColin Finck 
1961c2c66affSColin Finck     return mode;
1962c2c66affSColin Finck }
1963c2c66affSColin Finck 
file_create_parse_ea(fcb * fcb,FILE_FULL_EA_INFORMATION * ea)1964eb7fbc25SPierre Schweitzer static NTSTATUS file_create_parse_ea(fcb* fcb, FILE_FULL_EA_INFORMATION* ea) {
1965eb7fbc25SPierre Schweitzer     NTSTATUS Status;
1966eb7fbc25SPierre Schweitzer     LIST_ENTRY ealist, *le;
1967318da0c1SPierre Schweitzer     uint16_t size = 0;
1968eb7fbc25SPierre Schweitzer     char* buf;
1969eb7fbc25SPierre Schweitzer 
1970eb7fbc25SPierre Schweitzer     InitializeListHead(&ealist);
1971eb7fbc25SPierre Schweitzer 
1972eb7fbc25SPierre Schweitzer     do {
1973eb7fbc25SPierre Schweitzer         STRING s;
1974318da0c1SPierre Schweitzer         bool found = false;
1975eb7fbc25SPierre Schweitzer 
1976eb7fbc25SPierre Schweitzer         s.Length = s.MaximumLength = ea->EaNameLength;
1977eb7fbc25SPierre Schweitzer         s.Buffer = ea->EaName;
1978eb7fbc25SPierre Schweitzer 
1979eb7fbc25SPierre Schweitzer         RtlUpperString(&s, &s);
1980eb7fbc25SPierre Schweitzer 
1981eb7fbc25SPierre Schweitzer         le = ealist.Flink;
1982eb7fbc25SPierre Schweitzer         while (le != &ealist) {
1983eb7fbc25SPierre Schweitzer             ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
1984eb7fbc25SPierre Schweitzer 
1985eb7fbc25SPierre Schweitzer             if (item->name.Length == s.Length && RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
1986eb7fbc25SPierre Schweitzer                 item->flags = ea->Flags;
1987eb7fbc25SPierre Schweitzer                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
1988eb7fbc25SPierre Schweitzer                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
1989318da0c1SPierre Schweitzer                 found = true;
1990eb7fbc25SPierre Schweitzer                 break;
1991eb7fbc25SPierre Schweitzer             }
1992eb7fbc25SPierre Schweitzer 
1993eb7fbc25SPierre Schweitzer             le = le->Flink;
1994eb7fbc25SPierre Schweitzer         }
1995eb7fbc25SPierre Schweitzer 
1996eb7fbc25SPierre Schweitzer         if (!found) {
1997eb7fbc25SPierre Schweitzer             ea_item* item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
1998eb7fbc25SPierre Schweitzer             if (!item) {
1999eb7fbc25SPierre Schweitzer                 ERR("out of memory\n");
2000eb7fbc25SPierre Schweitzer                 Status = STATUS_INSUFFICIENT_RESOURCES;
2001eb7fbc25SPierre Schweitzer                 goto end;
2002eb7fbc25SPierre Schweitzer             }
2003eb7fbc25SPierre Schweitzer 
2004eb7fbc25SPierre Schweitzer             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
2005eb7fbc25SPierre Schweitzer             item->name.Buffer = ea->EaName;
2006eb7fbc25SPierre Schweitzer 
2007eb7fbc25SPierre Schweitzer             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
2008eb7fbc25SPierre Schweitzer             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
2009eb7fbc25SPierre Schweitzer 
2010eb7fbc25SPierre Schweitzer             item->flags = ea->Flags;
2011eb7fbc25SPierre Schweitzer 
2012eb7fbc25SPierre Schweitzer             InsertTailList(&ealist, &item->list_entry);
2013eb7fbc25SPierre Schweitzer         }
2014eb7fbc25SPierre Schweitzer 
2015eb7fbc25SPierre Schweitzer         if (ea->NextEntryOffset == 0)
2016eb7fbc25SPierre Schweitzer             break;
2017eb7fbc25SPierre Schweitzer 
2018318da0c1SPierre Schweitzer         ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
2019318da0c1SPierre Schweitzer     } while (true);
2020eb7fbc25SPierre Schweitzer 
2021eb7fbc25SPierre Schweitzer     // handle LXSS values
2022eb7fbc25SPierre Schweitzer     le = ealist.Flink;
2023eb7fbc25SPierre Schweitzer     while (le != &ealist) {
2024eb7fbc25SPierre Schweitzer         LIST_ENTRY* le2 = le->Flink;
2025eb7fbc25SPierre Schweitzer         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2026eb7fbc25SPierre Schweitzer 
2027eb7fbc25SPierre Schweitzer         if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
2028318da0c1SPierre Schweitzer             if (item->value.Length < sizeof(uint32_t)) {
2029eb7fbc25SPierre Schweitzer                 ERR("uid value was shorter than expected\n");
2030eb7fbc25SPierre Schweitzer                 Status = STATUS_INVALID_PARAMETER;
2031eb7fbc25SPierre Schweitzer                 goto end;
2032eb7fbc25SPierre Schweitzer             }
2033eb7fbc25SPierre Schweitzer 
2034318da0c1SPierre Schweitzer             RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t));
2035318da0c1SPierre Schweitzer             fcb->sd_dirty = true;
2036318da0c1SPierre Schweitzer             fcb->sd_deleted = false;
2037eb7fbc25SPierre Schweitzer 
2038eb7fbc25SPierre Schweitzer             RemoveEntryList(&item->list_entry);
2039eb7fbc25SPierre Schweitzer             ExFreePool(item);
2040eb7fbc25SPierre Schweitzer         } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
2041318da0c1SPierre Schweitzer             if (item->value.Length < sizeof(uint32_t)) {
2042eb7fbc25SPierre Schweitzer                 ERR("gid value was shorter than expected\n");
2043eb7fbc25SPierre Schweitzer                 Status = STATUS_INVALID_PARAMETER;
2044eb7fbc25SPierre Schweitzer                 goto end;
2045eb7fbc25SPierre Schweitzer             }
2046eb7fbc25SPierre Schweitzer 
2047318da0c1SPierre Schweitzer             RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
2048eb7fbc25SPierre Schweitzer 
2049eb7fbc25SPierre Schweitzer             RemoveEntryList(&item->list_entry);
2050eb7fbc25SPierre Schweitzer             ExFreePool(item);
2051eb7fbc25SPierre Schweitzer         } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
2052318da0c1SPierre Schweitzer             uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
2053318da0c1SPierre Schweitzer             uint32_t val;
2054eb7fbc25SPierre Schweitzer 
2055318da0c1SPierre Schweitzer             if (item->value.Length < sizeof(uint32_t)) {
2056eb7fbc25SPierre Schweitzer                 ERR("mode value was shorter than expected\n");
2057eb7fbc25SPierre Schweitzer                 Status = STATUS_INVALID_PARAMETER;
2058eb7fbc25SPierre Schweitzer                 goto end;
2059eb7fbc25SPierre Schweitzer             }
2060eb7fbc25SPierre Schweitzer 
2061174dfab6SVincent Franchomme             val = *(uint32_t*)item->value.Buffer;
2062eb7fbc25SPierre Schweitzer 
2063eb7fbc25SPierre Schweitzer             fcb->inode_item.st_mode &= ~allowed;
2064eb7fbc25SPierre Schweitzer             fcb->inode_item.st_mode |= val & allowed;
2065eb7fbc25SPierre Schweitzer 
2066eb7fbc25SPierre Schweitzer             if (fcb->type != BTRFS_TYPE_DIRECTORY) {
2067174dfab6SVincent Franchomme                 if (__S_ISTYPE(val, __S_IFCHR)) {
2068eb7fbc25SPierre Schweitzer                     fcb->type = BTRFS_TYPE_CHARDEV;
2069174dfab6SVincent Franchomme                     fcb->inode_item.st_mode &= ~__S_IFMT;
2070174dfab6SVincent Franchomme                     fcb->inode_item.st_mode |= __S_IFCHR;
2071174dfab6SVincent Franchomme                 } else if (__S_ISTYPE(val, __S_IFBLK)) {
2072eb7fbc25SPierre Schweitzer                     fcb->type = BTRFS_TYPE_BLOCKDEV;
2073174dfab6SVincent Franchomme                     fcb->inode_item.st_mode &= ~__S_IFMT;
2074174dfab6SVincent Franchomme                     fcb->inode_item.st_mode |= __S_IFBLK;
2075174dfab6SVincent Franchomme                 } else if (__S_ISTYPE(val, __S_IFIFO)) {
2076eb7fbc25SPierre Schweitzer                     fcb->type = BTRFS_TYPE_FIFO;
2077174dfab6SVincent Franchomme                     fcb->inode_item.st_mode &= ~__S_IFMT;
2078174dfab6SVincent Franchomme                     fcb->inode_item.st_mode |= __S_IFIFO;
2079174dfab6SVincent Franchomme                 } else if (__S_ISTYPE(val, __S_IFSOCK)) {
2080eb7fbc25SPierre Schweitzer                     fcb->type = BTRFS_TYPE_SOCKET;
2081174dfab6SVincent Franchomme                     fcb->inode_item.st_mode &= ~__S_IFMT;
2082174dfab6SVincent Franchomme                     fcb->inode_item.st_mode |= __S_IFSOCK;
2083174dfab6SVincent Franchomme                 }
2084eb7fbc25SPierre Schweitzer             }
2085eb7fbc25SPierre Schweitzer 
2086eb7fbc25SPierre Schweitzer             RemoveEntryList(&item->list_entry);
2087eb7fbc25SPierre Schweitzer             ExFreePool(item);
2088eb7fbc25SPierre Schweitzer         } else if (item->name.Length == sizeof(lxdev) - 1 && RtlCompareMemory(item->name.Buffer, lxdev, item->name.Length) == item->name.Length) {
2089318da0c1SPierre Schweitzer             uint32_t major, minor;
2090eb7fbc25SPierre Schweitzer 
2091318da0c1SPierre Schweitzer             if (item->value.Length < sizeof(uint64_t)) {
2092eb7fbc25SPierre Schweitzer                 ERR("dev value was shorter than expected\n");
2093eb7fbc25SPierre Schweitzer                 Status = STATUS_INVALID_PARAMETER;
2094eb7fbc25SPierre Schweitzer                 goto end;
2095eb7fbc25SPierre Schweitzer             }
2096eb7fbc25SPierre Schweitzer 
2097318da0c1SPierre Schweitzer             major = *(uint32_t*)item->value.Buffer;
2098318da0c1SPierre Schweitzer             minor = *(uint32_t*)&item->value.Buffer[sizeof(uint32_t)];
2099eb7fbc25SPierre Schweitzer 
2100eb7fbc25SPierre Schweitzer             fcb->inode_item.st_rdev = (minor & 0xFFFFF) | ((major & 0xFFFFFFFFFFF) << 20);
2101eb7fbc25SPierre Schweitzer 
2102eb7fbc25SPierre Schweitzer             RemoveEntryList(&item->list_entry);
2103eb7fbc25SPierre Schweitzer             ExFreePool(item);
2104eb7fbc25SPierre Schweitzer         }
2105eb7fbc25SPierre Schweitzer 
2106eb7fbc25SPierre Schweitzer         le = le2;
2107eb7fbc25SPierre Schweitzer     }
2108eb7fbc25SPierre Schweitzer 
2109eb7fbc25SPierre Schweitzer     if (fcb->type != BTRFS_TYPE_CHARDEV && fcb->type != BTRFS_TYPE_BLOCKDEV)
2110eb7fbc25SPierre Schweitzer         fcb->inode_item.st_rdev = 0;
2111eb7fbc25SPierre Schweitzer 
2112eb7fbc25SPierre Schweitzer     if (IsListEmpty(&ealist))
2113eb7fbc25SPierre Schweitzer         return STATUS_SUCCESS;
2114eb7fbc25SPierre Schweitzer 
2115eb7fbc25SPierre Schweitzer     le = ealist.Flink;
2116eb7fbc25SPierre Schweitzer     while (le != &ealist) {
2117eb7fbc25SPierre Schweitzer         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2118eb7fbc25SPierre Schweitzer 
2119eb7fbc25SPierre Schweitzer         if (size % 4 > 0)
2120eb7fbc25SPierre Schweitzer             size += 4 - (size % 4);
2121eb7fbc25SPierre Schweitzer 
2122318da0c1SPierre Schweitzer         size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
2123eb7fbc25SPierre Schweitzer 
2124eb7fbc25SPierre Schweitzer         le = le->Flink;
2125eb7fbc25SPierre Schweitzer     }
2126eb7fbc25SPierre Schweitzer 
2127eb7fbc25SPierre Schweitzer     buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
2128eb7fbc25SPierre Schweitzer     if (!buf) {
2129eb7fbc25SPierre Schweitzer         ERR("out of memory\n");
2130eb7fbc25SPierre Schweitzer         Status = STATUS_INSUFFICIENT_RESOURCES;
2131eb7fbc25SPierre Schweitzer         goto end;
2132eb7fbc25SPierre Schweitzer     }
2133eb7fbc25SPierre Schweitzer 
2134eb7fbc25SPierre Schweitzer     fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
2135eb7fbc25SPierre Schweitzer     fcb->ea_xattr.Buffer = buf;
2136eb7fbc25SPierre Schweitzer 
2137eb7fbc25SPierre Schweitzer     fcb->ealen = 4;
2138eb7fbc25SPierre Schweitzer     ea = NULL;
2139eb7fbc25SPierre Schweitzer 
2140eb7fbc25SPierre Schweitzer     le = ealist.Flink;
2141eb7fbc25SPierre Schweitzer     while (le != &ealist) {
2142eb7fbc25SPierre Schweitzer         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2143eb7fbc25SPierre Schweitzer 
2144eb7fbc25SPierre Schweitzer         if (ea) {
2145eb7fbc25SPierre Schweitzer             ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
2146eb7fbc25SPierre Schweitzer 
2147eb7fbc25SPierre Schweitzer             if (ea->NextEntryOffset % 4 > 0)
2148eb7fbc25SPierre Schweitzer                 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
2149eb7fbc25SPierre Schweitzer 
2150318da0c1SPierre Schweitzer             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
2151eb7fbc25SPierre Schweitzer         } else
2152eb7fbc25SPierre Schweitzer             ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
2153eb7fbc25SPierre Schweitzer 
2154eb7fbc25SPierre Schweitzer         ea->NextEntryOffset = 0;
2155eb7fbc25SPierre Schweitzer         ea->Flags = item->flags;
2156eb7fbc25SPierre Schweitzer         ea->EaNameLength = (UCHAR)item->name.Length;
2157eb7fbc25SPierre Schweitzer         ea->EaValueLength = item->value.Length;
2158eb7fbc25SPierre Schweitzer 
2159eb7fbc25SPierre Schweitzer         RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
2160eb7fbc25SPierre Schweitzer         ea->EaName[item->name.Length] = 0;
2161eb7fbc25SPierre Schweitzer         RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
2162eb7fbc25SPierre Schweitzer 
2163eb7fbc25SPierre Schweitzer         fcb->ealen += 5 + item->name.Length + item->value.Length;
2164eb7fbc25SPierre Schweitzer 
2165eb7fbc25SPierre Schweitzer         le = le->Flink;
2166eb7fbc25SPierre Schweitzer     }
2167eb7fbc25SPierre Schweitzer 
2168318da0c1SPierre Schweitzer     fcb->ea_changed = true;
2169eb7fbc25SPierre Schweitzer 
2170eb7fbc25SPierre Schweitzer     Status = STATUS_SUCCESS;
2171eb7fbc25SPierre Schweitzer 
2172eb7fbc25SPierre Schweitzer end:
2173eb7fbc25SPierre Schweitzer     while (!IsListEmpty(&ealist)) {
2174eb7fbc25SPierre Schweitzer         ea_item* item = CONTAINING_RECORD(RemoveHeadList(&ealist), ea_item, list_entry);
2175eb7fbc25SPierre Schweitzer 
2176eb7fbc25SPierre Schweitzer         ExFreePool(item);
2177eb7fbc25SPierre Schweitzer     }
2178eb7fbc25SPierre Schweitzer 
2179eb7fbc25SPierre Schweitzer     return Status;
2180eb7fbc25SPierre Schweitzer }
2181eb7fbc25SPierre Schweitzer 
2182c2c66affSColin Finck static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ PUNICODE_STRING fpus,
2183c2c66affSColin Finck                              _In_ file_ref* parfileref, _In_ ULONG options, _In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen,
2184318da0c1SPierre Schweitzer                              _Out_ file_ref** pfr, bool case_sensitive, _In_ LIST_ENTRY* rollback) {
2185c2c66affSColin Finck     NTSTATUS Status;
2186c2c66affSColin Finck     fcb* fcb;
2187c2c66affSColin Finck     ULONG utf8len;
2188c2c66affSColin Finck     char* utf8 = NULL;
2189318da0c1SPierre Schweitzer     uint64_t inode;
2190318da0c1SPierre Schweitzer     uint8_t type;
2191c2c66affSColin Finck     LARGE_INTEGER time;
2192c2c66affSColin Finck     BTRFS_TIME now;
2193c2c66affSColin Finck     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2194c2c66affSColin Finck     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
2195c2c66affSColin Finck     USHORT defda;
2196c2c66affSColin Finck     file_ref* fileref;
2197c2c66affSColin Finck     dir_child* dc;
2198c2c66affSColin Finck     ANSI_STRING utf8as;
2199883b1f31SPierre Schweitzer     LIST_ENTRY* lastle = NULL;
2200883b1f31SPierre Schweitzer     file_ref* existing_fileref = NULL;
2201c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
2202c2c66affSColin Finck     LONG rc;
2203c2c66affSColin Finck #endif
2204c2c66affSColin Finck 
2205c2c66affSColin Finck     if (parfileref->fcb == Vcb->dummy_fcb)
2206c2c66affSColin Finck         return STATUS_ACCESS_DENIED;
2207c2c66affSColin Finck 
22084672b2baSPierre Schweitzer     if (options & FILE_DIRECTORY_FILE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_TEMPORARY)
22094672b2baSPierre Schweitzer         return STATUS_INVALID_PARAMETER;
22104672b2baSPierre Schweitzer 
2211318da0c1SPierre Schweitzer     Status = utf16_to_utf8(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
2212c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2213194ea909SVictor Perevertkin         ERR("utf16_to_utf8 returned %08lx\n", Status);
2214c2c66affSColin Finck         return Status;
2215c2c66affSColin Finck     }
2216c2c66affSColin Finck 
2217c2c66affSColin Finck     utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
2218c2c66affSColin Finck     if (!utf8) {
2219c2c66affSColin Finck         ERR("out of memory\n");
2220c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2221c2c66affSColin Finck     }
2222c2c66affSColin Finck 
2223318da0c1SPierre Schweitzer     Status = utf16_to_utf8(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
2224c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2225194ea909SVictor Perevertkin         ERR("utf16_to_utf8 returned %08lx\n", Status);
2226c2c66affSColin Finck         ExFreePool(utf8);
2227c2c66affSColin Finck         return Status;
2228c2c66affSColin Finck     }
2229c2c66affSColin Finck 
2230c2c66affSColin Finck     utf8[utf8len] = 0;
2231c2c66affSColin Finck 
2232c2c66affSColin Finck     KeQuerySystemTime(&time);
2233c2c66affSColin Finck     win_time_to_unix(time, &now);
2234c2c66affSColin Finck 
2235194ea909SVictor Perevertkin     TRACE("create file %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer);
2236318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2237318da0c1SPierre Schweitzer     TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) was %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2238c2c66affSColin Finck     parfileref->fcb->inode_item.st_size += utf8len * 2;
2239318da0c1SPierre Schweitzer     TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) now %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2240c2c66affSColin Finck     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2241c2c66affSColin Finck     parfileref->fcb->inode_item.sequence++;
2242c2c66affSColin Finck     parfileref->fcb->inode_item.st_ctime = now;
2243c2c66affSColin Finck     parfileref->fcb->inode_item.st_mtime = now;
2244c2c66affSColin Finck     ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2245c2c66affSColin Finck 
2246318da0c1SPierre Schweitzer     parfileref->fcb->inode_item_changed = true;
2247c2c66affSColin Finck     mark_fcb_dirty(parfileref->fcb);
2248c2c66affSColin Finck 
2249c2c66affSColin Finck     inode = InterlockedIncrement64(&parfileref->fcb->subvol->lastinode);
2250c2c66affSColin Finck 
2251c2c66affSColin Finck     type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
2252c2c66affSColin Finck 
2253c2c66affSColin Finck     // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
2254c2c66affSColin Finck 
2255c2c66affSColin Finck     TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
2256c2c66affSColin Finck 
22574672b2baSPierre Schweitzer     defda = 0;
2258c2c66affSColin Finck 
2259c2c66affSColin Finck     if (utf8[0] == '.')
2260c2c66affSColin Finck         defda |= FILE_ATTRIBUTE_HIDDEN;
2261c2c66affSColin Finck 
2262c2c66affSColin Finck     if (options & FILE_DIRECTORY_FILE) {
2263c2c66affSColin Finck         defda |= FILE_ATTRIBUTE_DIRECTORY;
2264c2c66affSColin Finck         IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2265c2c66affSColin Finck     } else
2266c2c66affSColin Finck         IrpSp->Parameters.Create.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
2267c2c66affSColin Finck 
22684672b2baSPierre Schweitzer     if (!(IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
22694672b2baSPierre Schweitzer         IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
22704672b2baSPierre Schweitzer         defda |= FILE_ATTRIBUTE_ARCHIVE;
22714672b2baSPierre Schweitzer     }
22724672b2baSPierre Schweitzer 
2273c2c66affSColin Finck     TRACE("defda = %x\n", defda);
2274c2c66affSColin Finck 
2275c2c66affSColin Finck     if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
2276c2c66affSColin Finck         IrpSp->Parameters.Create.FileAttributes = defda;
2277c2c66affSColin Finck 
2278c2c66affSColin Finck     fcb = create_fcb(Vcb, pool_type);
2279c2c66affSColin Finck     if (!fcb) {
2280c2c66affSColin Finck         ERR("out of memory\n");
2281c2c66affSColin Finck         ExFreePool(utf8);
2282c2c66affSColin Finck 
2283318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2284c2c66affSColin Finck         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2285c2c66affSColin Finck         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2286c2c66affSColin Finck 
2287c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2288c2c66affSColin Finck     }
2289c2c66affSColin Finck 
2290c2c66affSColin Finck     fcb->Vcb = Vcb;
2291c2c66affSColin Finck 
2292318da0c1SPierre Schweitzer     if (IrpSp->Flags & SL_OPEN_PAGING_FILE)
2293c2c66affSColin Finck         fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
2294c2c66affSColin Finck 
2295c2c66affSColin Finck     fcb->inode_item.generation = Vcb->superblock.generation;
2296c2c66affSColin Finck     fcb->inode_item.transid = Vcb->superblock.generation;
2297c2c66affSColin Finck     fcb->inode_item.st_size = 0;
2298c2c66affSColin Finck     fcb->inode_item.st_blocks = 0;
2299c2c66affSColin Finck     fcb->inode_item.block_group = 0;
2300c2c66affSColin Finck     fcb->inode_item.st_nlink = 1;
2301c2c66affSColin Finck     fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
2302c2c66affSColin Finck     fcb->inode_item.st_mode = inherit_mode(parfileref->fcb, type == BTRFS_TYPE_DIRECTORY); // use parent's permissions by default
2303c2c66affSColin Finck     fcb->inode_item.st_rdev = 0;
2304c2c66affSColin Finck     fcb->inode_item.flags = 0;
2305c2c66affSColin Finck     fcb->inode_item.sequence = 1;
2306c2c66affSColin Finck     fcb->inode_item.st_atime = now;
2307c2c66affSColin Finck     fcb->inode_item.st_ctime = now;
2308c2c66affSColin Finck     fcb->inode_item.st_mtime = now;
2309c2c66affSColin Finck     fcb->inode_item.otime = now;
2310c2c66affSColin Finck 
2311c2c66affSColin Finck     if (type == BTRFS_TYPE_DIRECTORY)
2312c2c66affSColin Finck         fcb->inode_item.st_mode |= S_IFDIR;
2313c2c66affSColin Finck     else {
2314c2c66affSColin Finck         fcb->inode_item.st_mode |= S_IFREG;
2315c2c66affSColin Finck         fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
2316c2c66affSColin Finck     }
2317c2c66affSColin Finck 
2318c2c66affSColin Finck     if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
2319c2c66affSColin Finck         fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
2320c2c66affSColin Finck     } else {
2321c2c66affSColin Finck         // inherit nodatacow flag from parent directory
2322*29d19382SJohannes Obermayr         if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW || Vcb->options.nodatacow) {
2323c2c66affSColin Finck             fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
2324c2c66affSColin Finck 
2325c2c66affSColin Finck             if (type != BTRFS_TYPE_DIRECTORY)
2326c2c66affSColin Finck                 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
2327c2c66affSColin Finck         }
2328c2c66affSColin Finck 
2329*29d19382SJohannes Obermayr         if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS &&
2330*29d19382SJohannes Obermayr             !(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
2331c2c66affSColin Finck             fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
2332c2c66affSColin Finck         }
2333*29d19382SJohannes Obermayr     }
2334c2c66affSColin Finck 
2335*29d19382SJohannes Obermayr     if (!(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
2336c2c66affSColin Finck         fcb->prop_compression = parfileref->fcb->prop_compression;
2337c2c66affSColin Finck         fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
2338*29d19382SJohannes Obermayr     } else
2339*29d19382SJohannes Obermayr         fcb->prop_compression = PropCompression_None;
2340c2c66affSColin Finck 
2341318da0c1SPierre Schweitzer     fcb->inode_item_changed = true;
2342c2c66affSColin Finck 
2343c2c66affSColin Finck     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2344c2c66affSColin Finck     fcb->Header.AllocationSize.QuadPart = 0;
2345c2c66affSColin Finck     fcb->Header.FileSize.QuadPart = 0;
2346c2c66affSColin Finck     fcb->Header.ValidDataLength.QuadPart = 0;
2347c2c66affSColin Finck 
2348c2c66affSColin Finck     fcb->atts = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
2349c2c66affSColin Finck     fcb->atts_changed = fcb->atts != defda;
2350c2c66affSColin Finck 
2351c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
2352c2c66affSColin Finck     rc = InterlockedIncrement(&parfileref->fcb->refcount);
235362e630deSPierre Schweitzer     WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2354c2c66affSColin Finck #else
2355c2c66affSColin Finck     InterlockedIncrement(&parfileref->fcb->refcount);
2356c2c66affSColin Finck #endif
2357c2c66affSColin Finck     fcb->subvol = parfileref->fcb->subvol;
2358c2c66affSColin Finck     fcb->inode = inode;
2359c2c66affSColin Finck     fcb->type = type;
2360318da0c1SPierre Schweitzer     fcb->created = true;
2361318da0c1SPierre Schweitzer     fcb->deleted = true;
2362c2c66affSColin Finck 
2363318da0c1SPierre Schweitzer     fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
2364883b1f31SPierre Schweitzer 
2365883b1f31SPierre Schweitzer     acquire_fcb_lock_exclusive(Vcb);
2366883b1f31SPierre Schweitzer 
2367883b1f31SPierre Schweitzer     if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
2368883b1f31SPierre Schweitzer         LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
2369883b1f31SPierre Schweitzer 
2370883b1f31SPierre Schweitzer         while (le != &fcb->subvol->fcbs) {
2371883b1f31SPierre Schweitzer             struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
2372883b1f31SPierre Schweitzer 
2373883b1f31SPierre Schweitzer             if (fcb2->hash > fcb->hash) {
2374883b1f31SPierre Schweitzer                 lastle = le->Blink;
2375883b1f31SPierre Schweitzer                 break;
2376883b1f31SPierre Schweitzer             }
2377883b1f31SPierre Schweitzer 
2378883b1f31SPierre Schweitzer             le = le->Flink;
2379883b1f31SPierre Schweitzer         }
2380883b1f31SPierre Schweitzer     }
2381883b1f31SPierre Schweitzer 
2382883b1f31SPierre Schweitzer     if (!lastle) {
2383318da0c1SPierre Schweitzer         uint8_t c = fcb->hash >> 24;
2384883b1f31SPierre Schweitzer 
2385883b1f31SPierre Schweitzer         if (c != 0xff) {
2386318da0c1SPierre Schweitzer             uint8_t d = c + 1;
2387883b1f31SPierre Schweitzer 
2388883b1f31SPierre Schweitzer             do {
2389883b1f31SPierre Schweitzer                 if (fcb->subvol->fcbs_ptrs[d]) {
2390883b1f31SPierre Schweitzer                     lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
2391883b1f31SPierre Schweitzer                     break;
2392883b1f31SPierre Schweitzer                 }
2393883b1f31SPierre Schweitzer 
2394883b1f31SPierre Schweitzer                 d++;
2395883b1f31SPierre Schweitzer             } while (d != 0);
2396883b1f31SPierre Schweitzer         }
2397883b1f31SPierre Schweitzer     }
2398883b1f31SPierre Schweitzer 
2399883b1f31SPierre Schweitzer     if (lastle) {
2400883b1f31SPierre Schweitzer         InsertHeadList(lastle, &fcb->list_entry);
2401883b1f31SPierre Schweitzer 
2402883b1f31SPierre Schweitzer         if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2403883b1f31SPierre Schweitzer             fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2404883b1f31SPierre Schweitzer     } else {
2405883b1f31SPierre Schweitzer         InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
2406883b1f31SPierre Schweitzer 
2407883b1f31SPierre Schweitzer         if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2408883b1f31SPierre Schweitzer             fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2409883b1f31SPierre Schweitzer     }
2410883b1f31SPierre Schweitzer 
2411883b1f31SPierre Schweitzer     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2412883b1f31SPierre Schweitzer 
2413883b1f31SPierre Schweitzer     fcb->subvol->fcbs_version++;
2414883b1f31SPierre Schweitzer 
2415883b1f31SPierre Schweitzer     release_fcb_lock(Vcb);
2416883b1f31SPierre Schweitzer 
2417c2c66affSColin Finck     mark_fcb_dirty(fcb);
2418c2c66affSColin Finck 
2419c2c66affSColin Finck     Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
2420c2c66affSColin Finck 
2421c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2422194ea909SVictor Perevertkin         ERR("fcb_get_new_sd returned %08lx\n", Status);
2423883b1f31SPierre Schweitzer         free_fcb(fcb);
2424c2c66affSColin Finck 
2425318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2426c2c66affSColin Finck         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2427c2c66affSColin Finck         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2428c2c66affSColin Finck 
2429883b1f31SPierre Schweitzer         ExFreePool(utf8);
2430883b1f31SPierre Schweitzer 
2431c2c66affSColin Finck         return Status;
2432c2c66affSColin Finck     }
2433c2c66affSColin Finck 
2434318da0c1SPierre Schweitzer     fcb->sd_dirty = true;
2435c2c66affSColin Finck 
2436c2c66affSColin Finck     if (ea && ealen > 0) {
2437eb7fbc25SPierre Schweitzer         Status = file_create_parse_ea(fcb, ea);
2438eb7fbc25SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
2439194ea909SVictor Perevertkin             ERR("file_create_parse_ea returned %08lx\n", Status);
2440883b1f31SPierre Schweitzer             free_fcb(fcb);
2441c2c66affSColin Finck 
2442318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2443c2c66affSColin Finck             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2444c2c66affSColin Finck             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2445c2c66affSColin Finck 
2446883b1f31SPierre Schweitzer             ExFreePool(utf8);
2447883b1f31SPierre Schweitzer 
2448eb7fbc25SPierre Schweitzer             return Status;
2449c2c66affSColin Finck         }
2450c2c66affSColin Finck     }
2451c2c66affSColin Finck 
2452c2c66affSColin Finck     fileref = create_fileref(Vcb);
2453c2c66affSColin Finck     if (!fileref) {
2454c2c66affSColin Finck         ERR("out of memory\n");
2455883b1f31SPierre Schweitzer         free_fcb(fcb);
2456c2c66affSColin Finck 
2457318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2458c2c66affSColin Finck         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2459c2c66affSColin Finck         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2460c2c66affSColin Finck 
2461883b1f31SPierre Schweitzer         ExFreePool(utf8);
2462883b1f31SPierre Schweitzer 
2463c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2464c2c66affSColin Finck     }
2465c2c66affSColin Finck 
2466c2c66affSColin Finck     fileref->fcb = fcb;
2467c2c66affSColin Finck 
24686e0cf03dSVincent Franchomme     if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb) && fcb->type != BTRFS_TYPE_DIRECTORY) {
2469318da0c1SPierre Schweitzer         Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
2470c2c66affSColin Finck 
2471c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2472194ea909SVictor Perevertkin             ERR("extend_file returned %08lx\n", Status);
2473883b1f31SPierre Schweitzer             reap_fileref(Vcb, fileref);
2474c2c66affSColin Finck 
2475318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2476c2c66affSColin Finck             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2477c2c66affSColin Finck             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2478c2c66affSColin Finck 
2479883b1f31SPierre Schweitzer             ExFreePool(utf8);
2480883b1f31SPierre Schweitzer 
2481c2c66affSColin Finck             return Status;
2482c2c66affSColin Finck         }
2483c2c66affSColin Finck     }
2484c2c66affSColin Finck 
2485c2c66affSColin Finck     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2486c2c66affSColin Finck         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
2487c2c66affSColin Finck         if (!fcb->hash_ptrs) {
2488c2c66affSColin Finck             ERR("out of memory\n");
2489883b1f31SPierre Schweitzer             reap_fileref(Vcb, fileref);
2490c2c66affSColin Finck 
2491318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2492c2c66affSColin Finck             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2493c2c66affSColin Finck             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2494c2c66affSColin Finck 
2495883b1f31SPierre Schweitzer             ExFreePool(utf8);
2496883b1f31SPierre Schweitzer 
2497c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
2498c2c66affSColin Finck         }
2499c2c66affSColin Finck 
2500c2c66affSColin Finck         RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
2501c2c66affSColin Finck 
2502c2c66affSColin Finck         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
2503c2c66affSColin Finck         if (!fcb->hash_ptrs_uc) {
2504c2c66affSColin Finck             ERR("out of memory\n");
2505883b1f31SPierre Schweitzer             reap_fileref(Vcb, fileref);
2506c2c66affSColin Finck 
2507318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2508c2c66affSColin Finck             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2509c2c66affSColin Finck             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2510c2c66affSColin Finck 
2511883b1f31SPierre Schweitzer             ExFreePool(utf8);
2512883b1f31SPierre Schweitzer 
2513c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
2514c2c66affSColin Finck         }
2515c2c66affSColin Finck 
2516c2c66affSColin Finck         RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
2517c2c66affSColin Finck     }
2518c2c66affSColin Finck 
2519318da0c1SPierre Schweitzer     fcb->deleted = false;
2520c2c66affSColin Finck 
2521318da0c1SPierre Schweitzer     fileref->created = true;
2522c2c66affSColin Finck 
2523c2c66affSColin Finck     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2524c2c66affSColin Finck     fcb->subvol->root_item.ctime = now;
2525c2c66affSColin Finck 
2526c2c66affSColin Finck     utf8as.Buffer = utf8;
2527318da0c1SPierre Schweitzer     utf8as.Length = utf8as.MaximumLength = (uint16_t)utf8len;
2528c2c66affSColin Finck 
2529318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2530883b1f31SPierre Schweitzer 
2531883b1f31SPierre Schweitzer     // check again doesn't already exist
2532883b1f31SPierre Schweitzer     if (case_sensitive) {
2533318da0c1SPierre Schweitzer         uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpus->Buffer, fpus->Length);
2534883b1f31SPierre Schweitzer 
2535883b1f31SPierre Schweitzer         if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) {
2536883b1f31SPierre Schweitzer             LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24];
2537883b1f31SPierre Schweitzer             while (le != &parfileref->fcb->dir_children_hash) {
2538318da0c1SPierre Schweitzer                 dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
2539883b1f31SPierre Schweitzer 
2540883b1f31SPierre Schweitzer                 if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) {
2541883b1f31SPierre Schweitzer                     existing_fileref = dc->fileref;
2542883b1f31SPierre Schweitzer                     break;
2543883b1f31SPierre Schweitzer                 } else if (dc->hash > dc_hash)
2544883b1f31SPierre Schweitzer                     break;
2545883b1f31SPierre Schweitzer 
2546883b1f31SPierre Schweitzer                 le = le->Flink;
2547883b1f31SPierre Schweitzer             }
2548883b1f31SPierre Schweitzer         }
2549883b1f31SPierre Schweitzer     } else {
2550883b1f31SPierre Schweitzer         UNICODE_STRING fpusuc;
2551883b1f31SPierre Schweitzer 
2552318da0c1SPierre Schweitzer         Status = RtlUpcaseUnicodeString(&fpusuc, fpus, true);
2553883b1f31SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
2554883b1f31SPierre Schweitzer             ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2555194ea909SVictor Perevertkin             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2556883b1f31SPierre Schweitzer             reap_fileref(Vcb, fileref);
2557883b1f31SPierre Schweitzer 
2558318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2559883b1f31SPierre Schweitzer             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2560883b1f31SPierre Schweitzer             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2561c2c66affSColin Finck 
2562c2c66affSColin Finck             ExFreePool(utf8);
2563c2c66affSColin Finck 
2564883b1f31SPierre Schweitzer             return Status;
2565883b1f31SPierre Schweitzer         }
2566883b1f31SPierre Schweitzer 
256762e630deSPierre Schweitzer         uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
2568883b1f31SPierre Schweitzer 
2569883b1f31SPierre Schweitzer         if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
2570883b1f31SPierre Schweitzer             LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24];
2571883b1f31SPierre Schweitzer             while (le != &parfileref->fcb->dir_children_hash_uc) {
2572318da0c1SPierre Schweitzer                 dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
2573883b1f31SPierre Schweitzer 
2574883b1f31SPierre Schweitzer                 if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) {
2575883b1f31SPierre Schweitzer                     existing_fileref = dc->fileref;
2576883b1f31SPierre Schweitzer                     break;
2577883b1f31SPierre Schweitzer                 } else if (dc->hash_uc > dc_hash)
2578883b1f31SPierre Schweitzer                     break;
2579883b1f31SPierre Schweitzer 
2580883b1f31SPierre Schweitzer                 le = le->Flink;
2581883b1f31SPierre Schweitzer             }
2582883b1f31SPierre Schweitzer         }
2583883b1f31SPierre Schweitzer 
2584883b1f31SPierre Schweitzer         ExFreePool(fpusuc.Buffer);
2585883b1f31SPierre Schweitzer     }
2586883b1f31SPierre Schweitzer 
2587883b1f31SPierre Schweitzer     if (existing_fileref) {
2588883b1f31SPierre Schweitzer         ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2589883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2590883b1f31SPierre Schweitzer 
2591318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2592883b1f31SPierre Schweitzer         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2593883b1f31SPierre Schweitzer         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2594883b1f31SPierre Schweitzer 
2595883b1f31SPierre Schweitzer         ExFreePool(utf8);
2596883b1f31SPierre Schweitzer 
2597883b1f31SPierre Schweitzer         increase_fileref_refcount(existing_fileref);
2598883b1f31SPierre Schweitzer         *pfr = existing_fileref;
2599883b1f31SPierre Schweitzer 
2600883b1f31SPierre Schweitzer         return STATUS_OBJECT_NAME_COLLISION;
2601883b1f31SPierre Schweitzer     }
2602883b1f31SPierre Schweitzer 
2603318da0c1SPierre Schweitzer     Status = add_dir_child(parfileref->fcb, fcb->inode, false, &utf8as, fpus, fcb->type, &dc);
2604883b1f31SPierre Schweitzer     if (!NT_SUCCESS(Status)) {
2605883b1f31SPierre Schweitzer         ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2606194ea909SVictor Perevertkin         ERR("add_dir_child returned %08lx\n", Status);
2607883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2608883b1f31SPierre Schweitzer 
2609318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2610883b1f31SPierre Schweitzer         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2611883b1f31SPierre Schweitzer         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2612883b1f31SPierre Schweitzer 
2613883b1f31SPierre Schweitzer         ExFreePool(utf8);
2614883b1f31SPierre Schweitzer 
2615883b1f31SPierre Schweitzer         return Status;
2616883b1f31SPierre Schweitzer     }
2617883b1f31SPierre Schweitzer 
2618883b1f31SPierre Schweitzer     fileref->parent = parfileref;
2619c2c66affSColin Finck     fileref->dc = dc;
2620c2c66affSColin Finck     dc->fileref = fileref;
2621c2c66affSColin Finck 
2622c2c66affSColin Finck     if (type == BTRFS_TYPE_DIRECTORY)
2623c2c66affSColin Finck         fileref->fcb->fileref = fileref;
2624c2c66affSColin Finck 
2625883b1f31SPierre Schweitzer     InsertTailList(&parfileref->children, &fileref->list_entry);
2626883b1f31SPierre Schweitzer     ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2627883b1f31SPierre Schweitzer 
2628883b1f31SPierre Schweitzer     ExFreePool(utf8);
2629883b1f31SPierre Schweitzer 
2630883b1f31SPierre Schweitzer     mark_fileref_dirty(fileref);
2631883b1f31SPierre Schweitzer     increase_fileref_refcount(parfileref);
2632883b1f31SPierre Schweitzer 
2633883b1f31SPierre Schweitzer     *pfr = fileref;
2634883b1f31SPierre Schweitzer 
263562e630deSPierre Schweitzer     TRACE("created new file in subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
2636c2c66affSColin Finck 
2637c2c66affSColin Finck     return STATUS_SUCCESS;
2638c2c66affSColin Finck }
2639c2c66affSColin Finck 
2640c2c66affSColin Finck static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
2641c2c66affSColin Finck                               file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP Irp,
2642318da0c1SPierre Schweitzer                               ULONG options, POOL_TYPE pool_type, bool case_sensitive, LIST_ENTRY* rollback) {
2643c2c66affSColin Finck     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2644c2c66affSColin Finck     file_ref *fileref, *newpar, *parfileref;
2645c2c66affSColin Finck     fcb* fcb;
2646eb7fbc25SPierre Schweitzer     static const char xapref[] = "user.";
2647eb7fbc25SPierre Schweitzer     static const WCHAR DOSATTRIB[] = L"DOSATTRIB";
2648eb7fbc25SPierre Schweitzer     static const WCHAR EA[] = L"EA";
2649eb7fbc25SPierre Schweitzer     static const WCHAR reparse[] = L"reparse";
2650f381137cSPierre Schweitzer     static const WCHAR casesensitive_str[] = L"casesensitive";
2651c2c66affSColin Finck     LARGE_INTEGER time;
2652c2c66affSColin Finck     BTRFS_TIME now;
2653c2c66affSColin Finck     ULONG utf8len, overhead;
2654c2c66affSColin Finck     NTSTATUS Status;
2655c2c66affSColin Finck     KEY searchkey;
2656c2c66affSColin Finck     traverse_ptr tp;
2657c2c66affSColin Finck     dir_child* dc;
2658883b1f31SPierre Schweitzer     dir_child* existing_dc = NULL;
2659c2c66affSColin Finck     ACCESS_MASK granted_access;
2660c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
2661c2c66affSColin Finck     LONG rc;
2662c2c66affSColin Finck #endif
2663c2c66affSColin Finck 
2664194ea909SVictor Perevertkin     TRACE("fpus = %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer);
2665194ea909SVictor Perevertkin     TRACE("stream = %.*S\n", (int)(stream->Length / sizeof(WCHAR)), stream->Buffer);
2666c2c66affSColin Finck 
2667c2c66affSColin Finck     parfileref = *pparfileref;
2668c2c66affSColin Finck 
2669c2c66affSColin Finck     if (parfileref->fcb == Vcb->dummy_fcb)
2670c2c66affSColin Finck         return STATUS_ACCESS_DENIED;
2671c2c66affSColin Finck 
26726e0cf03dSVincent Franchomme     Status = check_file_name_valid(stream, false, true);
26736e0cf03dSVincent Franchomme     if (!NT_SUCCESS(Status))
26746e0cf03dSVincent Franchomme         return Status;
26756e0cf03dSVincent Franchomme 
2676318da0c1SPierre Schweitzer     Status = open_fileref(Vcb, &newpar, fpus, parfileref, false, NULL, NULL, PagedPool, case_sensitive, Irp);
2677c2c66affSColin Finck 
2678c2c66affSColin Finck     if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
2679c2c66affSColin Finck         UNICODE_STRING fpus2;
2680c2c66affSColin Finck 
26816e0cf03dSVincent Franchomme         Status = check_file_name_valid(fpus, false, false);
2682c982533eSVincent Franchomme         if (!NT_SUCCESS(Status))
2683c982533eSVincent Franchomme             return Status;
2684c2c66affSColin Finck 
2685c2c66affSColin Finck         fpus2.Length = fpus2.MaximumLength = fpus->Length;
2686c2c66affSColin Finck         fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
2687c2c66affSColin Finck 
2688c2c66affSColin Finck         if (!fpus2.Buffer) {
2689c2c66affSColin Finck             ERR("out of memory\n");
2690c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
2691c2c66affSColin Finck         }
2692c2c66affSColin Finck 
2693c2c66affSColin Finck         RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
2694c2c66affSColin Finck 
2695c2c66affSColin Finck         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2696c2c66affSColin Finck 
2697c2c66affSColin Finck         if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2698318da0c1SPierre Schweitzer                            true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
2699c2c66affSColin Finck                            IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
2700c2c66affSColin Finck                            &granted_access, &Status)) {
2701c2c66affSColin Finck             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2702c2c66affSColin Finck             return Status;
2703c2c66affSColin Finck         }
2704c2c66affSColin Finck 
2705c2c66affSColin Finck         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2706c2c66affSColin Finck 
2707883b1f31SPierre Schweitzer         Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
2708c2c66affSColin Finck 
2709c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2710194ea909SVictor Perevertkin             ERR("file_create2 returned %08lx\n", Status);
2711c2c66affSColin Finck             ExFreePool(fpus2.Buffer);
2712c2c66affSColin Finck             return Status;
2713883b1f31SPierre Schweitzer         } else if (Status != STATUS_OBJECT_NAME_COLLISION) {
2714c2c66affSColin Finck             send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
271562e630deSPierre Schweitzer             queue_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2716883b1f31SPierre Schweitzer         }
2717883b1f31SPierre Schweitzer 
2718883b1f31SPierre Schweitzer         ExFreePool(fpus2.Buffer);
2719c2c66affSColin Finck     } else if (!NT_SUCCESS(Status)) {
2720194ea909SVictor Perevertkin         ERR("open_fileref returned %08lx\n", Status);
2721c2c66affSColin Finck         return Status;
2722c2c66affSColin Finck     }
2723c2c66affSColin Finck 
2724c2c66affSColin Finck     parfileref = newpar;
2725c2c66affSColin Finck     *pparfileref = parfileref;
2726c2c66affSColin Finck 
2727c2c66affSColin Finck     if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
2728c2c66affSColin Finck         WARN("parent not file, directory, or symlink\n");
2729883b1f31SPierre Schweitzer         free_fileref(parfileref);
2730c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
2731c2c66affSColin Finck     }
2732c2c66affSColin Finck 
2733c2c66affSColin Finck     if (options & FILE_DIRECTORY_FILE) {
2734c2c66affSColin Finck         WARN("tried to create directory as stream\n");
2735883b1f31SPierre Schweitzer         free_fileref(parfileref);
2736c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
2737c2c66affSColin Finck     }
2738c2c66affSColin Finck 
2739174dfab6SVincent Franchomme     if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) {
2740883b1f31SPierre Schweitzer         free_fileref(parfileref);
2741c2c66affSColin Finck         return STATUS_ACCESS_DENIED;
2742883b1f31SPierre Schweitzer     }
2743c2c66affSColin Finck 
2744c2c66affSColin Finck     SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2745c2c66affSColin Finck 
2746c2c66affSColin Finck     if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2747318da0c1SPierre Schweitzer                        true, FILE_WRITE_DATA, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
2748c2c66affSColin Finck                        &granted_access, &Status)) {
2749c2c66affSColin Finck         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2750883b1f31SPierre Schweitzer         free_fileref(parfileref);
2751c2c66affSColin Finck         return Status;
2752c2c66affSColin Finck     }
2753c2c66affSColin Finck 
2754c2c66affSColin Finck     SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2755c2c66affSColin Finck 
2756eb7fbc25SPierre Schweitzer     if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
2757eb7fbc25SPierre Schweitzer         (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
2758f381137cSPierre Schweitzer         (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length) ||
2759f381137cSPierre Schweitzer         (stream->Length == sizeof(casesensitive_str) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, casesensitive_str, stream->Length) == stream->Length)) {
2760883b1f31SPierre Schweitzer         free_fileref(parfileref);
2761c2c66affSColin Finck         return STATUS_OBJECT_NAME_INVALID;
2762c2c66affSColin Finck     }
2763c2c66affSColin Finck 
2764c2c66affSColin Finck     fcb = create_fcb(Vcb, pool_type);
2765c2c66affSColin Finck     if (!fcb) {
2766c2c66affSColin Finck         ERR("out of memory\n");
2767883b1f31SPierre Schweitzer         free_fileref(parfileref);
2768c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2769c2c66affSColin Finck     }
2770c2c66affSColin Finck 
2771c2c66affSColin Finck     fcb->Vcb = Vcb;
2772c2c66affSColin Finck 
2773c2c66affSColin Finck     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2774c2c66affSColin Finck     fcb->Header.AllocationSize.QuadPart = 0;
2775c2c66affSColin Finck     fcb->Header.FileSize.QuadPart = 0;
2776c2c66affSColin Finck     fcb->Header.ValidDataLength.QuadPart = 0;
2777c2c66affSColin Finck 
2778c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
2779c2c66affSColin Finck     rc = InterlockedIncrement(&parfileref->fcb->refcount);
278062e630deSPierre Schweitzer     WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2781c2c66affSColin Finck #else
2782c2c66affSColin Finck     InterlockedIncrement(&parfileref->fcb->refcount);
2783c2c66affSColin Finck #endif
2784c2c66affSColin Finck     fcb->subvol = parfileref->fcb->subvol;
2785c2c66affSColin Finck     fcb->inode = parfileref->fcb->inode;
2786c982533eSVincent Franchomme     fcb->hash = parfileref->fcb->hash;
2787c2c66affSColin Finck     fcb->type = parfileref->fcb->type;
2788c2c66affSColin Finck 
2789318da0c1SPierre Schweitzer     fcb->ads = true;
2790c2c66affSColin Finck 
2791318da0c1SPierre Schweitzer     Status = utf16_to_utf8(NULL, 0, &utf8len, stream->Buffer, stream->Length);
2792c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2793194ea909SVictor Perevertkin         ERR("utf16_to_utf8 1 returned %08lx\n", Status);
2794883b1f31SPierre Schweitzer         reap_fcb(fcb);
2795883b1f31SPierre Schweitzer         free_fileref(parfileref);
2796c2c66affSColin Finck         return Status;
2797c2c66affSColin Finck     }
2798c2c66affSColin Finck 
2799318da0c1SPierre Schweitzer     fcb->adsxattr.Length = (uint16_t)utf8len + sizeof(xapref) - 1;
2800c2c66affSColin Finck     fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
2801c2c66affSColin Finck     fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
2802c2c66affSColin Finck     if (!fcb->adsxattr.Buffer) {
2803c2c66affSColin Finck         ERR("out of memory\n");
2804883b1f31SPierre Schweitzer         reap_fcb(fcb);
2805883b1f31SPierre Schweitzer         free_fileref(parfileref);
2806c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2807c2c66affSColin Finck     }
2808c2c66affSColin Finck 
2809eb7fbc25SPierre Schweitzer     RtlCopyMemory(fcb->adsxattr.Buffer, xapref, sizeof(xapref) - 1);
2810c2c66affSColin Finck 
2811318da0c1SPierre Schweitzer     Status = utf16_to_utf8(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length);
2812c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2813194ea909SVictor Perevertkin         ERR("utf16_to_utf8 2 returned %08lx\n", Status);
2814883b1f31SPierre Schweitzer         reap_fcb(fcb);
2815883b1f31SPierre Schweitzer         free_fileref(parfileref);
2816c2c66affSColin Finck         return Status;
2817c2c66affSColin Finck     }
2818c2c66affSColin Finck 
2819c2c66affSColin Finck     fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
2820c2c66affSColin Finck 
2821c2c66affSColin Finck     TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
2822c2c66affSColin Finck 
2823318da0c1SPierre Schweitzer     fcb->adshash = calc_crc32c(0xfffffffe, (uint8_t*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
2824c2c66affSColin Finck     TRACE("adshash = %08x\n", fcb->adshash);
2825c2c66affSColin Finck 
2826c2c66affSColin Finck     searchkey.obj_id = parfileref->fcb->inode;
2827c2c66affSColin Finck     searchkey.obj_type = TYPE_XATTR_ITEM;
2828c2c66affSColin Finck     searchkey.offset = fcb->adshash;
2829c2c66affSColin Finck 
2830318da0c1SPierre Schweitzer     Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, false, Irp);
2831c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2832194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
2833883b1f31SPierre Schweitzer         reap_fcb(fcb);
2834883b1f31SPierre Schweitzer         free_fileref(parfileref);
2835c2c66affSColin Finck         return Status;
2836c2c66affSColin Finck     }
2837c2c66affSColin Finck 
2838c2c66affSColin Finck     if (!keycmp(tp.item->key, searchkey))
2839c2c66affSColin Finck         overhead = tp.item->size;
2840c2c66affSColin Finck     else
2841c2c66affSColin Finck         overhead = 0;
2842c2c66affSColin Finck 
2843c2c66affSColin Finck     fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
2844c2c66affSColin Finck 
2845eb7fbc25SPierre Schweitzer     if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
2846*29d19382SJohannes Obermayr         WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)\n", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
2847883b1f31SPierre Schweitzer         reap_fcb(fcb);
2848883b1f31SPierre Schweitzer         free_fileref(parfileref);
2849c2c66affSColin Finck         return STATUS_DISK_FULL;
2850c2c66affSColin Finck     } else
2851eb7fbc25SPierre Schweitzer         fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
2852c2c66affSColin Finck 
2853318da0c1SPierre Schweitzer     fcb->created = true;
2854318da0c1SPierre Schweitzer     fcb->deleted = true;
2855883b1f31SPierre Schweitzer 
2856883b1f31SPierre Schweitzer     acquire_fcb_lock_exclusive(Vcb);
2857883b1f31SPierre Schweitzer     InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
2858883b1f31SPierre Schweitzer     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2859883b1f31SPierre Schweitzer     parfileref->fcb->subvol->fcbs_version++;
2860883b1f31SPierre Schweitzer     release_fcb_lock(Vcb);
2861883b1f31SPierre Schweitzer 
2862883b1f31SPierre Schweitzer     mark_fcb_dirty(fcb);
2863883b1f31SPierre Schweitzer 
2864c2c66affSColin Finck     fileref = create_fileref(Vcb);
2865c2c66affSColin Finck     if (!fileref) {
2866c2c66affSColin Finck         ERR("out of memory\n");
2867883b1f31SPierre Schweitzer         free_fcb(fcb);
2868883b1f31SPierre Schweitzer         free_fileref(parfileref);
2869c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2870c2c66affSColin Finck     }
2871c2c66affSColin Finck 
2872c2c66affSColin Finck     fileref->fcb = fcb;
2873c2c66affSColin Finck 
2874c2c66affSColin Finck     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
2875c2c66affSColin Finck     if (!dc) {
2876c2c66affSColin Finck         ERR("out of memory\n");
2877883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2878883b1f31SPierre Schweitzer         free_fileref(parfileref);
2879c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2880c2c66affSColin Finck     }
2881c2c66affSColin Finck 
2882c2c66affSColin Finck     RtlZeroMemory(dc, sizeof(dir_child));
2883c2c66affSColin Finck 
2884eb7fbc25SPierre Schweitzer     dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length + 1 - sizeof(xapref);
2885c2c66affSColin Finck     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
2886c2c66affSColin Finck     if (!dc->utf8.Buffer) {
2887c2c66affSColin Finck         ERR("out of memory\n");
2888c2c66affSColin Finck         ExFreePool(dc);
2889883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2890883b1f31SPierre Schweitzer         free_fileref(parfileref);
2891c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2892c2c66affSColin Finck     }
2893c2c66affSColin Finck 
2894eb7fbc25SPierre Schweitzer     RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[sizeof(xapref) - 1], fcb->adsxattr.Length + 1 - sizeof(xapref));
2895c2c66affSColin Finck 
2896c2c66affSColin Finck     dc->name.MaximumLength = dc->name.Length = stream->Length;
2897c2c66affSColin Finck     dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG);
2898c2c66affSColin Finck     if (!dc->name.Buffer) {
2899c2c66affSColin Finck         ERR("out of memory\n");
2900c2c66affSColin Finck         ExFreePool(dc->utf8.Buffer);
2901c2c66affSColin Finck         ExFreePool(dc);
2902883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2903883b1f31SPierre Schweitzer         free_fileref(parfileref);
2904c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2905c2c66affSColin Finck     }
2906c2c66affSColin Finck 
2907c2c66affSColin Finck     RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
2908c2c66affSColin Finck 
2909318da0c1SPierre Schweitzer     Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
2910c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2911194ea909SVictor Perevertkin         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2912c2c66affSColin Finck         ExFreePool(dc->utf8.Buffer);
2913c2c66affSColin Finck         ExFreePool(dc->name.Buffer);
2914c2c66affSColin Finck         ExFreePool(dc);
2915883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2916883b1f31SPierre Schweitzer         free_fileref(parfileref);
2917c2c66affSColin Finck         return Status;
2918c2c66affSColin Finck     }
2919c2c66affSColin Finck 
2920883b1f31SPierre Schweitzer     KeQuerySystemTime(&time);
2921883b1f31SPierre Schweitzer     win_time_to_unix(time, &now);
2922883b1f31SPierre Schweitzer 
2923318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2924883b1f31SPierre Schweitzer 
2925883b1f31SPierre Schweitzer     LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink;
2926883b1f31SPierre Schweitzer     while (le != &parfileref->fcb->dir_children_index) {
2927883b1f31SPierre Schweitzer         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
2928883b1f31SPierre Schweitzer 
2929883b1f31SPierre Schweitzer         if (dc2->index == 0) {
2930883b1f31SPierre Schweitzer             if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) ||
2931883b1f31SPierre Schweitzer                 (!case_sensitive && dc2->name_uc.Length == dc->name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, dc->name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
2932883b1f31SPierre Schweitzer             ) {
2933883b1f31SPierre Schweitzer                 existing_dc = dc2;
2934883b1f31SPierre Schweitzer                 break;
2935883b1f31SPierre Schweitzer             }
2936883b1f31SPierre Schweitzer         } else
2937883b1f31SPierre Schweitzer             break;
2938883b1f31SPierre Schweitzer 
2939883b1f31SPierre Schweitzer         le = le->Flink;
2940883b1f31SPierre Schweitzer     }
2941883b1f31SPierre Schweitzer 
2942883b1f31SPierre Schweitzer     if (existing_dc) {
2943883b1f31SPierre Schweitzer         ExFreePool(dc->utf8.Buffer);
2944883b1f31SPierre Schweitzer         ExFreePool(dc->name.Buffer);
2945883b1f31SPierre Schweitzer         ExFreePool(dc);
2946883b1f31SPierre Schweitzer         reap_fileref(Vcb, fileref);
2947883b1f31SPierre Schweitzer         free_fileref(parfileref);
2948883b1f31SPierre Schweitzer 
2949883b1f31SPierre Schweitzer         increase_fileref_refcount(existing_dc->fileref);
2950883b1f31SPierre Schweitzer         *pfileref = existing_dc->fileref;
2951883b1f31SPierre Schweitzer 
2952883b1f31SPierre Schweitzer         return STATUS_OBJECT_NAME_COLLISION;
2953883b1f31SPierre Schweitzer     }
2954883b1f31SPierre Schweitzer 
2955c2c66affSColin Finck     dc->fileref = fileref;
2956c2c66affSColin Finck     fileref->dc = dc;
2957883b1f31SPierre Schweitzer     fileref->parent = (struct _file_ref*)parfileref;
2958318da0c1SPierre Schweitzer     fcb->deleted = false;
2959c2c66affSColin Finck 
2960c2c66affSColin Finck     InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
2961c2c66affSColin Finck 
2962883b1f31SPierre Schweitzer     InsertTailList(&parfileref->children, &fileref->list_entry);
2963883b1f31SPierre Schweitzer 
2964883b1f31SPierre Schweitzer     ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2965883b1f31SPierre Schweitzer 
2966c2c66affSColin Finck     mark_fileref_dirty(fileref);
2967c2c66affSColin Finck 
2968c2c66affSColin Finck     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2969c2c66affSColin Finck     parfileref->fcb->inode_item.sequence++;
2970c2c66affSColin Finck     parfileref->fcb->inode_item.st_ctime = now;
2971318da0c1SPierre Schweitzer     parfileref->fcb->inode_item_changed = true;
2972c2c66affSColin Finck 
2973c2c66affSColin Finck     mark_fcb_dirty(parfileref->fcb);
2974c2c66affSColin Finck 
2975c2c66affSColin Finck     parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2976c2c66affSColin Finck     parfileref->fcb->subvol->root_item.ctime = now;
2977c2c66affSColin Finck 
2978c2c66affSColin Finck     increase_fileref_refcount(parfileref);
2979c2c66affSColin Finck 
2980c2c66affSColin Finck     *pfileref = fileref;
2981c2c66affSColin Finck 
29824672b2baSPierre Schweitzer     send_notification_fileref(parfileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_ADDED_STREAM, &fileref->dc->name);
2983c2c66affSColin Finck 
2984c2c66affSColin Finck     return STATUS_SUCCESS;
2985c2c66affSColin Finck }
2986c2c66affSColin Finck 
2987c2c66affSColin Finck // LXSS programs can be distinguished by the fact they have a NULL PEB.
2988c2c66affSColin Finck #ifdef _AMD64_
called_from_lxss()2989318da0c1SPierre Schweitzer static __inline bool called_from_lxss() {
2990c2c66affSColin Finck     NTSTATUS Status;
2991c2c66affSColin Finck     PROCESS_BASIC_INFORMATION pbi;
2992c2c66affSColin Finck     ULONG retlen;
2993c2c66affSColin Finck 
2994c2c66affSColin Finck     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
2995c2c66affSColin Finck 
2996c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2997194ea909SVictor Perevertkin         ERR("ZwQueryInformationProcess returned %08lx\n", Status);
2998318da0c1SPierre Schweitzer         return false;
2999c2c66affSColin Finck     }
3000c2c66affSColin Finck 
3001c2c66affSColin Finck     return !pbi.PebBaseAddress;
3002c2c66affSColin Finck }
3003c2c66affSColin Finck #else
3004318da0c1SPierre Schweitzer #define called_from_lxss() false
3005c2c66affSColin Finck #endif
3006c2c66affSColin Finck 
3007c2c66affSColin Finck static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
3008318da0c1SPierre Schweitzer                             PFILE_OBJECT FileObject, file_ref* related, bool loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options,
3009883b1f31SPierre Schweitzer                             file_ref** existing_fileref, LIST_ENTRY* rollback) {
3010c2c66affSColin Finck     NTSTATUS Status;
3011c2c66affSColin Finck     file_ref *fileref, *parfileref = NULL;
3012c2c66affSColin Finck     ULONG i, j;
3013c2c66affSColin Finck     ccb* ccb;
3014eb7fbc25SPierre Schweitzer     static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
3015c2c66affSColin Finck     UNICODE_STRING dsus, fpus, stream;
3016c2c66affSColin Finck     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3017c2c66affSColin Finck     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
3018eb7fbc25SPierre Schweitzer     ECP_LIST* ecp_list;
3019eb7fbc25SPierre Schweitzer     ATOMIC_CREATE_ECP_CONTEXT* acec = NULL;
3020c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
3021c2c66affSColin Finck     LONG oc;
3022c2c66affSColin Finck #endif
3023c2c66affSColin Finck 
3024194ea909SVictor Perevertkin     TRACE("(%p, %p, %p, %.*S, %lx, %lx)\n", Irp, Vcb, FileObject, (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer, disposition, options);
3025c2c66affSColin Finck 
3026c2c66affSColin Finck     if (Vcb->readonly)
3027c2c66affSColin Finck         return STATUS_MEDIA_WRITE_PROTECTED;
3028c2c66affSColin Finck 
3029174dfab6SVincent Franchomme     if (options & FILE_DELETE_ON_CLOSE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_READONLY &&
3030174dfab6SVincent Franchomme         !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) {
30314672b2baSPierre Schweitzer         return STATUS_CANNOT_DELETE;
3032174dfab6SVincent Franchomme     }
30334672b2baSPierre Schweitzer 
3034318da0c1SPierre Schweitzer     if (fFsRtlGetEcpListFromIrp && fFsRtlGetNextExtraCreateParameter) {
3035318da0c1SPierre Schweitzer         if (NT_SUCCESS(fFsRtlGetEcpListFromIrp(Irp, &ecp_list)) && ecp_list) {
3036eb7fbc25SPierre Schweitzer             void* ctx = NULL;
3037eb7fbc25SPierre Schweitzer             GUID type;
3038eb7fbc25SPierre Schweitzer             ULONG ctxsize;
3039eb7fbc25SPierre Schweitzer 
3040eb7fbc25SPierre Schweitzer             do {
3041318da0c1SPierre Schweitzer                 Status = fFsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx, &ctxsize);
3042eb7fbc25SPierre Schweitzer 
3043eb7fbc25SPierre Schweitzer                 if (NT_SUCCESS(Status)) {
304462e630deSPierre Schweitzer                     if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID)) {
304562e630deSPierre Schweitzer                         if (ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT))
3046eb7fbc25SPierre Schweitzer                             acec = ctx;
304762e630deSPierre Schweitzer                         else {
3048194ea909SVictor Perevertkin                             ERR("GUID_ECP_ATOMIC_CREATE context was too short: %lu bytes, expected %Iu\n", ctxsize,
304962e630deSPierre Schweitzer                                 sizeof(ATOMIC_CREATE_ECP_CONTEXT));
305062e630deSPierre Schweitzer                         }
3051194ea909SVictor Perevertkin                     } else if (RtlCompareMemory(&type, &GUID_ECP_QUERY_ON_CREATE, sizeof(GUID)) == sizeof(GUID))
3052194ea909SVictor Perevertkin                         WARN("unhandled ECP GUID_ECP_QUERY_ON_CREATE\n");
3053194ea909SVictor Perevertkin                     else if (RtlCompareMemory(&type, &GUID_ECP_CREATE_REDIRECTION, sizeof(GUID)) == sizeof(GUID))
3054194ea909SVictor Perevertkin                         WARN("unhandled ECP GUID_ECP_CREATE_REDIRECTION\n");
3055194ea909SVictor Perevertkin                     else {
3056194ea909SVictor Perevertkin                         WARN("unhandled ECP {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", type.Data1, type.Data2,
305762e630deSPierre Schweitzer                              type.Data3, type.Data4[0], type.Data4[1], type.Data4[2], type.Data4[3], type.Data4[4], type.Data4[5],
305862e630deSPierre Schweitzer                              type.Data4[6], type.Data4[7]);
3059eb7fbc25SPierre Schweitzer                     }
3060eb7fbc25SPierre Schweitzer                 }
3061eb7fbc25SPierre Schweitzer             } while (NT_SUCCESS(Status));
3062eb7fbc25SPierre Schweitzer         }
3063318da0c1SPierre Schweitzer     }
3064eb7fbc25SPierre Schweitzer 
3065eb7fbc25SPierre Schweitzer     dsus.Buffer = (WCHAR*)datasuf;
3066eb7fbc25SPierre Schweitzer     dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3067c2c66affSColin Finck     fpus.Buffer = NULL;
3068c2c66affSColin Finck 
3069c2c66affSColin Finck     if (!loaded_related) {
3070318da0c1SPierre Schweitzer         Status = open_fileref(Vcb, &parfileref, fnus, related, true, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
3071c2c66affSColin Finck 
3072c2c66affSColin Finck         if (!NT_SUCCESS(Status))
3073c2c66affSColin Finck             goto end;
3074c2c66affSColin Finck     } else
3075c2c66affSColin Finck         parfileref = related;
3076c2c66affSColin Finck 
3077c2c66affSColin Finck     if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
3078c2c66affSColin Finck         Status = STATUS_OBJECT_PATH_NOT_FOUND;
3079c2c66affSColin Finck         goto end;
3080c2c66affSColin Finck     }
3081c2c66affSColin Finck 
3082c2c66affSColin Finck     if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) {
3083c2c66affSColin Finck         Status = STATUS_ACCESS_DENIED;
3084c2c66affSColin Finck         goto end;
3085c2c66affSColin Finck     }
3086c2c66affSColin Finck 
3087c2c66affSColin Finck     i = (fnus->Length / sizeof(WCHAR))-1;
3088c2c66affSColin Finck     while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
3089c2c66affSColin Finck 
3090c2c66affSColin Finck     j = i;
3091c2c66affSColin Finck 
3092c2c66affSColin Finck     while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
3093c2c66affSColin Finck 
3094c2c66affSColin Finck     fpus.MaximumLength = (USHORT)((j - i + 2) * sizeof(WCHAR));
3095c2c66affSColin Finck     fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
3096c2c66affSColin Finck     if (!fpus.Buffer) {
3097c2c66affSColin Finck         ERR("out of memory\n");
3098c2c66affSColin Finck         Status = STATUS_INSUFFICIENT_RESOURCES;
3099c2c66affSColin Finck         goto end;
3100c2c66affSColin Finck     }
3101c2c66affSColin Finck 
3102c2c66affSColin Finck     fpus.Length = (USHORT)((j - i + 1) * sizeof(WCHAR));
3103c2c66affSColin Finck 
3104c2c66affSColin Finck     RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
3105c2c66affSColin Finck     fpus.Buffer[j - i + 1] = 0;
3106c2c66affSColin Finck 
3107c2c66affSColin Finck     if (fpus.Length > dsus.Length) { // check for :$DATA suffix
3108c2c66affSColin Finck         UNICODE_STRING lb;
3109c2c66affSColin Finck 
3110c2c66affSColin Finck         lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
3111c2c66affSColin Finck         lb.Length = lb.MaximumLength = dsus.Length;
3112c2c66affSColin Finck 
3113194ea909SVictor Perevertkin         TRACE("lb = %.*S\n", (int)(lb.Length/sizeof(WCHAR)), lb.Buffer);
3114c2c66affSColin Finck 
3115318da0c1SPierre Schweitzer         if (FsRtlAreNamesEqual(&dsus, &lb, true, NULL)) {
3116c2c66affSColin Finck             TRACE("ignoring :$DATA suffix\n");
3117c2c66affSColin Finck 
3118c2c66affSColin Finck             fpus.Length -= lb.Length;
3119c2c66affSColin Finck 
3120c2c66affSColin Finck             if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
3121c2c66affSColin Finck                 fpus.Length -= sizeof(WCHAR);
3122c2c66affSColin Finck 
3123194ea909SVictor Perevertkin             TRACE("fpus = %.*S\n", (int)(fpus.Length / sizeof(WCHAR)), fpus.Buffer);
3124c2c66affSColin Finck         }
3125c2c66affSColin Finck     }
3126c2c66affSColin Finck 
3127c2c66affSColin Finck     stream.Length = 0;
3128c2c66affSColin Finck 
3129c2c66affSColin Finck     for (i = 0; i < fpus.Length / sizeof(WCHAR); i++) {
3130c2c66affSColin Finck         if (fpus.Buffer[i] == ':') {
3131c2c66affSColin Finck             stream.Length = (USHORT)(fpus.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
3132c2c66affSColin Finck             stream.Buffer = &fpus.Buffer[i+1];
3133c2c66affSColin Finck             fpus.Buffer[i] = 0;
3134c2c66affSColin Finck             fpus.Length = (USHORT)(i * sizeof(WCHAR));
3135c2c66affSColin Finck             break;
3136c2c66affSColin Finck         }
3137c2c66affSColin Finck     }
3138c2c66affSColin Finck 
3139c2c66affSColin Finck     if (stream.Length > 0) {
3140c2c66affSColin Finck         Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3141c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
3142194ea909SVictor Perevertkin             ERR("create_stream returned %08lx\n", Status);
3143c2c66affSColin Finck             goto end;
3144c2c66affSColin Finck         }
3145c2c66affSColin Finck 
3146c2c66affSColin Finck         IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess,
3147c2c66affSColin Finck                          FileObject, &fileref->fcb->share_access);
3148c2c66affSColin Finck     } else {
3149c2c66affSColin Finck         ACCESS_MASK granted_access;
3150c2c66affSColin Finck 
3151c982533eSVincent Franchomme         Status = check_file_name_valid(&fpus, false, false);
3152c982533eSVincent Franchomme         if (!NT_SUCCESS(Status))
3153c2c66affSColin Finck             goto end;
3154c2c66affSColin Finck 
3155c2c66affSColin Finck         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3156c2c66affSColin Finck 
3157c2c66affSColin Finck         if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
3158318da0c1SPierre Schweitzer                            true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
3159c2c66affSColin Finck                            IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
3160c2c66affSColin Finck                            &granted_access, &Status)) {
3161c2c66affSColin Finck             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3162c2c66affSColin Finck             goto end;
3163c2c66affSColin Finck         }
3164c2c66affSColin Finck 
3165c2c66affSColin Finck         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3166c2c66affSColin Finck 
3167c2c66affSColin Finck         if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3168c2c66affSColin Finck             ULONG offset;
3169c2c66affSColin Finck 
3170c2c66affSColin Finck             Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
3171c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3172194ea909SVictor Perevertkin                 ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
3173c2c66affSColin Finck                 goto end;
3174c2c66affSColin Finck             }
3175c2c66affSColin Finck         }
3176c2c66affSColin Finck 
3177c2c66affSColin Finck         Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
3178883b1f31SPierre Schweitzer                               &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3179c2c66affSColin Finck 
3180883b1f31SPierre Schweitzer         if (Status == STATUS_OBJECT_NAME_COLLISION) {
3181883b1f31SPierre Schweitzer             *existing_fileref = fileref;
3182883b1f31SPierre Schweitzer             goto end;
3183883b1f31SPierre Schweitzer         } else if (!NT_SUCCESS(Status)) {
3184194ea909SVictor Perevertkin             ERR("file_create2 returned %08lx\n", Status);
3185c2c66affSColin Finck             goto end;
3186c2c66affSColin Finck         }
3187c2c66affSColin Finck 
3188c2c66affSColin Finck         IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
3189c2c66affSColin Finck 
3190c2c66affSColin Finck         send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
319162e630deSPierre Schweitzer         queue_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3192c2c66affSColin Finck     }
3193c2c66affSColin Finck 
3194c2c66affSColin Finck     FileObject->FsContext = fileref->fcb;
3195c2c66affSColin Finck 
3196c2c66affSColin Finck     ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
3197c2c66affSColin Finck     if (!ccb) {
3198c2c66affSColin Finck         ERR("out of memory\n");
3199c2c66affSColin Finck         Status = STATUS_INSUFFICIENT_RESOURCES;
3200318da0c1SPierre Schweitzer         fileref->deleted = true;
3201318da0c1SPierre Schweitzer         fileref->fcb->deleted = true;
3202eb7fbc25SPierre Schweitzer 
3203eb7fbc25SPierre Schweitzer         if (stream.Length == 0) {
3204318da0c1SPierre Schweitzer             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3205eb7fbc25SPierre Schweitzer             parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3206eb7fbc25SPierre Schweitzer             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3207eb7fbc25SPierre Schweitzer         }
3208eb7fbc25SPierre Schweitzer 
3209883b1f31SPierre Schweitzer         free_fileref(fileref);
3210c2c66affSColin Finck         goto end;
3211c2c66affSColin Finck     }
3212c2c66affSColin Finck 
3213c2c66affSColin Finck     RtlZeroMemory(ccb, sizeof(*ccb));
3214c2c66affSColin Finck 
3215c2c66affSColin Finck     ccb->fileref = fileref;
3216c2c66affSColin Finck 
3217c2c66affSColin Finck     ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3218c2c66affSColin Finck     ccb->NodeSize = sizeof(*ccb);
3219c2c66affSColin Finck     ccb->disposition = disposition;
3220c2c66affSColin Finck     ccb->options = options;
3221c2c66affSColin Finck     ccb->query_dir_offset = 0;
3222c2c66affSColin Finck     RtlInitUnicodeString(&ccb->query_string, NULL);
3223318da0c1SPierre Schweitzer     ccb->has_wildcard = false;
3224318da0c1SPierre Schweitzer     ccb->specific_file = false;
3225c2c66affSColin Finck     ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
3226c2c66affSColin Finck     ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
3227318da0c1SPierre Schweitzer     ccb->reserving = false;
3228c2c66affSColin Finck     ccb->lxss = called_from_lxss();
3229c2c66affSColin Finck 
3230c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
3231c2c66affSColin Finck     oc = InterlockedIncrement(&fileref->open_count);
3232c2c66affSColin Finck     ERR("fileref %p: open_count now %i\n", fileref, oc);
3233c2c66affSColin Finck #else
3234c2c66affSColin Finck     InterlockedIncrement(&fileref->open_count);
3235c2c66affSColin Finck #endif
3236c2c66affSColin Finck     InterlockedIncrement(&Vcb->open_files);
3237c2c66affSColin Finck 
3238c2c66affSColin Finck     FileObject->FsContext2 = ccb;
3239c2c66affSColin Finck 
3240c2c66affSColin Finck     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
3241c2c66affSColin Finck 
3242eb7fbc25SPierre Schweitzer     // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
3243eb7fbc25SPierre Schweitzer     if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED) {
3244318da0c1SPierre Schweitzer         if (acec->ReparseBufferLength > sizeof(uint32_t) && *(uint32_t*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) {
3245eb7fbc25SPierre Schweitzer             fileref->fcb->inode_item.st_mode &= ~(__S_IFIFO | __S_IFCHR | __S_IFBLK | __S_IFSOCK);
3246eb7fbc25SPierre Schweitzer             fileref->fcb->type = BTRFS_TYPE_FILE;
3247174dfab6SVincent Franchomme             fileref->fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
3248eb7fbc25SPierre Schweitzer         }
3249eb7fbc25SPierre Schweitzer 
3250eb7fbc25SPierre Schweitzer         if (fileref->fcb->type == BTRFS_TYPE_SOCKET || fileref->fcb->type == BTRFS_TYPE_FIFO ||
3251eb7fbc25SPierre Schweitzer             fileref->fcb->type == BTRFS_TYPE_CHARDEV || fileref->fcb->type == BTRFS_TYPE_BLOCKDEV) {
3252eb7fbc25SPierre Schweitzer             // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
3253eb7fbc25SPierre Schweitzer         } else {
3254eb7fbc25SPierre Schweitzer             Status = set_reparse_point2(fileref->fcb, acec->ReparseBuffer, acec->ReparseBufferLength, NULL, NULL, Irp, rollback);
3255eb7fbc25SPierre Schweitzer             if (!NT_SUCCESS(Status)) {
3256194ea909SVictor Perevertkin                 ERR("set_reparse_point2 returned %08lx\n", Status);
3257318da0c1SPierre Schweitzer                 fileref->deleted = true;
3258318da0c1SPierre Schweitzer                 fileref->fcb->deleted = true;
3259eb7fbc25SPierre Schweitzer 
3260eb7fbc25SPierre Schweitzer                 if (stream.Length == 0) {
3261318da0c1SPierre Schweitzer                     ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3262eb7fbc25SPierre Schweitzer                     parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3263eb7fbc25SPierre Schweitzer                     ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3264eb7fbc25SPierre Schweitzer                 }
3265eb7fbc25SPierre Schweitzer 
3266883b1f31SPierre Schweitzer                 free_fileref(fileref);
3267eb7fbc25SPierre Schweitzer                 return Status;
3268eb7fbc25SPierre Schweitzer             }
3269eb7fbc25SPierre Schweitzer         }
3270eb7fbc25SPierre Schweitzer 
3271eb7fbc25SPierre Schweitzer         acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET;
3272eb7fbc25SPierre Schweitzer     }
3273eb7fbc25SPierre Schweitzer 
3274194ea909SVictor Perevertkin     if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED) {
3275194ea909SVictor Perevertkin         if (acec->InOpFlags & ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED && fileref->fcb->atts & FILE_ATTRIBUTE_DIRECTORY) {
3276194ea909SVictor Perevertkin             if ((acec->InCaseSensitiveFlags & acec->CaseSensitiveFlagsMask) & FILE_CS_FLAG_CASE_SENSITIVE_DIR) {
3277194ea909SVictor Perevertkin                 acec->OutCaseSensitiveFlags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
3278194ea909SVictor Perevertkin                 fileref->fcb->case_sensitive = true;
3279194ea909SVictor Perevertkin                 ccb->case_sensitive = true;
3280194ea909SVictor Perevertkin             }
3281194ea909SVictor Perevertkin 
3282194ea909SVictor Perevertkin             acec->OutOpFlags |= ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET;
3283194ea909SVictor Perevertkin         }
3284194ea909SVictor Perevertkin 
3285194ea909SVictor Perevertkin         acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED;
3286194ea909SVictor Perevertkin     }
3287194ea909SVictor Perevertkin 
3288eb7fbc25SPierre Schweitzer     fileref->dc->type = fileref->fcb->type;
3289eb7fbc25SPierre Schweitzer 
3290c2c66affSColin Finck end:
3291c2c66affSColin Finck     if (fpus.Buffer)
3292c2c66affSColin Finck         ExFreePool(fpus.Buffer);
3293c2c66affSColin Finck 
3294c2c66affSColin Finck     if (parfileref && !loaded_related)
3295883b1f31SPierre Schweitzer         free_fileref(parfileref);
3296c2c66affSColin Finck 
3297c2c66affSColin Finck     return Status;
3298c2c66affSColin Finck }
3299c2c66affSColin Finck 
debug_create_options(ULONG RequestedOptions)3300c2c66affSColin Finck static __inline void debug_create_options(ULONG RequestedOptions) {
3301c2c66affSColin Finck     if (RequestedOptions != 0) {
3302c2c66affSColin Finck         ULONG options = RequestedOptions;
3303c2c66affSColin Finck 
3304c2c66affSColin Finck         TRACE("requested options:\n");
3305c2c66affSColin Finck 
3306c2c66affSColin Finck         if (options & FILE_DIRECTORY_FILE) {
3307c2c66affSColin Finck             TRACE("    FILE_DIRECTORY_FILE\n");
3308c2c66affSColin Finck             options &= ~FILE_DIRECTORY_FILE;
3309c2c66affSColin Finck         }
3310c2c66affSColin Finck 
3311c2c66affSColin Finck         if (options & FILE_WRITE_THROUGH) {
3312c2c66affSColin Finck             TRACE("    FILE_WRITE_THROUGH\n");
3313c2c66affSColin Finck             options &= ~FILE_WRITE_THROUGH;
3314c2c66affSColin Finck         }
3315c2c66affSColin Finck 
3316c2c66affSColin Finck         if (options & FILE_SEQUENTIAL_ONLY) {
3317c2c66affSColin Finck             TRACE("    FILE_SEQUENTIAL_ONLY\n");
3318c2c66affSColin Finck             options &= ~FILE_SEQUENTIAL_ONLY;
3319c2c66affSColin Finck         }
3320c2c66affSColin Finck 
3321c2c66affSColin Finck         if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
3322c2c66affSColin Finck             TRACE("    FILE_NO_INTERMEDIATE_BUFFERING\n");
3323c2c66affSColin Finck             options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
3324c2c66affSColin Finck         }
3325c2c66affSColin Finck 
3326c2c66affSColin Finck         if (options & FILE_SYNCHRONOUS_IO_ALERT) {
3327c2c66affSColin Finck             TRACE("    FILE_SYNCHRONOUS_IO_ALERT\n");
3328c2c66affSColin Finck             options &= ~FILE_SYNCHRONOUS_IO_ALERT;
3329c2c66affSColin Finck         }
3330c2c66affSColin Finck 
3331c2c66affSColin Finck         if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
3332c2c66affSColin Finck             TRACE("    FILE_SYNCHRONOUS_IO_NONALERT\n");
3333c2c66affSColin Finck             options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
3334c2c66affSColin Finck         }
3335c2c66affSColin Finck 
3336c2c66affSColin Finck         if (options & FILE_NON_DIRECTORY_FILE) {
3337c2c66affSColin Finck             TRACE("    FILE_NON_DIRECTORY_FILE\n");
3338c2c66affSColin Finck             options &= ~FILE_NON_DIRECTORY_FILE;
3339c2c66affSColin Finck         }
3340c2c66affSColin Finck 
3341c2c66affSColin Finck         if (options & FILE_CREATE_TREE_CONNECTION) {
3342c2c66affSColin Finck             TRACE("    FILE_CREATE_TREE_CONNECTION\n");
3343c2c66affSColin Finck             options &= ~FILE_CREATE_TREE_CONNECTION;
3344c2c66affSColin Finck         }
3345c2c66affSColin Finck 
3346c2c66affSColin Finck         if (options & FILE_COMPLETE_IF_OPLOCKED) {
3347c2c66affSColin Finck             TRACE("    FILE_COMPLETE_IF_OPLOCKED\n");
3348c2c66affSColin Finck             options &= ~FILE_COMPLETE_IF_OPLOCKED;
3349c2c66affSColin Finck         }
3350c2c66affSColin Finck 
3351c2c66affSColin Finck         if (options & FILE_NO_EA_KNOWLEDGE) {
3352c2c66affSColin Finck             TRACE("    FILE_NO_EA_KNOWLEDGE\n");
3353c2c66affSColin Finck             options &= ~FILE_NO_EA_KNOWLEDGE;
3354c2c66affSColin Finck         }
3355c2c66affSColin Finck 
3356c2c66affSColin Finck         if (options & FILE_OPEN_REMOTE_INSTANCE) {
3357c2c66affSColin Finck             TRACE("    FILE_OPEN_REMOTE_INSTANCE\n");
3358c2c66affSColin Finck             options &= ~FILE_OPEN_REMOTE_INSTANCE;
3359c2c66affSColin Finck         }
3360c2c66affSColin Finck 
3361c2c66affSColin Finck         if (options & FILE_RANDOM_ACCESS) {
3362c2c66affSColin Finck             TRACE("    FILE_RANDOM_ACCESS\n");
3363c2c66affSColin Finck             options &= ~FILE_RANDOM_ACCESS;
3364c2c66affSColin Finck         }
3365c2c66affSColin Finck 
3366c2c66affSColin Finck         if (options & FILE_DELETE_ON_CLOSE) {
3367c2c66affSColin Finck             TRACE("    FILE_DELETE_ON_CLOSE\n");
3368c2c66affSColin Finck             options &= ~FILE_DELETE_ON_CLOSE;
3369c2c66affSColin Finck         }
3370c2c66affSColin Finck 
3371c2c66affSColin Finck         if (options & FILE_OPEN_BY_FILE_ID) {
3372c2c66affSColin Finck             TRACE("    FILE_OPEN_BY_FILE_ID\n");
3373c2c66affSColin Finck             options &= ~FILE_OPEN_BY_FILE_ID;
3374c2c66affSColin Finck         }
3375c2c66affSColin Finck 
3376c2c66affSColin Finck         if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
3377c2c66affSColin Finck             TRACE("    FILE_OPEN_FOR_BACKUP_INTENT\n");
3378c2c66affSColin Finck             options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
3379c2c66affSColin Finck         }
3380c2c66affSColin Finck 
3381c2c66affSColin Finck         if (options & FILE_NO_COMPRESSION) {
3382c2c66affSColin Finck             TRACE("    FILE_NO_COMPRESSION\n");
3383c2c66affSColin Finck             options &= ~FILE_NO_COMPRESSION;
3384c2c66affSColin Finck         }
3385c2c66affSColin Finck 
3386c2c66affSColin Finck #if NTDDI_VERSION >= NTDDI_WIN7
3387c2c66affSColin Finck         if (options & FILE_OPEN_REQUIRING_OPLOCK) {
3388c2c66affSColin Finck             TRACE("    FILE_OPEN_REQUIRING_OPLOCK\n");
3389c2c66affSColin Finck             options &= ~FILE_OPEN_REQUIRING_OPLOCK;
3390c2c66affSColin Finck         }
3391c2c66affSColin Finck 
3392c2c66affSColin Finck         if (options & FILE_DISALLOW_EXCLUSIVE) {
3393c2c66affSColin Finck             TRACE("    FILE_DISALLOW_EXCLUSIVE\n");
3394c2c66affSColin Finck             options &= ~FILE_DISALLOW_EXCLUSIVE;
3395c2c66affSColin Finck         }
3396c2c66affSColin Finck #endif
3397c2c66affSColin Finck 
3398c2c66affSColin Finck         if (options & FILE_RESERVE_OPFILTER) {
3399c2c66affSColin Finck             TRACE("    FILE_RESERVE_OPFILTER\n");
3400c2c66affSColin Finck             options &= ~FILE_RESERVE_OPFILTER;
3401c2c66affSColin Finck         }
3402c2c66affSColin Finck 
3403c2c66affSColin Finck         if (options & FILE_OPEN_REPARSE_POINT) {
3404c2c66affSColin Finck             TRACE("    FILE_OPEN_REPARSE_POINT\n");
3405c2c66affSColin Finck             options &= ~FILE_OPEN_REPARSE_POINT;
3406c2c66affSColin Finck         }
3407c2c66affSColin Finck 
3408c2c66affSColin Finck         if (options & FILE_OPEN_NO_RECALL) {
3409c2c66affSColin Finck             TRACE("    FILE_OPEN_NO_RECALL\n");
3410c2c66affSColin Finck             options &= ~FILE_OPEN_NO_RECALL;
3411c2c66affSColin Finck         }
3412c2c66affSColin Finck 
3413c2c66affSColin Finck         if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
3414c2c66affSColin Finck             TRACE("    FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
3415c2c66affSColin Finck             options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
3416c2c66affSColin Finck         }
3417c2c66affSColin Finck 
3418c2c66affSColin Finck         if (options)
3419194ea909SVictor Perevertkin             TRACE("    unknown options: %lx\n", options);
3420c2c66affSColin Finck     } else {
3421c2c66affSColin Finck         TRACE("requested options: (none)\n");
3422c2c66affSColin Finck     }
3423c2c66affSColin Finck }
3424c2c66affSColin Finck 
get_reparse_block(fcb * fcb,uint8_t ** data)3425318da0c1SPierre Schweitzer static NTSTATUS get_reparse_block(fcb* fcb, uint8_t** data) {
3426c2c66affSColin Finck     NTSTATUS Status;
3427c2c66affSColin Finck 
3428c2c66affSColin Finck     if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
3429c2c66affSColin Finck         ULONG size, bytes_read, i;
3430c2c66affSColin Finck 
3431c2c66affSColin Finck         if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
3432c2c66affSColin Finck             WARN("file was too short to be a reparse point\n");
3433c2c66affSColin Finck             return STATUS_INVALID_PARAMETER;
3434c2c66affSColin Finck         }
3435c2c66affSColin Finck 
3436c2c66affSColin Finck         // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
3437c2c66affSColin Finck         size = (ULONG)min(0x10007, fcb->inode_item.st_size);
3438c2c66affSColin Finck 
3439c2c66affSColin Finck         if (size == 0)
3440c2c66affSColin Finck             return STATUS_INVALID_PARAMETER;
3441c2c66affSColin Finck 
3442c2c66affSColin Finck         *data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
3443c2c66affSColin Finck         if (!*data) {
3444c2c66affSColin Finck             ERR("out of memory\n");
3445c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
3446c2c66affSColin Finck         }
3447c2c66affSColin Finck 
3448c2c66affSColin Finck         Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
3449c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
3450194ea909SVictor Perevertkin             ERR("read_file_fcb returned %08lx\n", Status);
3451c2c66affSColin Finck             ExFreePool(*data);
3452c2c66affSColin Finck             return Status;
3453c2c66affSColin Finck         }
3454c2c66affSColin Finck 
3455c2c66affSColin Finck         if (fcb->type == BTRFS_TYPE_SYMLINK) {
3456c2c66affSColin Finck             ULONG stringlen, reqlen;
3457318da0c1SPierre Schweitzer             uint16_t subnamelen, printnamelen;
3458c2c66affSColin Finck             REPARSE_DATA_BUFFER* rdb;
3459c2c66affSColin Finck 
3460318da0c1SPierre Schweitzer             Status = utf8_to_utf16(NULL, 0, &stringlen, (char*)*data, bytes_read);
3461c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3462194ea909SVictor Perevertkin                 ERR("utf8_to_utf16 1 returned %08lx\n", Status);
3463c2c66affSColin Finck                 ExFreePool(*data);
3464c2c66affSColin Finck                 return Status;
3465c2c66affSColin Finck             }
3466c2c66affSColin Finck 
3467c2c66affSColin Finck             subnamelen = printnamelen = (USHORT)stringlen;
3468c2c66affSColin Finck 
3469c2c66affSColin Finck             reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
3470c2c66affSColin Finck 
3471c2c66affSColin Finck             rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
3472c2c66affSColin Finck 
3473c2c66affSColin Finck             if (!rdb) {
3474c2c66affSColin Finck                 ERR("out of memory\n");
3475c2c66affSColin Finck                 ExFreePool(*data);
3476c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
3477c2c66affSColin Finck             }
3478c2c66affSColin Finck 
3479c2c66affSColin Finck             rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
3480c2c66affSColin Finck             rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
3481c2c66affSColin Finck             rdb->Reserved = 0;
3482c2c66affSColin Finck 
3483c2c66affSColin Finck             rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
3484c2c66affSColin Finck             rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
3485c2c66affSColin Finck             rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
3486c2c66affSColin Finck             rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
3487c2c66affSColin Finck             rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
3488c2c66affSColin Finck 
3489318da0c1SPierre Schweitzer             Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3490c2c66affSColin Finck                                     stringlen, &stringlen, (char*)*data, size);
3491c2c66affSColin Finck 
3492c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3493194ea909SVictor Perevertkin                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
3494c2c66affSColin Finck                 ExFreePool(rdb);
3495c2c66affSColin Finck                 ExFreePool(*data);
3496c2c66affSColin Finck                 return Status;
3497c2c66affSColin Finck             }
3498c2c66affSColin Finck 
3499c2c66affSColin Finck             for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
3500c2c66affSColin Finck                 if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
3501c2c66affSColin Finck                     rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
3502c2c66affSColin Finck             }
3503c2c66affSColin Finck 
3504c2c66affSColin Finck             RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
3505c2c66affSColin Finck                         &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3506c2c66affSColin Finck                         rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
3507c2c66affSColin Finck 
3508c2c66affSColin Finck             ExFreePool(*data);
3509c2c66affSColin Finck 
3510318da0c1SPierre Schweitzer             *data = (uint8_t*)rdb;
3511c2c66affSColin Finck         } else {
3512318da0c1SPierre Schweitzer             Status = fFsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data);
3513c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3514194ea909SVictor Perevertkin                 ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3515c2c66affSColin Finck                 ExFreePool(*data);
3516c2c66affSColin Finck                 return Status;
3517c2c66affSColin Finck             }
3518c2c66affSColin Finck         }
3519c2c66affSColin Finck     } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3520c2c66affSColin Finck         if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0)
3521c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
3522c2c66affSColin Finck 
3523c2c66affSColin Finck         if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
3524c2c66affSColin Finck             WARN("xattr was too short to be a reparse point\n");
3525c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
3526c2c66affSColin Finck         }
3527c2c66affSColin Finck 
3528318da0c1SPierre Schweitzer         Status = fFsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer);
3529c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
3530194ea909SVictor Perevertkin             ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3531c2c66affSColin Finck             return Status;
3532c2c66affSColin Finck         }
3533c2c66affSColin Finck 
3534c2c66affSColin Finck         *data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG);
3535c2c66affSColin Finck         if (!*data) {
3536c2c66affSColin Finck             ERR("out of memory\n");
3537c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
3538c2c66affSColin Finck         }
3539c2c66affSColin Finck 
3540c2c66affSColin Finck         RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
3541eb7fbc25SPierre Schweitzer     } else
3542eb7fbc25SPierre Schweitzer         return STATUS_INVALID_PARAMETER;
3543c2c66affSColin Finck 
3544c2c66affSColin Finck     return STATUS_SUCCESS;
3545c2c66affSColin Finck }
3546c2c66affSColin Finck 
3547c2c66affSColin Finck static void fcb_load_csums(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, PIRP Irp) {
3548c2c66affSColin Finck     LIST_ENTRY* le;
3549c2c66affSColin Finck     NTSTATUS Status;
3550c2c66affSColin Finck 
3551c2c66affSColin Finck     if (fcb->csum_loaded)
3552c2c66affSColin Finck         return;
3553c2c66affSColin Finck 
3554c2c66affSColin Finck     if (IsListEmpty(&fcb->extents) || fcb->inode_item.flags & BTRFS_INODE_NODATASUM)
3555c2c66affSColin Finck         goto end;
3556c2c66affSColin Finck 
3557c2c66affSColin Finck     le = fcb->extents.Flink;
3558c2c66affSColin Finck     while (le != &fcb->extents) {
3559c2c66affSColin Finck         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3560c2c66affSColin Finck 
356162e630deSPierre Schweitzer         if (!ext->ignore && ext->extent_data.type == EXTENT_TYPE_REGULAR) {
3562c2c66affSColin Finck             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
3563318da0c1SPierre Schweitzer             uint64_t len;
3564c2c66affSColin Finck 
3565174dfab6SVincent Franchomme             len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) >> Vcb->sector_shift;
3566c2c66affSColin Finck 
3567194ea909SVictor Perevertkin             ext->csum = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(len * Vcb->csum_size), ALLOC_TAG);
3568c2c66affSColin Finck             if (!ext->csum) {
3569c2c66affSColin Finck                 ERR("out of memory\n");
3570c2c66affSColin Finck                 goto end;
3571c2c66affSColin Finck             }
3572c2c66affSColin Finck 
3573c2c66affSColin Finck             Status = load_csum(Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
3574c2c66affSColin Finck 
3575c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3576194ea909SVictor Perevertkin                 ERR("load_csum returned %08lx\n", Status);
3577c2c66affSColin Finck                 goto end;
3578c2c66affSColin Finck             }
3579c2c66affSColin Finck         }
3580c2c66affSColin Finck 
3581c2c66affSColin Finck         le = le->Flink;
3582c2c66affSColin Finck     }
3583c2c66affSColin Finck 
3584c2c66affSColin Finck end:
3585318da0c1SPierre Schweitzer     fcb->csum_loaded = true;
3586c2c66affSColin Finck }
3587c2c66affSColin Finck 
open_file3(device_extension * Vcb,PIRP Irp,ACCESS_MASK granted_access,file_ref * fileref,LIST_ENTRY * rollback)3588c982533eSVincent Franchomme static NTSTATUS open_file3(device_extension* Vcb, PIRP Irp, ACCESS_MASK granted_access, file_ref* fileref, LIST_ENTRY* rollback) {
3589c982533eSVincent Franchomme     NTSTATUS Status;
3590c982533eSVincent Franchomme     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3591c982533eSVincent Franchomme     ULONG options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
3592c982533eSVincent Franchomme     ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
3593c982533eSVincent Franchomme     PFILE_OBJECT FileObject = IrpSp->FileObject;
3594c982533eSVincent Franchomme     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
3595c982533eSVincent Franchomme     ccb* ccb;
3596c982533eSVincent Franchomme 
3597c982533eSVincent Franchomme     if (granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
3598c982533eSVincent Franchomme         if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite))
3599c982533eSVincent Franchomme             return (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
3600c982533eSVincent Franchomme     }
3601c982533eSVincent Franchomme 
3602c982533eSVincent Franchomme     if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
3603c982533eSVincent Franchomme         ULONG defda, oldatts, filter;
3604c982533eSVincent Franchomme         LARGE_INTEGER time;
3605c982533eSVincent Franchomme         BTRFS_TIME now;
3606c982533eSVincent Franchomme 
3607c982533eSVincent Franchomme         if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN))))
3608c982533eSVincent Franchomme             return STATUS_ACCESS_DENIED;
3609c982533eSVincent Franchomme 
3610c982533eSVincent Franchomme         if (fileref->fcb->ads) {
3611c982533eSVincent Franchomme             Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, false);
3612c982533eSVincent Franchomme             if (!NT_SUCCESS(Status)) {
3613c982533eSVincent Franchomme                 ERR("stream_set_end_of_file_information returned %08lx\n", Status);
3614c982533eSVincent Franchomme                 return Status;
3615c982533eSVincent Franchomme             }
3616c982533eSVincent Franchomme         } else {
3617c982533eSVincent Franchomme             Status = truncate_file(fileref->fcb, 0, Irp, rollback);
3618c982533eSVincent Franchomme             if (!NT_SUCCESS(Status)) {
3619c982533eSVincent Franchomme                 ERR("truncate_file returned %08lx\n", Status);
3620c982533eSVincent Franchomme                 return Status;
3621c982533eSVincent Franchomme             }
3622c982533eSVincent Franchomme         }
3623c982533eSVincent Franchomme 
3624c982533eSVincent Franchomme         if (Irp->Overlay.AllocationSize.QuadPart > 0) {
3625c982533eSVincent Franchomme             Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
3626c982533eSVincent Franchomme 
3627c982533eSVincent Franchomme             if (!NT_SUCCESS(Status)) {
3628c982533eSVincent Franchomme                 ERR("extend_file returned %08lx\n", Status);
3629c982533eSVincent Franchomme                 return Status;
3630c982533eSVincent Franchomme             }
3631c982533eSVincent Franchomme         }
3632c982533eSVincent Franchomme 
3633c982533eSVincent Franchomme         if (!fileref->fcb->ads) {
3634c982533eSVincent Franchomme             LIST_ENTRY* le;
3635c982533eSVincent Franchomme 
3636c982533eSVincent Franchomme             if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3637c982533eSVincent Franchomme                 ULONG offset;
3638c982533eSVincent Franchomme                 FILE_FULL_EA_INFORMATION* eainfo;
3639c982533eSVincent Franchomme 
3640c982533eSVincent Franchomme                 Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
3641c982533eSVincent Franchomme                 if (!NT_SUCCESS(Status)) {
3642c982533eSVincent Franchomme                     ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
3643c982533eSVincent Franchomme                     return Status;
3644c982533eSVincent Franchomme                 }
3645c982533eSVincent Franchomme 
3646c982533eSVincent Franchomme                 fileref->fcb->ealen = 4;
3647c982533eSVincent Franchomme 
3648c982533eSVincent Franchomme                 // capitalize EA name
3649c982533eSVincent Franchomme                 eainfo = Irp->AssociatedIrp.SystemBuffer;
3650c982533eSVincent Franchomme                 do {
3651c982533eSVincent Franchomme                     STRING s;
3652c982533eSVincent Franchomme 
3653c982533eSVincent Franchomme                     s.Length = s.MaximumLength = eainfo->EaNameLength;
3654c982533eSVincent Franchomme                     s.Buffer = eainfo->EaName;
3655c982533eSVincent Franchomme 
3656c982533eSVincent Franchomme                     RtlUpperString(&s, &s);
3657c982533eSVincent Franchomme 
3658c982533eSVincent Franchomme                     fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
3659c982533eSVincent Franchomme 
3660c982533eSVincent Franchomme                     if (eainfo->NextEntryOffset == 0)
3661c982533eSVincent Franchomme                         break;
3662c982533eSVincent Franchomme 
3663c982533eSVincent Franchomme                     eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset);
3664c982533eSVincent Franchomme                 } while (true);
3665c982533eSVincent Franchomme 
3666c982533eSVincent Franchomme                 if (fileref->fcb->ea_xattr.Buffer)
3667c982533eSVincent Franchomme                     ExFreePool(fileref->fcb->ea_xattr.Buffer);
3668c982533eSVincent Franchomme 
3669c982533eSVincent Franchomme                 fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
3670c982533eSVincent Franchomme                 if (!fileref->fcb->ea_xattr.Buffer) {
3671c982533eSVincent Franchomme                     ERR("out of memory\n");
3672c982533eSVincent Franchomme                     return STATUS_INSUFFICIENT_RESOURCES;
3673c982533eSVincent Franchomme                 }
3674c982533eSVincent Franchomme 
3675c982533eSVincent Franchomme                 fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
3676c982533eSVincent Franchomme                 RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
3677c982533eSVincent Franchomme             } else {
3678c982533eSVincent Franchomme                 if (fileref->fcb->ea_xattr.Length > 0) {
3679c982533eSVincent Franchomme                     ExFreePool(fileref->fcb->ea_xattr.Buffer);
3680c982533eSVincent Franchomme                     fileref->fcb->ea_xattr.Buffer = NULL;
3681c982533eSVincent Franchomme                     fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
3682c982533eSVincent Franchomme 
3683c982533eSVincent Franchomme                     fileref->fcb->ea_changed = true;
3684c982533eSVincent Franchomme                     fileref->fcb->ealen = 0;
3685c982533eSVincent Franchomme                 }
3686c982533eSVincent Franchomme             }
3687c982533eSVincent Franchomme 
3688c982533eSVincent Franchomme             // remove streams and send notifications
3689c982533eSVincent Franchomme             le = fileref->fcb->dir_children_index.Flink;
3690c982533eSVincent Franchomme             while (le != &fileref->fcb->dir_children_index) {
3691c982533eSVincent Franchomme                 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3692c982533eSVincent Franchomme                 LIST_ENTRY* le2 = le->Flink;
3693c982533eSVincent Franchomme 
3694c982533eSVincent Franchomme                 if (dc->index == 0) {
3695c982533eSVincent Franchomme                     if (!dc->fileref) {
3696c982533eSVincent Franchomme                         file_ref* fr2;
3697c982533eSVincent Franchomme 
3698c982533eSVincent Franchomme                         Status = open_fileref_child(Vcb, fileref, &dc->name, true, true, true, PagedPool, &fr2, NULL);
3699c982533eSVincent Franchomme                         if (!NT_SUCCESS(Status))
3700c982533eSVincent Franchomme                             WARN("open_fileref_child returned %08lx\n", Status);
3701c982533eSVincent Franchomme                     }
3702c982533eSVincent Franchomme 
3703c982533eSVincent Franchomme                     if (dc->fileref) {
3704c982533eSVincent Franchomme                         queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
3705c982533eSVincent Franchomme 
3706c982533eSVincent Franchomme                         Status = delete_fileref(dc->fileref, NULL, false, NULL, rollback);
3707c982533eSVincent Franchomme                         if (!NT_SUCCESS(Status)) {
3708c982533eSVincent Franchomme                             ERR("delete_fileref returned %08lx\n", Status);
3709c982533eSVincent Franchomme                             return Status;
3710c982533eSVincent Franchomme                         }
3711c982533eSVincent Franchomme                     }
3712c982533eSVincent Franchomme                 } else
3713c982533eSVincent Franchomme                     break;
3714c982533eSVincent Franchomme 
3715c982533eSVincent Franchomme                 le = le2;
3716c982533eSVincent Franchomme             }
3717c982533eSVincent Franchomme         }
3718c982533eSVincent Franchomme 
3719c982533eSVincent Franchomme         KeQuerySystemTime(&time);
3720c982533eSVincent Franchomme         win_time_to_unix(time, &now);
3721c982533eSVincent Franchomme 
3722c982533eSVincent Franchomme         filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
3723c982533eSVincent Franchomme 
3724c982533eSVincent Franchomme         if (fileref->fcb->ads) {
3725c982533eSVincent Franchomme             fileref->parent->fcb->inode_item.st_mtime = now;
3726c982533eSVincent Franchomme             fileref->parent->fcb->inode_item_changed = true;
3727c982533eSVincent Franchomme             mark_fcb_dirty(fileref->parent->fcb);
3728c982533eSVincent Franchomme 
3729c982533eSVincent Franchomme             queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
3730c982533eSVincent Franchomme         } else {
3731c982533eSVincent Franchomme             mark_fcb_dirty(fileref->fcb);
3732c982533eSVincent Franchomme 
3733c982533eSVincent Franchomme             oldatts = fileref->fcb->atts;
3734c982533eSVincent Franchomme 
3735c982533eSVincent Franchomme             defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
3736c982533eSVincent Franchomme                                         fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
3737c982533eSVincent Franchomme 
3738c982533eSVincent Franchomme             if (RequestedDisposition == FILE_SUPERSEDE)
3739c982533eSVincent Franchomme                 fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
3740c982533eSVincent Franchomme             else
3741c982533eSVincent Franchomme                 fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
3742c982533eSVincent Franchomme 
3743c982533eSVincent Franchomme             if (fileref->fcb->atts != oldatts) {
3744c982533eSVincent Franchomme                 fileref->fcb->atts_changed = true;
3745c982533eSVincent Franchomme                 fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
3746c982533eSVincent Franchomme                 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
3747c982533eSVincent Franchomme             }
3748c982533eSVincent Franchomme 
3749c982533eSVincent Franchomme             fileref->fcb->inode_item.transid = Vcb->superblock.generation;
3750c982533eSVincent Franchomme             fileref->fcb->inode_item.sequence++;
3751c982533eSVincent Franchomme             fileref->fcb->inode_item.st_ctime = now;
3752c982533eSVincent Franchomme             fileref->fcb->inode_item.st_mtime = now;
3753c982533eSVincent Franchomme             fileref->fcb->inode_item_changed = true;
3754c982533eSVincent Franchomme 
3755c982533eSVincent Franchomme             queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3756c982533eSVincent Franchomme         }
3757c982533eSVincent Franchomme     } else {
3758c982533eSVincent Franchomme         if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
3759c982533eSVincent Franchomme             FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
3760c982533eSVincent Franchomme 
3761c982533eSVincent Franchomme             do {
3762c982533eSVincent Franchomme                 if (ffei->Flags & FILE_NEED_EA) {
3763c982533eSVincent Franchomme                     WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3764c982533eSVincent Franchomme 
3765c982533eSVincent Franchomme                     return STATUS_ACCESS_DENIED;
3766c982533eSVincent Franchomme                 }
3767c982533eSVincent Franchomme 
3768c982533eSVincent Franchomme                 if (ffei->NextEntryOffset == 0)
3769c982533eSVincent Franchomme                     break;
3770c982533eSVincent Franchomme 
3771c982533eSVincent Franchomme                 ffei = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ffei) + ffei->NextEntryOffset);
3772c982533eSVincent Franchomme             } while (true);
3773c982533eSVincent Franchomme         }
3774c982533eSVincent Franchomme     }
3775c982533eSVincent Franchomme 
3776c982533eSVincent Franchomme     FileObject->FsContext = fileref->fcb;
3777c982533eSVincent Franchomme 
3778c982533eSVincent Franchomme     ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
3779c982533eSVincent Franchomme     if (!ccb) {
3780c982533eSVincent Franchomme         ERR("out of memory\n");
3781c982533eSVincent Franchomme 
3782c982533eSVincent Franchomme         return STATUS_INSUFFICIENT_RESOURCES;
3783c982533eSVincent Franchomme     }
3784c982533eSVincent Franchomme 
3785c982533eSVincent Franchomme     RtlZeroMemory(ccb, sizeof(*ccb));
3786c982533eSVincent Franchomme 
3787c982533eSVincent Franchomme     ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3788c982533eSVincent Franchomme     ccb->NodeSize = sizeof(*ccb);
3789c982533eSVincent Franchomme     ccb->disposition = RequestedDisposition;
3790c982533eSVincent Franchomme     ccb->options = options;
3791c982533eSVincent Franchomme     ccb->query_dir_offset = 0;
3792c982533eSVincent Franchomme     RtlInitUnicodeString(&ccb->query_string, NULL);
3793c982533eSVincent Franchomme     ccb->has_wildcard = false;
3794c982533eSVincent Franchomme     ccb->specific_file = false;
3795c982533eSVincent Franchomme     ccb->access = granted_access;
3796c982533eSVincent Franchomme     ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
3797c982533eSVincent Franchomme     ccb->reserving = false;
3798c982533eSVincent Franchomme     ccb->lxss = called_from_lxss();
3799c982533eSVincent Franchomme 
3800c982533eSVincent Franchomme     ccb->fileref = fileref;
3801c982533eSVincent Franchomme 
3802c982533eSVincent Franchomme     FileObject->FsContext2 = ccb;
3803c982533eSVincent Franchomme     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
3804c982533eSVincent Franchomme 
3805c982533eSVincent Franchomme     switch (RequestedDisposition) {
3806c982533eSVincent Franchomme         case FILE_SUPERSEDE:
3807c982533eSVincent Franchomme             Irp->IoStatus.Information = FILE_SUPERSEDED;
3808c982533eSVincent Franchomme             break;
3809c982533eSVincent Franchomme 
3810c982533eSVincent Franchomme         case FILE_OPEN:
3811c982533eSVincent Franchomme         case FILE_OPEN_IF:
3812c982533eSVincent Franchomme             Irp->IoStatus.Information = FILE_OPENED;
3813c982533eSVincent Franchomme             break;
3814c982533eSVincent Franchomme 
3815c982533eSVincent Franchomme         case FILE_OVERWRITE:
3816c982533eSVincent Franchomme         case FILE_OVERWRITE_IF:
3817c982533eSVincent Franchomme             Irp->IoStatus.Information = FILE_OVERWRITTEN;
3818c982533eSVincent Franchomme             break;
3819c982533eSVincent Franchomme     }
3820c982533eSVincent Franchomme 
3821c982533eSVincent Franchomme     // Make sure paging files don't have any extents marked as being prealloc,
3822c982533eSVincent Franchomme     // as this would mean we'd have to lock exclusively when writing.
3823c982533eSVincent Franchomme     if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
3824c982533eSVincent Franchomme         LIST_ENTRY* le;
3825c982533eSVincent Franchomme         bool changed = false;
3826c982533eSVincent Franchomme 
3827c982533eSVincent Franchomme         ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
3828c982533eSVincent Franchomme 
3829c982533eSVincent Franchomme         le = fileref->fcb->extents.Flink;
3830c982533eSVincent Franchomme 
3831c982533eSVincent Franchomme         while (le != &fileref->fcb->extents) {
3832c982533eSVincent Franchomme             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3833c982533eSVincent Franchomme 
3834c982533eSVincent Franchomme             if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
3835c982533eSVincent Franchomme                 ext->extent_data.type = EXTENT_TYPE_REGULAR;
3836c982533eSVincent Franchomme                 changed = true;
3837c982533eSVincent Franchomme             }
3838c982533eSVincent Franchomme 
3839c982533eSVincent Franchomme             le = le->Flink;
3840c982533eSVincent Franchomme         }
3841c982533eSVincent Franchomme 
3842c982533eSVincent Franchomme         ExReleaseResourceLite(fileref->fcb->Header.Resource);
3843c982533eSVincent Franchomme 
3844c982533eSVincent Franchomme         if (changed) {
3845c982533eSVincent Franchomme             fileref->fcb->extents_changed = true;
3846c982533eSVincent Franchomme             mark_fcb_dirty(fileref->fcb);
3847c982533eSVincent Franchomme         }
3848c982533eSVincent Franchomme 
3849c982533eSVincent Franchomme         fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
3850c982533eSVincent Franchomme     }
3851c982533eSVincent Franchomme 
3852c982533eSVincent Franchomme #ifdef DEBUG_FCB_REFCOUNTS
3853c982533eSVincent Franchomme     LONG oc = InterlockedIncrement(&fileref->open_count);
3854c982533eSVincent Franchomme     ERR("fileref %p: open_count now %i\n", fileref, oc);
3855c982533eSVincent Franchomme #else
3856c982533eSVincent Franchomme     InterlockedIncrement(&fileref->open_count);
3857c982533eSVincent Franchomme #endif
3858c982533eSVincent Franchomme     InterlockedIncrement(&Vcb->open_files);
3859c982533eSVincent Franchomme 
3860c982533eSVincent Franchomme     return STATUS_SUCCESS;
3861c982533eSVincent Franchomme }
3862c982533eSVincent Franchomme 
oplock_complete(PVOID Context,PIRP Irp)38636e0cf03dSVincent Franchomme static void __stdcall oplock_complete(PVOID Context, PIRP Irp) {
3864c982533eSVincent Franchomme     NTSTATUS Status;
3865c982533eSVincent Franchomme     LIST_ENTRY rollback;
3866c982533eSVincent Franchomme     bool skip_lock;
3867c982533eSVincent Franchomme     oplock_context* ctx = Context;
3868c982533eSVincent Franchomme     device_extension* Vcb = ctx->Vcb;
3869c982533eSVincent Franchomme 
3870c982533eSVincent Franchomme     TRACE("(%p, %p)\n", Context, Irp);
3871c982533eSVincent Franchomme 
3872c982533eSVincent Franchomme     InitializeListHead(&rollback);
3873c982533eSVincent Franchomme 
3874c982533eSVincent Franchomme     skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
3875c982533eSVincent Franchomme 
3876c982533eSVincent Franchomme     if (!skip_lock)
3877c982533eSVincent Franchomme         ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3878c982533eSVincent Franchomme 
3879c982533eSVincent Franchomme     ExAcquireResourceSharedLite(&Vcb->fileref_lock, true);
3880c982533eSVincent Franchomme 
3881c982533eSVincent Franchomme     // FIXME - trans
3882c982533eSVincent Franchomme     Status = open_file3(Vcb, Irp, ctx->granted_access, ctx->fileref, &rollback);
3883c982533eSVincent Franchomme 
3884c982533eSVincent Franchomme     if (!NT_SUCCESS(Status)) {
3885c982533eSVincent Franchomme         free_fileref(ctx->fileref);
3886c982533eSVincent Franchomme         do_rollback(ctx->Vcb, &rollback);
3887c982533eSVincent Franchomme     } else
3888c982533eSVincent Franchomme         clear_rollback(&rollback);
3889c982533eSVincent Franchomme 
3890c982533eSVincent Franchomme     ExReleaseResourceLite(&Vcb->fileref_lock);
3891c982533eSVincent Franchomme 
3892c982533eSVincent Franchomme     if (Status == STATUS_SUCCESS) {
3893c982533eSVincent Franchomme         fcb* fcb2;
3894c982533eSVincent Franchomme         PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3895c982533eSVincent Franchomme         PFILE_OBJECT FileObject = IrpSp->FileObject;
3896c982533eSVincent Franchomme         bool skip_fcb_lock;
3897c982533eSVincent Franchomme 
3898c982533eSVincent Franchomme         IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= ctx->granted_access;
3899c982533eSVincent Franchomme         IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(ctx->granted_access | MAXIMUM_ALLOWED);
3900c982533eSVincent Franchomme 
3901c982533eSVincent Franchomme         if (!FileObject->Vpb)
3902c982533eSVincent Franchomme             FileObject->Vpb = Vcb->devobj->Vpb;
3903c982533eSVincent Franchomme 
3904c982533eSVincent Franchomme         fcb2 = FileObject->FsContext;
3905c982533eSVincent Franchomme 
3906c982533eSVincent Franchomme         if (fcb2->ads) {
3907c982533eSVincent Franchomme             struct _ccb* ccb2 = FileObject->FsContext2;
3908c982533eSVincent Franchomme 
3909c982533eSVincent Franchomme             fcb2 = ccb2->fileref->parent->fcb;
3910c982533eSVincent Franchomme         }
3911c982533eSVincent Franchomme 
3912c982533eSVincent Franchomme         skip_fcb_lock = ExIsResourceAcquiredExclusiveLite(fcb2->Header.Resource);
3913c982533eSVincent Franchomme 
3914c982533eSVincent Franchomme         if (!skip_fcb_lock)
3915c982533eSVincent Franchomme             ExAcquireResourceExclusiveLite(fcb2->Header.Resource, true);
3916c982533eSVincent Franchomme 
3917c982533eSVincent Franchomme         fcb_load_csums(Vcb, fcb2, Irp);
3918c982533eSVincent Franchomme 
3919c982533eSVincent Franchomme         if (!skip_fcb_lock)
3920c982533eSVincent Franchomme             ExReleaseResourceLite(fcb2->Header.Resource);
3921c982533eSVincent Franchomme     }
3922c982533eSVincent Franchomme 
3923c982533eSVincent Franchomme     if (!skip_lock)
3924c982533eSVincent Franchomme         ExReleaseResourceLite(&Vcb->tree_lock);
3925c982533eSVincent Franchomme 
3926c982533eSVincent Franchomme     // FIXME - call free_trans if failed and within transaction
3927c982533eSVincent Franchomme 
3928c982533eSVincent Franchomme     Irp->IoStatus.Status = Status;
3929c982533eSVincent Franchomme     IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
3930c982533eSVincent Franchomme 
39316e0cf03dSVincent Franchomme     ctx->Status = Status;
39326e0cf03dSVincent Franchomme 
39336e0cf03dSVincent Franchomme     KeSetEvent(&ctx->event, 0, false);
3934c982533eSVincent Franchomme }
3935c982533eSVincent Franchomme 
open_file2(device_extension * Vcb,ULONG RequestedDisposition,file_ref * fileref,ACCESS_MASK * granted_access,PFILE_OBJECT FileObject,UNICODE_STRING * fn,ULONG options,PIRP Irp,LIST_ENTRY * rollback,oplock_context ** opctx)3936c982533eSVincent Franchomme static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, file_ref* fileref, ACCESS_MASK* granted_access,
39376e0cf03dSVincent Franchomme                            PFILE_OBJECT FileObject, UNICODE_STRING* fn, ULONG options, PIRP Irp, LIST_ENTRY* rollback,
39386e0cf03dSVincent Franchomme                            oplock_context** opctx) {
3939883b1f31SPierre Schweitzer     NTSTATUS Status;
3940883b1f31SPierre Schweitzer     file_ref* sf;
3941318da0c1SPierre Schweitzer     bool readonly;
3942883b1f31SPierre Schweitzer     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3943883b1f31SPierre Schweitzer 
3944883b1f31SPierre Schweitzer     if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
3945883b1f31SPierre Schweitzer         LARGE_INTEGER zero;
3946883b1f31SPierre Schweitzer 
3947883b1f31SPierre Schweitzer         if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
3948b826992aSVincent Franchomme             Status = STATUS_ACCESS_DENIED;
3949b826992aSVincent Franchomme             goto end;
3950883b1f31SPierre Schweitzer         }
3951883b1f31SPierre Schweitzer 
3952883b1f31SPierre Schweitzer         if (Vcb->readonly) {
3953b826992aSVincent Franchomme             Status = STATUS_MEDIA_WRITE_PROTECTED;
3954b826992aSVincent Franchomme             goto end;
3955883b1f31SPierre Schweitzer         }
3956883b1f31SPierre Schweitzer 
3957883b1f31SPierre Schweitzer         zero.QuadPart = 0;
3958883b1f31SPierre Schweitzer         if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
3959b826992aSVincent Franchomme             Status = STATUS_USER_MAPPED_FILE;
3960b826992aSVincent Franchomme             goto end;
3961883b1f31SPierre Schweitzer         }
3962883b1f31SPierre Schweitzer     }
3963883b1f31SPierre Schweitzer 
3964883b1f31SPierre Schweitzer     if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
3965883b1f31SPierre Schweitzer         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3966883b1f31SPierre Schweitzer 
3967883b1f31SPierre Schweitzer         if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
3968883b1f31SPierre Schweitzer                             &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
3969318da0c1SPierre Schweitzer                             true, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
3970883b1f31SPierre Schweitzer                             IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
3971883b1f31SPierre Schweitzer                             granted_access, &Status)) {
3972883b1f31SPierre Schweitzer             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3973194ea909SVictor Perevertkin             TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3974b826992aSVincent Franchomme             goto end;
3975883b1f31SPierre Schweitzer         }
3976883b1f31SPierre Schweitzer 
3977883b1f31SPierre Schweitzer         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3978883b1f31SPierre Schweitzer     } else
3979883b1f31SPierre Schweitzer         *granted_access = 0;
3980883b1f31SPierre Schweitzer 
3981318da0c1SPierre Schweitzer     TRACE("deleted = %s\n", fileref->deleted ? "true" : "false");
3982883b1f31SPierre Schweitzer 
3983883b1f31SPierre Schweitzer     sf = fileref;
3984883b1f31SPierre Schweitzer     while (sf) {
3985883b1f31SPierre Schweitzer         if (sf->delete_on_close) {
3986883b1f31SPierre Schweitzer             TRACE("could not open as deletion pending\n");
3987b826992aSVincent Franchomme             Status = STATUS_DELETE_PENDING;
3988b826992aSVincent Franchomme             goto end;
3989883b1f31SPierre Schweitzer         }
3990883b1f31SPierre Schweitzer         sf = sf->parent;
3991883b1f31SPierre Schweitzer     }
3992883b1f31SPierre Schweitzer 
3993174dfab6SVincent Franchomme     readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) ||
3994174dfab6SVincent Franchomme                (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY && !(IrpSp->Flags & SL_IGNORE_READONLY_ATTRIBUTE)) ||
3995883b1f31SPierre Schweitzer                is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
3996883b1f31SPierre Schweitzer 
3997883b1f31SPierre Schweitzer     if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
3998b826992aSVincent Franchomme         Status = STATUS_CANNOT_DELETE;
3999b826992aSVincent Franchomme         goto end;
4000883b1f31SPierre Schweitzer     }
4001883b1f31SPierre Schweitzer 
40026e0cf03dSVincent Franchomme     readonly |= fileref->fcb->inode_item.flags_ro & BTRFS_INODE_RO_VERITY;
40036e0cf03dSVincent Franchomme 
4004883b1f31SPierre Schweitzer     if (readonly) {
4005883b1f31SPierre Schweitzer         ACCESS_MASK allowed;
4006883b1f31SPierre Schweitzer 
4007883b1f31SPierre Schweitzer         allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
4008883b1f31SPierre Schweitzer                     FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
4009883b1f31SPierre Schweitzer                     FILE_TRAVERSE;
4010883b1f31SPierre Schweitzer 
4011883b1f31SPierre Schweitzer         if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
4012883b1f31SPierre Schweitzer             allowed |= DELETE;
4013883b1f31SPierre Schweitzer 
4014883b1f31SPierre Schweitzer         if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
4015883b1f31SPierre Schweitzer             allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
4016883b1f31SPierre Schweitzer 
4017883b1f31SPierre Schweitzer             if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
4018883b1f31SPierre Schweitzer                 allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
4019883b1f31SPierre Schweitzer         } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
4020883b1f31SPierre Schweitzer             // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
4021883b1f31SPierre Schweitzer 
4022883b1f31SPierre Schweitzer             allowed |= FILE_WRITE_ATTRIBUTES;
4023883b1f31SPierre Schweitzer         }
4024883b1f31SPierre Schweitzer 
4025883b1f31SPierre Schweitzer         if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
4026883b1f31SPierre Schweitzer             *granted_access &= allowed;
4027883b1f31SPierre Schweitzer             IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
4028883b1f31SPierre Schweitzer         } else if (*granted_access & ~allowed) {
4029b826992aSVincent Franchomme             Status = Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
4030b826992aSVincent Franchomme             goto end;
4031883b1f31SPierre Schweitzer         }
4032c982533eSVincent Franchomme 
4033c982533eSVincent Franchomme         if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
4034c982533eSVincent Franchomme             WARN("cannot overwrite readonly file\n");
4035c982533eSVincent Franchomme             Status = STATUS_ACCESS_DENIED;
4036c982533eSVincent Franchomme             goto end;
4037c982533eSVincent Franchomme         }
4038883b1f31SPierre Schweitzer     }
4039883b1f31SPierre Schweitzer 
4040883b1f31SPierre Schweitzer     if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
4041883b1f31SPierre Schweitzer         REPARSE_DATA_BUFFER* data;
4042883b1f31SPierre Schweitzer 
4043883b1f31SPierre Schweitzer         /* How reparse points work from the point of view of the filesystem appears to
4044883b1f31SPierre Schweitzer             * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
4045883b1f31SPierre Schweitzer             * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
4046883b1f31SPierre Schweitzer             * translation. If we instead return the reparse tag in Information, and store
4047883b1f31SPierre Schweitzer             * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
4048883b1f31SPierre Schweitzer             * IopSymlinkProcessReparse will do the translation for us. */
4049883b1f31SPierre Schweitzer 
4050318da0c1SPierre Schweitzer         Status = get_reparse_block(fileref->fcb, (uint8_t**)&data);
4051883b1f31SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
4052194ea909SVictor Perevertkin             ERR("get_reparse_block returned %08lx\n", Status);
4053883b1f31SPierre Schweitzer             Status = STATUS_SUCCESS;
4054883b1f31SPierre Schweitzer         } else {
4055883b1f31SPierre Schweitzer             Irp->IoStatus.Information = data->ReparseTag;
4056883b1f31SPierre Schweitzer 
4057883b1f31SPierre Schweitzer             if (fn->Buffer[(fn->Length / sizeof(WCHAR)) - 1] == '\\')
4058883b1f31SPierre Schweitzer                 data->Reserved = sizeof(WCHAR);
4059883b1f31SPierre Schweitzer 
4060883b1f31SPierre Schweitzer             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
4061883b1f31SPierre Schweitzer 
4062b826992aSVincent Franchomme             Status = STATUS_REPARSE;
4063b826992aSVincent Franchomme             goto end;
4064883b1f31SPierre Schweitzer         }
4065883b1f31SPierre Schweitzer     }
4066883b1f31SPierre Schweitzer 
4067883b1f31SPierre Schweitzer     if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
4068883b1f31SPierre Schweitzer         if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
4069b826992aSVincent Franchomme             Status = STATUS_FILE_IS_A_DIRECTORY;
4070b826992aSVincent Franchomme             goto end;
4071883b1f31SPierre Schweitzer         }
4072883b1f31SPierre Schweitzer     } else if (options & FILE_DIRECTORY_FILE) {
407362e630deSPierre Schweitzer         TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u)\n", fileref->fcb->type);
4074b826992aSVincent Franchomme         Status = STATUS_NOT_A_DIRECTORY;
4075b826992aSVincent Franchomme         goto end;
4076883b1f31SPierre Schweitzer     }
4077883b1f31SPierre Schweitzer 
4078883b1f31SPierre Schweitzer     if (fileref->open_count > 0) {
4079c982533eSVincent Franchomme         oplock_context* ctx;
4080c982533eSVincent Franchomme 
4081318da0c1SPierre Schweitzer         Status = IoCheckShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, false);
4082883b1f31SPierre Schweitzer 
4083883b1f31SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
4084883b1f31SPierre Schweitzer             if (Status == STATUS_SHARING_VIOLATION)
4085194ea909SVictor Perevertkin                 TRACE("IoCheckShareAccess failed, returning %08lx\n", Status);
4086883b1f31SPierre Schweitzer             else
4087194ea909SVictor Perevertkin                 WARN("IoCheckShareAccess failed, returning %08lx\n", Status);
4088883b1f31SPierre Schweitzer 
4089b826992aSVincent Franchomme             goto end;
4090883b1f31SPierre Schweitzer         }
4091883b1f31SPierre Schweitzer 
4092c982533eSVincent Franchomme         ctx = ExAllocatePoolWithTag(NonPagedPool, sizeof(oplock_context), ALLOC_TAG);
4093c982533eSVincent Franchomme         if (!ctx) {
4094c982533eSVincent Franchomme             ERR("out of memory\n");
4095c982533eSVincent Franchomme             Status = STATUS_INSUFFICIENT_RESOURCES;
4096c982533eSVincent Franchomme             goto end;
4097c982533eSVincent Franchomme         }
4098c982533eSVincent Franchomme 
4099c982533eSVincent Franchomme         ctx->Vcb = Vcb;
4100c982533eSVincent Franchomme         ctx->granted_access = *granted_access;
4101c982533eSVincent Franchomme         ctx->fileref = fileref;
41026e0cf03dSVincent Franchomme         KeInitializeEvent(&ctx->event, NotificationEvent, false);
4103c982533eSVincent Franchomme #ifdef __REACTOS__
4104c982533eSVincent Franchomme         Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, ctx, (POPLOCK_WAIT_COMPLETE_ROUTINE) oplock_complete, NULL);
4105c982533eSVincent Franchomme #else
4106c982533eSVincent Franchomme         Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, ctx, oplock_complete, NULL);
4107c982533eSVincent Franchomme #endif /* __REACTOS__ */
41086e0cf03dSVincent Franchomme         if (Status == STATUS_PENDING) {
41096e0cf03dSVincent Franchomme             *opctx = ctx;
4110c982533eSVincent Franchomme             return Status;
41116e0cf03dSVincent Franchomme         }
4112c982533eSVincent Franchomme 
4113c982533eSVincent Franchomme         ExFreePool(ctx);
4114c982533eSVincent Franchomme 
4115c982533eSVincent Franchomme         if (!NT_SUCCESS(Status)) {
4116c982533eSVincent Franchomme             WARN("FsRtlCheckOplock returned %08lx\n", Status);
4117c982533eSVincent Franchomme             goto end;
4118c982533eSVincent Franchomme         }
4119c982533eSVincent Franchomme 
4120883b1f31SPierre Schweitzer         IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
4121883b1f31SPierre Schweitzer     } else
4122883b1f31SPierre Schweitzer         IoSetShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
4123883b1f31SPierre Schweitzer 
4124c982533eSVincent Franchomme     Status = open_file3(Vcb, Irp, *granted_access, fileref, rollback);
4125883b1f31SPierre Schweitzer 
4126b826992aSVincent Franchomme     if (!NT_SUCCESS(Status))
4127b826992aSVincent Franchomme         IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
4128b826992aSVincent Franchomme 
4129b826992aSVincent Franchomme end:
4130b826992aSVincent Franchomme     if (!NT_SUCCESS(Status))
4131b826992aSVincent Franchomme         free_fileref(fileref);
4132b826992aSVincent Franchomme 
4133b826992aSVincent Franchomme     return Status;
4134883b1f31SPierre Schweitzer }
4135883b1f31SPierre Schweitzer 
4136883b1f31SPierre Schweitzer NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
4137318da0c1SPierre Schweitzer                                root* subvol, uint64_t inode, file_ref** pfr, PIRP Irp) {
4138883b1f31SPierre Schweitzer     NTSTATUS Status;
4139883b1f31SPierre Schweitzer     fcb* fcb;
4140318da0c1SPierre Schweitzer     uint64_t parent = 0;
4141883b1f31SPierre Schweitzer     UNICODE_STRING name;
4142318da0c1SPierre Schweitzer     bool hl_alloc = false;
4143883b1f31SPierre Schweitzer     file_ref *parfr, *fr;
4144883b1f31SPierre Schweitzer 
4145318da0c1SPierre Schweitzer     Status = open_fcb(Vcb, subvol, inode, 0, NULL, true, NULL, &fcb, PagedPool, Irp);
4146883b1f31SPierre Schweitzer     if (!NT_SUCCESS(Status)) {
4147194ea909SVictor Perevertkin         ERR("open_fcb returned %08lx\n", Status);
4148883b1f31SPierre Schweitzer         return Status;
4149883b1f31SPierre Schweitzer     }
4150883b1f31SPierre Schweitzer 
4151318da0c1SPierre Schweitzer     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4152f381137cSPierre Schweitzer 
4153f381137cSPierre Schweitzer     if (fcb->inode_item.st_nlink == 0 || fcb->deleted) {
4154f381137cSPierre Schweitzer         ExReleaseResourceLite(fcb->Header.Resource);
4155f381137cSPierre Schweitzer         free_fcb(fcb);
4156f381137cSPierre Schweitzer         return STATUS_OBJECT_NAME_NOT_FOUND;
4157f381137cSPierre Schweitzer     }
4158f381137cSPierre Schweitzer 
4159883b1f31SPierre Schweitzer     if (fcb->fileref) {
4160883b1f31SPierre Schweitzer         *pfr = fcb->fileref;
4161883b1f31SPierre Schweitzer         increase_fileref_refcount(fcb->fileref);
4162f381137cSPierre Schweitzer         free_fcb(fcb);
4163f381137cSPierre Schweitzer         ExReleaseResourceLite(fcb->Header.Resource);
4164883b1f31SPierre Schweitzer         return STATUS_SUCCESS;
4165883b1f31SPierre Schweitzer     }
4166883b1f31SPierre Schweitzer 
4167f381137cSPierre Schweitzer     if (IsListEmpty(&fcb->hardlinks)) {
4168f381137cSPierre Schweitzer         ExReleaseResourceLite(fcb->Header.Resource);
4169f381137cSPierre Schweitzer 
4170318da0c1SPierre Schweitzer         ExAcquireResourceSharedLite(&Vcb->dirty_filerefs_lock, true);
4171f381137cSPierre Schweitzer 
4172f381137cSPierre Schweitzer         if (!IsListEmpty(&Vcb->dirty_filerefs)) {
4173f381137cSPierre Schweitzer             LIST_ENTRY* le = Vcb->dirty_filerefs.Flink;
4174f381137cSPierre Schweitzer             while (le != &Vcb->dirty_filerefs) {
4175318da0c1SPierre Schweitzer                 fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty);
4176f381137cSPierre Schweitzer 
4177f381137cSPierre Schweitzer                 if (fr->fcb == fcb) {
4178f381137cSPierre Schweitzer                     ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
4179f381137cSPierre Schweitzer                     increase_fileref_refcount(fr);
4180f381137cSPierre Schweitzer                     free_fcb(fcb);
4181f381137cSPierre Schweitzer                     *pfr = fr;
4182f381137cSPierre Schweitzer                     return STATUS_SUCCESS;
4183f381137cSPierre Schweitzer                 }
4184f381137cSPierre Schweitzer 
4185f381137cSPierre Schweitzer                 le = le->Flink;
4186f381137cSPierre Schweitzer             }
4187f381137cSPierre Schweitzer         }
4188f381137cSPierre Schweitzer 
4189f381137cSPierre Schweitzer         ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
4190f381137cSPierre Schweitzer 
4191f381137cSPierre Schweitzer         {
4192f381137cSPierre Schweitzer             KEY searchkey;
4193f381137cSPierre Schweitzer             traverse_ptr tp;
4194f381137cSPierre Schweitzer 
4195f381137cSPierre Schweitzer             searchkey.obj_id = fcb->inode;
4196f381137cSPierre Schweitzer             searchkey.obj_type = TYPE_INODE_REF;
4197f381137cSPierre Schweitzer             searchkey.offset = 0;
4198f381137cSPierre Schweitzer 
4199318da0c1SPierre Schweitzer             Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
4200f381137cSPierre Schweitzer             if (!NT_SUCCESS(Status)) {
4201194ea909SVictor Perevertkin                 ERR("find_item returned %08lx\n", Status);
4202f381137cSPierre Schweitzer                 free_fcb(fcb);
4203f381137cSPierre Schweitzer                 return Status;
4204f381137cSPierre Schweitzer             }
4205f381137cSPierre Schweitzer 
4206f381137cSPierre Schweitzer             do {
4207f381137cSPierre Schweitzer                 traverse_ptr next_tp;
4208f381137cSPierre Schweitzer 
4209f381137cSPierre Schweitzer                 if (tp.item->key.obj_id > fcb->inode || (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type > TYPE_INODE_EXTREF))
4210f381137cSPierre Schweitzer                     break;
4211f381137cSPierre Schweitzer 
4212f381137cSPierre Schweitzer                 if (tp.item->key.obj_id == fcb->inode) {
4213f381137cSPierre Schweitzer                     if (tp.item->key.obj_type == TYPE_INODE_REF) {
4214f381137cSPierre Schweitzer                         INODE_REF* ir = (INODE_REF*)tp.item->data;
4215883b1f31SPierre Schweitzer 
4216f381137cSPierre Schweitzer                         if (tp.item->size < offsetof(INODE_REF, name[0]) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
4217f381137cSPierre Schweitzer                             ERR("INODE_REF was too short\n");
4218f381137cSPierre Schweitzer                             free_fcb(fcb);
4219f381137cSPierre Schweitzer                             return STATUS_INTERNAL_ERROR;
4220f381137cSPierre Schweitzer                         }
4221f381137cSPierre Schweitzer 
4222f381137cSPierre Schweitzer                         ULONG stringlen;
4223f381137cSPierre Schweitzer 
4224318da0c1SPierre Schweitzer                         Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n);
4225f381137cSPierre Schweitzer                         if (!NT_SUCCESS(Status)) {
4226194ea909SVictor Perevertkin                             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4227f381137cSPierre Schweitzer                             free_fcb(fcb);
4228f381137cSPierre Schweitzer                             return Status;
4229f381137cSPierre Schweitzer                         }
4230f381137cSPierre Schweitzer 
4231318da0c1SPierre Schweitzer                         name.Length = name.MaximumLength = (uint16_t)stringlen;
4232f381137cSPierre Schweitzer 
4233f381137cSPierre Schweitzer                         if (stringlen == 0)
4234f381137cSPierre Schweitzer                             name.Buffer = NULL;
4235f381137cSPierre Schweitzer                         else {
4236f381137cSPierre Schweitzer                             name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4237f381137cSPierre Schweitzer 
4238f381137cSPierre Schweitzer                             if (!name.Buffer) {
4239f381137cSPierre Schweitzer                                 ERR("out of memory\n");
4240f381137cSPierre Schweitzer                                 free_fcb(fcb);
4241f381137cSPierre Schweitzer                                 return STATUS_INSUFFICIENT_RESOURCES;
4242f381137cSPierre Schweitzer                             }
4243f381137cSPierre Schweitzer 
4244318da0c1SPierre Schweitzer                             Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
4245f381137cSPierre Schweitzer                             if (!NT_SUCCESS(Status)) {
4246194ea909SVictor Perevertkin                                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4247f381137cSPierre Schweitzer                                 ExFreePool(name.Buffer);
4248f381137cSPierre Schweitzer                                 free_fcb(fcb);
4249f381137cSPierre Schweitzer                                 return Status;
4250f381137cSPierre Schweitzer                             }
4251f381137cSPierre Schweitzer 
4252318da0c1SPierre Schweitzer                             hl_alloc = true;
4253f381137cSPierre Schweitzer                         }
4254f381137cSPierre Schweitzer 
4255f381137cSPierre Schweitzer                         parent = tp.item->key.offset;
4256f381137cSPierre Schweitzer 
4257f381137cSPierre Schweitzer                         break;
4258f381137cSPierre Schweitzer                     } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
4259f381137cSPierre Schweitzer                         INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
4260f381137cSPierre Schweitzer 
4261f381137cSPierre Schweitzer                         if (tp.item->size < offsetof(INODE_EXTREF, name[0]) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
4262f381137cSPierre Schweitzer                             ERR("INODE_EXTREF was too short\n");
4263f381137cSPierre Schweitzer                             free_fcb(fcb);
4264f381137cSPierre Schweitzer                             return STATUS_INTERNAL_ERROR;
4265f381137cSPierre Schweitzer                         }
4266f381137cSPierre Schweitzer 
4267f381137cSPierre Schweitzer                         ULONG stringlen;
4268f381137cSPierre Schweitzer 
4269318da0c1SPierre Schweitzer                         Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n);
4270f381137cSPierre Schweitzer                         if (!NT_SUCCESS(Status)) {
4271194ea909SVictor Perevertkin                             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4272f381137cSPierre Schweitzer                             free_fcb(fcb);
4273f381137cSPierre Schweitzer                             return Status;
4274f381137cSPierre Schweitzer                         }
4275f381137cSPierre Schweitzer 
4276318da0c1SPierre Schweitzer                         name.Length = name.MaximumLength = (uint16_t)stringlen;
4277f381137cSPierre Schweitzer 
4278f381137cSPierre Schweitzer                         if (stringlen == 0)
4279f381137cSPierre Schweitzer                             name.Buffer = NULL;
4280f381137cSPierre Schweitzer                         else {
4281f381137cSPierre Schweitzer                             name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4282f381137cSPierre Schweitzer 
4283f381137cSPierre Schweitzer                             if (!name.Buffer) {
4284f381137cSPierre Schweitzer                                 ERR("out of memory\n");
4285f381137cSPierre Schweitzer                                 free_fcb(fcb);
4286f381137cSPierre Schweitzer                                 return STATUS_INSUFFICIENT_RESOURCES;
4287f381137cSPierre Schweitzer                             }
4288f381137cSPierre Schweitzer 
4289318da0c1SPierre Schweitzer                             Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
4290f381137cSPierre Schweitzer                             if (!NT_SUCCESS(Status)) {
4291194ea909SVictor Perevertkin                                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4292f381137cSPierre Schweitzer                                 ExFreePool(name.Buffer);
4293f381137cSPierre Schweitzer                                 free_fcb(fcb);
4294f381137cSPierre Schweitzer                                 return Status;
4295f381137cSPierre Schweitzer                             }
4296f381137cSPierre Schweitzer 
4297318da0c1SPierre Schweitzer                             hl_alloc = true;
4298f381137cSPierre Schweitzer                         }
4299f381137cSPierre Schweitzer 
4300f381137cSPierre Schweitzer                         parent = ier->dir;
4301f381137cSPierre Schweitzer 
4302f381137cSPierre Schweitzer                         break;
4303f381137cSPierre Schweitzer                     }
4304f381137cSPierre Schweitzer                 }
4305f381137cSPierre Schweitzer 
4306318da0c1SPierre Schweitzer                 if (find_next_item(Vcb, &tp, &next_tp, false, Irp))
4307f381137cSPierre Schweitzer                     tp = next_tp;
4308f381137cSPierre Schweitzer                 else
4309f381137cSPierre Schweitzer                     break;
4310318da0c1SPierre Schweitzer             } while (true);
4311f381137cSPierre Schweitzer         }
4312f381137cSPierre Schweitzer 
4313f381137cSPierre Schweitzer         if (parent == 0) {
4314f381137cSPierre Schweitzer             WARN("trying to open inode with no references\n");
4315f381137cSPierre Schweitzer             free_fcb(fcb);
4316f381137cSPierre Schweitzer             return STATUS_INVALID_PARAMETER;
4317f381137cSPierre Schweitzer         }
4318f381137cSPierre Schweitzer     } else {
4319f381137cSPierre Schweitzer         hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
4320f381137cSPierre Schweitzer 
4321883b1f31SPierre Schweitzer         name = hl->name;
4322883b1f31SPierre Schweitzer         parent = hl->parent;
4323883b1f31SPierre Schweitzer 
4324f381137cSPierre Schweitzer         ExReleaseResourceLite(fcb->Header.Resource);
4325f381137cSPierre Schweitzer     }
4326f381137cSPierre Schweitzer 
4327883b1f31SPierre Schweitzer     if (parent == inode) { // subvolume root
4328883b1f31SPierre Schweitzer         KEY searchkey;
4329883b1f31SPierre Schweitzer         traverse_ptr tp;
4330883b1f31SPierre Schweitzer 
4331883b1f31SPierre Schweitzer         searchkey.obj_id = subvol->id;
4332883b1f31SPierre Schweitzer         searchkey.obj_type = TYPE_ROOT_BACKREF;
4333883b1f31SPierre Schweitzer         searchkey.offset = 0xffffffffffffffff;
4334883b1f31SPierre Schweitzer 
4335318da0c1SPierre Schweitzer         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
4336883b1f31SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
4337194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
4338883b1f31SPierre Schweitzer             free_fcb(fcb);
4339883b1f31SPierre Schweitzer             return Status;
4340883b1f31SPierre Schweitzer         }
4341883b1f31SPierre Schweitzer 
4342883b1f31SPierre Schweitzer         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
4343883b1f31SPierre Schweitzer             ROOT_REF* rr = (ROOT_REF*)tp.item->data;
4344883b1f31SPierre Schweitzer             LIST_ENTRY* le;
4345883b1f31SPierre Schweitzer             root* r = NULL;
4346883b1f31SPierre Schweitzer             ULONG stringlen;
4347883b1f31SPierre Schweitzer 
4348883b1f31SPierre Schweitzer             if (tp.item->size < sizeof(ROOT_REF)) {
4349194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
4350883b1f31SPierre Schweitzer                 free_fcb(fcb);
4351883b1f31SPierre Schweitzer                 return STATUS_INTERNAL_ERROR;
4352883b1f31SPierre Schweitzer             }
4353883b1f31SPierre Schweitzer 
4354883b1f31SPierre Schweitzer             if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
4355194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n);
4356883b1f31SPierre Schweitzer                 free_fcb(fcb);
4357883b1f31SPierre Schweitzer                 return STATUS_INTERNAL_ERROR;
4358883b1f31SPierre Schweitzer             }
4359883b1f31SPierre Schweitzer 
4360883b1f31SPierre Schweitzer             le = Vcb->roots.Flink;
4361883b1f31SPierre Schweitzer             while (le != &Vcb->roots) {
4362883b1f31SPierre Schweitzer                 root* r2 = CONTAINING_RECORD(le, root, list_entry);
4363883b1f31SPierre Schweitzer 
4364883b1f31SPierre Schweitzer                 if (r2->id == tp.item->key.offset) {
4365883b1f31SPierre Schweitzer                     r = r2;
4366883b1f31SPierre Schweitzer                     break;
4367883b1f31SPierre Schweitzer                 }
4368883b1f31SPierre Schweitzer 
4369883b1f31SPierre Schweitzer                 le = le->Flink;
4370883b1f31SPierre Schweitzer             }
4371883b1f31SPierre Schweitzer 
4372883b1f31SPierre Schweitzer             if (!r) {
4373318da0c1SPierre Schweitzer                 ERR("couldn't find subvol %I64x\n", tp.item->key.offset);
4374883b1f31SPierre Schweitzer                 free_fcb(fcb);
4375883b1f31SPierre Schweitzer                 return STATUS_INTERNAL_ERROR;
4376883b1f31SPierre Schweitzer             }
4377883b1f31SPierre Schweitzer 
4378883b1f31SPierre Schweitzer             Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
4379883b1f31SPierre Schweitzer             if (!NT_SUCCESS(Status)) {
4380194ea909SVictor Perevertkin                 ERR("open_fileref_by_inode returned %08lx\n", Status);
4381883b1f31SPierre Schweitzer                 free_fcb(fcb);
4382883b1f31SPierre Schweitzer                 return Status;
4383883b1f31SPierre Schweitzer             }
4384883b1f31SPierre Schweitzer 
4385318da0c1SPierre Schweitzer             Status = utf8_to_utf16(NULL, 0, &stringlen, rr->name, rr->n);
4386883b1f31SPierre Schweitzer             if (!NT_SUCCESS(Status)) {
4387194ea909SVictor Perevertkin                 ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4388883b1f31SPierre Schweitzer                 free_fcb(fcb);
4389883b1f31SPierre Schweitzer                 return Status;
4390883b1f31SPierre Schweitzer             }
4391883b1f31SPierre Schweitzer 
4392318da0c1SPierre Schweitzer             name.Length = name.MaximumLength = (uint16_t)stringlen;
4393883b1f31SPierre Schweitzer 
4394883b1f31SPierre Schweitzer             if (stringlen == 0)
4395883b1f31SPierre Schweitzer                 name.Buffer = NULL;
4396883b1f31SPierre Schweitzer             else {
4397f381137cSPierre Schweitzer                 if (hl_alloc)
4398f381137cSPierre Schweitzer                     ExFreePool(name.Buffer);
4399f381137cSPierre Schweitzer 
4400883b1f31SPierre Schweitzer                 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4401883b1f31SPierre Schweitzer 
4402883b1f31SPierre Schweitzer                 if (!name.Buffer) {
4403883b1f31SPierre Schweitzer                     ERR("out of memory\n");
4404883b1f31SPierre Schweitzer                     free_fcb(fcb);
4405883b1f31SPierre Schweitzer                     return STATUS_INSUFFICIENT_RESOURCES;
4406883b1f31SPierre Schweitzer                 }
4407883b1f31SPierre Schweitzer 
4408318da0c1SPierre Schweitzer                 Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
4409883b1f31SPierre Schweitzer                 if (!NT_SUCCESS(Status)) {
4410194ea909SVictor Perevertkin                     ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4411883b1f31SPierre Schweitzer                     ExFreePool(name.Buffer);
4412883b1f31SPierre Schweitzer                     free_fcb(fcb);
4413883b1f31SPierre Schweitzer                     return Status;
4414883b1f31SPierre Schweitzer                 }
4415883b1f31SPierre Schweitzer 
4416318da0c1SPierre Schweitzer                 hl_alloc = true;
4417883b1f31SPierre Schweitzer             }
4418883b1f31SPierre Schweitzer         } else {
441962e630deSPierre Schweitzer             if (!Vcb->options.no_root_dir && subvol->id == BTRFS_ROOT_FSTREE && Vcb->root_fileref->fcb->subvol != subvol) {
442062e630deSPierre Schweitzer                 Status = open_fileref_by_inode(Vcb, Vcb->root_fileref->fcb->subvol, SUBVOL_ROOT_INODE, &parfr, Irp);
442162e630deSPierre Schweitzer                 if (!NT_SUCCESS(Status)) {
4422194ea909SVictor Perevertkin                     ERR("open_fileref_by_inode returned %08lx\n", Status);
442362e630deSPierre Schweitzer                     free_fcb(fcb);
442462e630deSPierre Schweitzer                     return Status;
442562e630deSPierre Schweitzer                 }
442662e630deSPierre Schweitzer 
442762e630deSPierre Schweitzer                 name.Length = name.MaximumLength = sizeof(root_dir_utf16) - sizeof(WCHAR);
442862e630deSPierre Schweitzer                 name.Buffer = (WCHAR*)root_dir_utf16;
442962e630deSPierre Schweitzer             } else {
4430318da0c1SPierre Schweitzer                 ERR("couldn't find parent for subvol %I64x\n", subvol->id);
4431883b1f31SPierre Schweitzer                 free_fcb(fcb);
4432883b1f31SPierre Schweitzer                 return STATUS_INTERNAL_ERROR;
4433883b1f31SPierre Schweitzer             }
443462e630deSPierre Schweitzer         }
4435883b1f31SPierre Schweitzer     } else {
4436883b1f31SPierre Schweitzer         Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
4437883b1f31SPierre Schweitzer         if (!NT_SUCCESS(Status)) {
4438194ea909SVictor Perevertkin             ERR("open_fileref_by_inode returned %08lx\n", Status);
4439883b1f31SPierre Schweitzer             free_fcb(fcb);
4440883b1f31SPierre Schweitzer             return Status;
4441883b1f31SPierre Schweitzer         }
4442883b1f31SPierre Schweitzer     }
4443883b1f31SPierre Schweitzer 
4444318da0c1SPierre Schweitzer     Status = open_fileref_child(Vcb, parfr, &name, true, true, false, PagedPool, &fr, Irp);
4445883b1f31SPierre Schweitzer 
4446883b1f31SPierre Schweitzer     if (hl_alloc)
4447883b1f31SPierre Schweitzer         ExFreePool(name.Buffer);
4448883b1f31SPierre Schweitzer 
4449883b1f31SPierre Schweitzer     if (!NT_SUCCESS(Status)) {
4450194ea909SVictor Perevertkin         ERR("open_fileref_child returned %08lx\n", Status);
4451883b1f31SPierre Schweitzer 
4452883b1f31SPierre Schweitzer         free_fcb(fcb);
4453883b1f31SPierre Schweitzer         free_fileref(parfr);
4454883b1f31SPierre Schweitzer 
4455883b1f31SPierre Schweitzer         return Status;
4456883b1f31SPierre Schweitzer     }
4457883b1f31SPierre Schweitzer 
4458883b1f31SPierre Schweitzer     *pfr = fr;
4459883b1f31SPierre Schweitzer 
4460883b1f31SPierre Schweitzer     free_fcb(fcb);
4461883b1f31SPierre Schweitzer     free_fileref(parfr);
4462883b1f31SPierre Schweitzer 
4463883b1f31SPierre Schweitzer     return STATUS_SUCCESS;
4464883b1f31SPierre Schweitzer }
4465883b1f31SPierre Schweitzer 
44666e0cf03dSVincent Franchomme static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp,
44676e0cf03dSVincent Franchomme                           LIST_ENTRY* rollback, oplock_context** opctx) {
4468c2c66affSColin Finck     PFILE_OBJECT FileObject = NULL;
4469c2c66affSColin Finck     ULONG RequestedDisposition;
4470c2c66affSColin Finck     ULONG options;
4471c2c66affSColin Finck     NTSTATUS Status;
4472c2c66affSColin Finck     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4473c2c66affSColin Finck     USHORT parsed;
4474c2c66affSColin Finck     ULONG fn_offset = 0;
4475c2c66affSColin Finck     file_ref *related, *fileref = NULL;
4476c2c66affSColin Finck     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
4477c2c66affSColin Finck     ACCESS_MASK granted_access;
4478318da0c1SPierre Schweitzer     bool loaded_related = false;
4479c2c66affSColin Finck     UNICODE_STRING fn;
4480c2c66affSColin Finck 
4481c2c66affSColin Finck     Irp->IoStatus.Information = 0;
4482c2c66affSColin Finck 
4483c2c66affSColin Finck     RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
4484c2c66affSColin Finck     options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
4485c2c66affSColin Finck 
4486c2c66affSColin Finck     if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
4487c2c66affSColin Finck         WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
4488c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
4489c2c66affSColin Finck     }
4490c2c66affSColin Finck 
4491c2c66affSColin Finck     FileObject = IrpSp->FileObject;
4492c2c66affSColin Finck 
4493c2c66affSColin Finck     if (!FileObject) {
4494c2c66affSColin Finck         ERR("FileObject was NULL\n");
4495c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
4496c2c66affSColin Finck     }
4497c2c66affSColin Finck 
4498c2c66affSColin Finck     if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
4499c2c66affSColin Finck         struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
4500c2c66affSColin Finck 
4501c2c66affSColin Finck         related = relatedccb->fileref;
4502c2c66affSColin Finck     } else
4503c2c66affSColin Finck         related = NULL;
4504c2c66affSColin Finck 
4505c2c66affSColin Finck     debug_create_options(options);
4506c2c66affSColin Finck 
4507c2c66affSColin Finck     switch (RequestedDisposition) {
4508c2c66affSColin Finck         case FILE_SUPERSEDE:
4509c2c66affSColin Finck             TRACE("requested disposition: FILE_SUPERSEDE\n");
4510c2c66affSColin Finck             break;
4511c2c66affSColin Finck 
4512c2c66affSColin Finck         case FILE_CREATE:
4513c2c66affSColin Finck             TRACE("requested disposition: FILE_CREATE\n");
4514c2c66affSColin Finck             break;
4515c2c66affSColin Finck 
4516c2c66affSColin Finck         case FILE_OPEN:
4517c2c66affSColin Finck             TRACE("requested disposition: FILE_OPEN\n");
4518c2c66affSColin Finck             break;
4519c2c66affSColin Finck 
4520c2c66affSColin Finck         case FILE_OPEN_IF:
4521c2c66affSColin Finck             TRACE("requested disposition: FILE_OPEN_IF\n");
4522c2c66affSColin Finck             break;
4523c2c66affSColin Finck 
4524c2c66affSColin Finck         case FILE_OVERWRITE:
4525c2c66affSColin Finck             TRACE("requested disposition: FILE_OVERWRITE\n");
4526c2c66affSColin Finck             break;
4527c2c66affSColin Finck 
4528c2c66affSColin Finck         case FILE_OVERWRITE_IF:
4529c2c66affSColin Finck             TRACE("requested disposition: FILE_OVERWRITE_IF\n");
4530c2c66affSColin Finck             break;
4531c2c66affSColin Finck 
4532c2c66affSColin Finck         default:
4533194ea909SVictor Perevertkin             ERR("unknown disposition: %lx\n", RequestedDisposition);
4534c2c66affSColin Finck             Status = STATUS_NOT_IMPLEMENTED;
4535c2c66affSColin Finck             goto exit;
4536c2c66affSColin Finck     }
4537c2c66affSColin Finck 
4538c2c66affSColin Finck     fn = FileObject->FileName;
4539c2c66affSColin Finck 
4540194ea909SVictor Perevertkin     TRACE("(%.*S)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4541c2c66affSColin Finck     TRACE("FileObject = %p\n", FileObject);
4542c2c66affSColin Finck 
4543c2c66affSColin Finck     if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
4544c2c66affSColin Finck         Status = STATUS_MEDIA_WRITE_PROTECTED;
4545c2c66affSColin Finck         goto exit;
4546c2c66affSColin Finck     }
4547c2c66affSColin Finck 
4548c2c66affSColin Finck     if (options & FILE_OPEN_BY_FILE_ID) {
4549f381137cSPierre Schweitzer         if (RequestedDisposition != FILE_OPEN) {
4550f381137cSPierre Schweitzer             WARN("FILE_OPEN_BY_FILE_ID not supported for anything other than FILE_OPEN\n");
4551f381137cSPierre Schweitzer             Status = STATUS_INVALID_PARAMETER;
4552f381137cSPierre Schweitzer             goto exit;
4553f381137cSPierre Schweitzer         }
4554f381137cSPierre Schweitzer 
4555318da0c1SPierre Schweitzer         if (fn.Length == sizeof(uint64_t)) {
4556318da0c1SPierre Schweitzer             uint64_t inode;
4557c2c66affSColin Finck 
4558f381137cSPierre Schweitzer             if (!related) {
4559*29d19382SJohannes Obermayr                 WARN("cannot open by short file ID unless related fileref also provided"\n);
4560f381137cSPierre Schweitzer                 Status = STATUS_INVALID_PARAMETER;
4561f381137cSPierre Schweitzer                 goto exit;
4562f381137cSPierre Schweitzer             }
4563f381137cSPierre Schweitzer 
4564194ea909SVictor Perevertkin             inode = (*(uint64_t*)fn.Buffer) & 0xffffffffff;
4565c2c66affSColin Finck 
4566c2c66affSColin Finck             if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
4567c2c66affSColin Finck                 inode = Vcb->root_fileref->fcb->inode;
4568c2c66affSColin Finck 
4569c2c66affSColin Finck             if (inode == 0) { // we use 0 to mean the parent of a subvolume
4570c2c66affSColin Finck                 fileref = related->parent;
4571c2c66affSColin Finck                 increase_fileref_refcount(fileref);
4572c2c66affSColin Finck                 Status = STATUS_SUCCESS;
4573883b1f31SPierre Schweitzer             } else
4574c2c66affSColin Finck                 Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
4575883b1f31SPierre Schweitzer 
4576883b1f31SPierre Schweitzer             goto loaded;
4577f381137cSPierre Schweitzer         } else if (fn.Length == sizeof(FILE_ID_128)) {
4578318da0c1SPierre Schweitzer             uint64_t inode, subvol_id;
4579f381137cSPierre Schweitzer             root* subvol = NULL;
4580f381137cSPierre Schweitzer 
4581318da0c1SPierre Schweitzer             RtlCopyMemory(&inode, fn.Buffer, sizeof(uint64_t));
4582318da0c1SPierre Schweitzer             RtlCopyMemory(&subvol_id, (uint8_t*)fn.Buffer + sizeof(uint64_t), sizeof(uint64_t));
4583f381137cSPierre Schweitzer 
4584f381137cSPierre Schweitzer             if (subvol_id == BTRFS_ROOT_FSTREE || (subvol_id >= 0x100 && subvol_id < 0x8000000000000000)) {
4585f381137cSPierre Schweitzer                 LIST_ENTRY* le = Vcb->roots.Flink;
4586f381137cSPierre Schweitzer                 while (le != &Vcb->roots) {
4587f381137cSPierre Schweitzer                     root* r = CONTAINING_RECORD(le, root, list_entry);
4588f381137cSPierre Schweitzer 
4589f381137cSPierre Schweitzer                     if (r->id == subvol_id) {
4590f381137cSPierre Schweitzer                         subvol = r;
4591f381137cSPierre Schweitzer                         break;
4592f381137cSPierre Schweitzer                     }
4593f381137cSPierre Schweitzer 
4594f381137cSPierre Schweitzer                     le = le->Flink;
4595f381137cSPierre Schweitzer                 }
4596f381137cSPierre Schweitzer             }
4597f381137cSPierre Schweitzer 
4598f381137cSPierre Schweitzer             if (!subvol) {
4599318da0c1SPierre Schweitzer                 WARN("subvol %I64x not found\n", subvol_id);
4600f381137cSPierre Schweitzer                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
4601f381137cSPierre Schweitzer             } else
4602f381137cSPierre Schweitzer                 Status = open_fileref_by_inode(Vcb, subvol, inode, &fileref, Irp);
4603f381137cSPierre Schweitzer 
4604f381137cSPierre Schweitzer             goto loaded;
4605c2c66affSColin Finck         } else {
4606f381137cSPierre Schweitzer             WARN("invalid ID size for FILE_OPEN_BY_FILE_ID\n");
4607f381137cSPierre Schweitzer             Status = STATUS_INVALID_PARAMETER;
4608c2c66affSColin Finck             goto exit;
4609c2c66affSColin Finck         }
4610883b1f31SPierre Schweitzer     }
4611883b1f31SPierre Schweitzer 
4612c2c66affSColin Finck     if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
46134672b2baSPierre Schweitzer         Status = STATUS_INVALID_PARAMETER;
4614c2c66affSColin Finck         goto exit;
4615c2c66affSColin Finck     }
4616c2c66affSColin Finck 
4617c2c66affSColin Finck     if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
4618c2c66affSColin Finck         ULONG fnoff;
4619c2c66affSColin Finck 
4620318da0c1SPierre Schweitzer         Status = open_fileref(Vcb, &related, &fn, NULL, true, &parsed, &fnoff,
4621c2c66affSColin Finck                               pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4622c2c66affSColin Finck 
4623c2c66affSColin Finck         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
4624c2c66affSColin Finck             Status = STATUS_OBJECT_PATH_NOT_FOUND;
4625c2c66affSColin Finck         else if (Status == STATUS_REPARSE)
4626c2c66affSColin Finck             fileref = related;
4627c2c66affSColin Finck         else if (NT_SUCCESS(Status)) {
4628c2c66affSColin Finck             fnoff *= sizeof(WCHAR);
4629c2c66affSColin Finck             fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
4630c2c66affSColin Finck 
4631c2c66affSColin Finck             if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
4632c2c66affSColin Finck                 Status = STATUS_REPARSE;
4633c2c66affSColin Finck                 fileref = related;
4634c2c66affSColin Finck                 parsed = (USHORT)fnoff - sizeof(WCHAR);
4635c2c66affSColin Finck             } else {
4636c2c66affSColin Finck                 fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
4637c2c66affSColin Finck                 fn.Length -= (USHORT)fnoff;
4638c2c66affSColin Finck 
4639c2c66affSColin Finck                 Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
4640c2c66affSColin Finck                                       pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4641c2c66affSColin Finck 
4642318da0c1SPierre Schweitzer                 loaded_related = true;
4643c2c66affSColin Finck             }
4644c2c66affSColin Finck         }
4645c2c66affSColin Finck     } else {
4646c2c66affSColin Finck         Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
4647c2c66affSColin Finck                               pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4648c2c66affSColin Finck     }
4649c2c66affSColin Finck 
4650883b1f31SPierre Schweitzer loaded:
4651c2c66affSColin Finck     if (Status == STATUS_REPARSE) {
4652c2c66affSColin Finck         REPARSE_DATA_BUFFER* data;
4653c2c66affSColin Finck 
4654318da0c1SPierre Schweitzer         ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, true);
4655318da0c1SPierre Schweitzer         Status = get_reparse_block(fileref->fcb, (uint8_t**)&data);
4656c2c66affSColin Finck         ExReleaseResourceLite(fileref->fcb->Header.Resource);
4657c2c66affSColin Finck 
4658c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
4659194ea909SVictor Perevertkin             ERR("get_reparse_block returned %08lx\n", Status);
4660c2c66affSColin Finck 
4661c2c66affSColin Finck             Status = STATUS_SUCCESS;
4662c2c66affSColin Finck         } else {
4663c2c66affSColin Finck             Status = STATUS_REPARSE;
4664c2c66affSColin Finck             RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
4665c2c66affSColin Finck 
4666c2c66affSColin Finck             data->Reserved = FileObject->FileName.Length - parsed;
4667c2c66affSColin Finck 
4668c2c66affSColin Finck             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
4669c2c66affSColin Finck 
4670883b1f31SPierre Schweitzer             free_fileref(fileref);
4671c2c66affSColin Finck 
4672c2c66affSColin Finck             goto exit;
4673c2c66affSColin Finck         }
4674c2c66affSColin Finck     }
4675c2c66affSColin Finck 
4676c2c66affSColin Finck     if (NT_SUCCESS(Status) && fileref->deleted)
4677c2c66affSColin Finck         Status = STATUS_OBJECT_NAME_NOT_FOUND;
4678c2c66affSColin Finck 
4679c2c66affSColin Finck     if (NT_SUCCESS(Status)) {
4680c2c66affSColin Finck         if (RequestedDisposition == FILE_CREATE) {
468162e630deSPierre Schweitzer             TRACE("file already exists, returning STATUS_OBJECT_NAME_COLLISION\n");
4682c2c66affSColin Finck             Status = STATUS_OBJECT_NAME_COLLISION;
4683c2c66affSColin Finck 
4684883b1f31SPierre Schweitzer             free_fileref(fileref);
4685c2c66affSColin Finck 
4686c2c66affSColin Finck             goto exit;
4687c2c66affSColin Finck         }
4688c2c66affSColin Finck     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
4689c2c66affSColin Finck         if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
4690c2c66affSColin Finck             TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
4691c2c66affSColin Finck             goto exit;
4692c2c66affSColin Finck         }
469398654b54SVincent Franchomme     } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_INVALID) {
4694194ea909SVictor Perevertkin         TRACE("open_fileref returned %08lx\n", Status);
4695c2c66affSColin Finck         goto exit;
4696c2c66affSColin Finck     } else {
4697194ea909SVictor Perevertkin         ERR("open_fileref returned %08lx\n", Status);
4698c2c66affSColin Finck         goto exit;
4699c2c66affSColin Finck     }
4700c2c66affSColin Finck 
47016e0cf03dSVincent Franchomme     if (NT_SUCCESS(Status)) { // file already exists
47026e0cf03dSVincent Franchomme         Status = open_file2(Vcb, RequestedDisposition, fileref, &granted_access, FileObject, &fn,
47036e0cf03dSVincent Franchomme                             options, Irp, rollback, opctx);
47046e0cf03dSVincent Franchomme     } else {
470506042735SVincent Franchomme         file_ref* existing_file = NULL;
4706c2c66affSColin Finck 
4707883b1f31SPierre Schweitzer         Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, &existing_file, rollback);
4708c2c66affSColin Finck 
4709883b1f31SPierre Schweitzer         if (Status == STATUS_OBJECT_NAME_COLLISION) { // already exists
4710883b1f31SPierre Schweitzer             fileref = existing_file;
4711883b1f31SPierre Schweitzer 
47126e0cf03dSVincent Franchomme             Status = open_file2(Vcb, RequestedDisposition, fileref, &granted_access, FileObject, &fn,
47136e0cf03dSVincent Franchomme                                 options, Irp, rollback, opctx);
4714883b1f31SPierre Schweitzer         } else {
4715c2c66affSColin Finck             Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
471657293803SMark Harmstone             granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
4717c2c66affSColin Finck         }
4718883b1f31SPierre Schweitzer     }
4719c2c66affSColin Finck 
4720c2c66affSColin Finck     if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
4721c2c66affSColin Finck         FileObject->Flags |= FO_CACHE_SUPPORTED;
4722c2c66affSColin Finck 
4723c2c66affSColin Finck exit:
4724883b1f31SPierre Schweitzer     if (loaded_related)
4725883b1f31SPierre Schweitzer         free_fileref(related);
4726c2c66affSColin Finck 
4727c2c66affSColin Finck     if (Status == STATUS_SUCCESS) {
4728c2c66affSColin Finck         fcb* fcb2;
4729c2c66affSColin Finck 
4730c2c66affSColin Finck         IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= granted_access;
4731c2c66affSColin Finck         IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(granted_access | MAXIMUM_ALLOWED);
4732c2c66affSColin Finck 
4733c2c66affSColin Finck         if (!FileObject->Vpb)
4734c2c66affSColin Finck             FileObject->Vpb = DeviceObject->Vpb;
4735c2c66affSColin Finck 
4736c2c66affSColin Finck         fcb2 = FileObject->FsContext;
4737c2c66affSColin Finck 
4738c2c66affSColin Finck         if (fcb2->ads) {
4739c2c66affSColin Finck             struct _ccb* ccb2 = FileObject->FsContext2;
4740c2c66affSColin Finck 
4741c2c66affSColin Finck             fcb2 = ccb2->fileref->parent->fcb;
4742c2c66affSColin Finck         }
4743c2c66affSColin Finck 
4744318da0c1SPierre Schweitzer         ExAcquireResourceExclusiveLite(fcb2->Header.Resource, true);
4745c2c66affSColin Finck         fcb_load_csums(Vcb, fcb2, Irp);
4746c2c66affSColin Finck         ExReleaseResourceLite(fcb2->Header.Resource);
4747c2c66affSColin Finck     } else if (Status != STATUS_REPARSE && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
4748194ea909SVictor Perevertkin         TRACE("returning %08lx\n", Status);
4749c2c66affSColin Finck 
4750c2c66affSColin Finck     return Status;
4751c2c66affSColin Finck }
4752c2c66affSColin Finck 
verify_vcb(device_extension * Vcb,PIRP Irp)4753c2c66affSColin Finck static NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
4754c2c66affSColin Finck     NTSTATUS Status;
4755c2c66affSColin Finck     LIST_ENTRY* le;
4756318da0c1SPierre Schweitzer     bool need_verify = false;
4757c2c66affSColin Finck 
4758318da0c1SPierre Schweitzer     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4759c2c66affSColin Finck 
4760c2c66affSColin Finck     le = Vcb->devices.Flink;
4761c2c66affSColin Finck     while (le != &Vcb->devices) {
4762c2c66affSColin Finck         device* dev = CONTAINING_RECORD(le, device, list_entry);
4763c2c66affSColin Finck 
4764c2c66affSColin Finck         if (dev->devobj && dev->removable) {
4765c2c66affSColin Finck             ULONG cc;
4766c2c66affSColin Finck             IO_STATUS_BLOCK iosb;
4767c2c66affSColin Finck 
4768318da0c1SPierre Schweitzer             Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
4769c2c66affSColin Finck 
4770c2c66affSColin Finck             if (IoIsErrorUserInduced(Status)) {
4771194ea909SVictor Perevertkin                 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status);
4772318da0c1SPierre Schweitzer                 need_verify = true;
4773c2c66affSColin Finck             } else if (!NT_SUCCESS(Status)) {
4774194ea909SVictor Perevertkin                 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status);
4775c2c66affSColin Finck                 goto end;
4776c2c66affSColin Finck             } else if (iosb.Information < sizeof(ULONG)) {
4777c2c66affSColin Finck                 ERR("iosb.Information was too short\n");
4778c2c66affSColin Finck                 Status = STATUS_INTERNAL_ERROR;
4779c2c66affSColin Finck             } else if (cc != dev->change_count) {
4780c2c66affSColin Finck                 dev->devobj->Flags |= DO_VERIFY_VOLUME;
4781318da0c1SPierre Schweitzer                 need_verify = true;
4782c2c66affSColin Finck             }
4783c2c66affSColin Finck         }
4784c2c66affSColin Finck 
4785c2c66affSColin Finck         le = le->Flink;
4786c2c66affSColin Finck     }
4787c2c66affSColin Finck 
4788c2c66affSColin Finck     Status = STATUS_SUCCESS;
4789c2c66affSColin Finck 
4790c2c66affSColin Finck end:
4791c2c66affSColin Finck     ExReleaseResourceLite(&Vcb->tree_lock);
4792c2c66affSColin Finck 
4793c2c66affSColin Finck     if (need_verify) {
4794c2c66affSColin Finck         PDEVICE_OBJECT devobj;
4795c2c66affSColin Finck 
4796c2c66affSColin Finck         devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
4797c2c66affSColin Finck         IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
4798c2c66affSColin Finck 
4799c2c66affSColin Finck         if (!devobj) {
4800c2c66affSColin Finck             devobj = IoGetDeviceToVerify(PsGetCurrentThread());
4801c2c66affSColin Finck             IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
4802c2c66affSColin Finck         }
4803c2c66affSColin Finck 
4804c2c66affSColin Finck         devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
4805c2c66affSColin Finck 
4806c2c66affSColin Finck         if (devobj)
4807318da0c1SPierre Schweitzer             Status = IoVerifyVolume(devobj, false);
4808c2c66affSColin Finck         else
4809c2c66affSColin Finck             Status = STATUS_VERIFY_REQUIRED;
4810c2c66affSColin Finck     }
4811c2c66affSColin Finck 
4812c2c66affSColin Finck     return Status;
4813c2c66affSColin Finck }
4814c2c66affSColin Finck 
has_manage_volume_privilege(ACCESS_STATE * access_state,KPROCESSOR_MODE processor_mode)4815318da0c1SPierre Schweitzer static bool has_manage_volume_privilege(ACCESS_STATE* access_state, KPROCESSOR_MODE processor_mode) {
4816c2c66affSColin Finck     PRIVILEGE_SET privset;
4817c2c66affSColin Finck 
4818c2c66affSColin Finck     privset.PrivilegeCount = 1;
4819c2c66affSColin Finck     privset.Control = PRIVILEGE_SET_ALL_NECESSARY;
4820c2c66affSColin Finck     privset.Privilege[0].Luid = RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE);
4821c2c66affSColin Finck     privset.Privilege[0].Attributes = 0;
4822c2c66affSColin Finck 
4823318da0c1SPierre Schweitzer     return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? true : false;
4824c2c66affSColin Finck }
4825c2c66affSColin Finck 
4826c2c66affSColin Finck _Dispatch_type_(IRP_MJ_CREATE)
_Function_class_(DRIVER_DISPATCH)4827c2c66affSColin Finck _Function_class_(DRIVER_DISPATCH)
4828318da0c1SPierre Schweitzer NTSTATUS __stdcall drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4829c2c66affSColin Finck     NTSTATUS Status;
4830c2c66affSColin Finck     PIO_STACK_LOCATION IrpSp;
4831c2c66affSColin Finck     device_extension* Vcb = DeviceObject->DeviceExtension;
4832318da0c1SPierre Schweitzer     bool top_level, locked = false;
48336e0cf03dSVincent Franchomme     oplock_context* opctx = NULL;
4834c2c66affSColin Finck 
4835c2c66affSColin Finck     FsRtlEnterFileSystem();
4836c2c66affSColin Finck 
4837194ea909SVictor Perevertkin     TRACE("create (flags = %lx)\n", Irp->Flags);
4838c2c66affSColin Finck 
4839c2c66affSColin Finck     top_level = is_top_level(Irp);
4840c2c66affSColin Finck 
4841c2c66affSColin Finck     /* return success if just called for FS device object */
4842c2c66affSColin Finck     if (DeviceObject == master_devobj)  {
4843c2c66affSColin Finck         TRACE("create called for FS device object\n");
4844c2c66affSColin Finck 
4845c2c66affSColin Finck         Irp->IoStatus.Information = FILE_OPENED;
4846c2c66affSColin Finck         Status = STATUS_SUCCESS;
4847c2c66affSColin Finck 
4848c2c66affSColin Finck         goto exit;
4849c2c66affSColin Finck     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4850c2c66affSColin Finck         Status = vol_create(DeviceObject, Irp);
4851c2c66affSColin Finck         goto exit;
4852c2c66affSColin Finck     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4853c2c66affSColin Finck         Status = STATUS_INVALID_PARAMETER;
4854c2c66affSColin Finck         goto exit;
4855c2c66affSColin Finck     }
4856c2c66affSColin Finck 
4857c2c66affSColin Finck     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
4858c2c66affSColin Finck         Status = STATUS_DEVICE_NOT_READY;
4859c2c66affSColin Finck         goto exit;
4860c2c66affSColin Finck     }
4861c2c66affSColin Finck 
4862c2c66affSColin Finck     if (Vcb->removing) {
4863c2c66affSColin Finck         Status = STATUS_ACCESS_DENIED;
4864c2c66affSColin Finck         goto exit;
4865c2c66affSColin Finck     }
4866c2c66affSColin Finck 
4867c2c66affSColin Finck     Status = verify_vcb(Vcb, Irp);
4868c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
4869194ea909SVictor Perevertkin         ERR("verify_vcb returned %08lx\n", Status);
4870c2c66affSColin Finck         goto exit;
4871c2c66affSColin Finck     }
4872c2c66affSColin Finck 
4873318da0c1SPierre Schweitzer     ExAcquireResourceSharedLite(&Vcb->load_lock, true);
4874318da0c1SPierre Schweitzer     locked = true;
4875c2c66affSColin Finck 
4876c2c66affSColin Finck     IrpSp = IoGetCurrentIrpStackLocation(Irp);
4877c2c66affSColin Finck 
4878c2c66affSColin Finck     if (IrpSp->Flags != 0) {
4879318da0c1SPierre Schweitzer         uint32_t flags = IrpSp->Flags;
4880c2c66affSColin Finck 
4881c2c66affSColin Finck         TRACE("flags:\n");
4882c2c66affSColin Finck 
4883c2c66affSColin Finck         if (flags & SL_CASE_SENSITIVE) {
4884c2c66affSColin Finck             TRACE("SL_CASE_SENSITIVE\n");
4885c2c66affSColin Finck             flags &= ~SL_CASE_SENSITIVE;
4886c2c66affSColin Finck         }
4887c2c66affSColin Finck 
4888c2c66affSColin Finck         if (flags & SL_FORCE_ACCESS_CHECK) {
4889c2c66affSColin Finck             TRACE("SL_FORCE_ACCESS_CHECK\n");
4890c2c66affSColin Finck             flags &= ~SL_FORCE_ACCESS_CHECK;
4891c2c66affSColin Finck         }
4892c2c66affSColin Finck 
4893c2c66affSColin Finck         if (flags & SL_OPEN_PAGING_FILE) {
4894c2c66affSColin Finck             TRACE("SL_OPEN_PAGING_FILE\n");
4895c2c66affSColin Finck             flags &= ~SL_OPEN_PAGING_FILE;
4896c2c66affSColin Finck         }
4897c2c66affSColin Finck 
4898c2c66affSColin Finck         if (flags & SL_OPEN_TARGET_DIRECTORY) {
4899c2c66affSColin Finck             TRACE("SL_OPEN_TARGET_DIRECTORY\n");
4900c2c66affSColin Finck             flags &= ~SL_OPEN_TARGET_DIRECTORY;
4901c2c66affSColin Finck         }
4902c2c66affSColin Finck 
4903c2c66affSColin Finck         if (flags & SL_STOP_ON_SYMLINK) {
4904c2c66affSColin Finck             TRACE("SL_STOP_ON_SYMLINK\n");
4905c2c66affSColin Finck             flags &= ~SL_STOP_ON_SYMLINK;
4906c2c66affSColin Finck         }
4907c2c66affSColin Finck 
4908174dfab6SVincent Franchomme         if (flags & SL_IGNORE_READONLY_ATTRIBUTE) {
4909174dfab6SVincent Franchomme             TRACE("SL_IGNORE_READONLY_ATTRIBUTE\n");
4910174dfab6SVincent Franchomme             flags &= ~SL_IGNORE_READONLY_ATTRIBUTE;
4911174dfab6SVincent Franchomme         }
4912174dfab6SVincent Franchomme 
4913c2c66affSColin Finck         if (flags)
4914c2c66affSColin Finck             WARN("unknown flags: %x\n", flags);
4915c2c66affSColin Finck     } else {
4916c2c66affSColin Finck         TRACE("flags: (none)\n");
4917c2c66affSColin Finck     }
4918c2c66affSColin Finck 
4919c2c66affSColin Finck     if (!IrpSp->FileObject) {
4920c2c66affSColin Finck         ERR("FileObject was NULL\n");
4921c2c66affSColin Finck         Status = STATUS_INVALID_PARAMETER;
4922c2c66affSColin Finck         goto exit;
4923c2c66affSColin Finck     }
4924c2c66affSColin Finck 
4925c2c66affSColin Finck     if (IrpSp->FileObject->RelatedFileObject) {
4926c2c66affSColin Finck         fcb* relatedfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
4927c2c66affSColin Finck 
4928c2c66affSColin Finck         if (relatedfcb && relatedfcb->Vcb != Vcb) {
4929c2c66affSColin Finck             WARN("RelatedFileObject was for different device\n");
4930c2c66affSColin Finck             Status = STATUS_INVALID_PARAMETER;
4931c2c66affSColin Finck             goto exit;
4932c2c66affSColin Finck         }
4933c2c66affSColin Finck     }
4934c2c66affSColin Finck 
4935c2c66affSColin Finck     // opening volume
4936c2c66affSColin Finck     if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
4937c2c66affSColin Finck         ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
4938c2c66affSColin Finck         ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
4939c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
4940c2c66affSColin Finck         LONG rc;
4941c2c66affSColin Finck #endif
4942c2c66affSColin Finck         ccb* ccb;
4943c2c66affSColin Finck 
4944c2c66affSColin Finck         TRACE("open operation for volume\n");
4945c2c66affSColin Finck 
4946c2c66affSColin Finck         if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) {
4947c2c66affSColin Finck             Status = STATUS_ACCESS_DENIED;
4948c2c66affSColin Finck             goto exit;
4949c2c66affSColin Finck         }
4950c2c66affSColin Finck 
4951c2c66affSColin Finck         if (RequestedOptions & FILE_DIRECTORY_FILE) {
4952c2c66affSColin Finck             Status = STATUS_NOT_A_DIRECTORY;
4953c2c66affSColin Finck             goto exit;
4954c2c66affSColin Finck         }
4955c2c66affSColin Finck 
4956c2c66affSColin Finck         ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
4957c2c66affSColin Finck         if (!ccb) {
4958c2c66affSColin Finck             ERR("out of memory\n");
4959c2c66affSColin Finck             Status = STATUS_INSUFFICIENT_RESOURCES;
4960c2c66affSColin Finck             goto exit;
4961c2c66affSColin Finck         }
4962c2c66affSColin Finck 
4963c2c66affSColin Finck         RtlZeroMemory(ccb, sizeof(*ccb));
4964c2c66affSColin Finck 
4965c2c66affSColin Finck         ccb->NodeType = BTRFS_NODE_TYPE_CCB;
4966c2c66affSColin Finck         ccb->NodeSize = sizeof(*ccb);
4967c2c66affSColin Finck         ccb->disposition = RequestedDisposition;
4968c2c66affSColin Finck         ccb->options = RequestedOptions;
4969c2c66affSColin Finck         ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
4970c2c66affSColin Finck         ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState,
4971c2c66affSColin Finck                                                                    IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode);
4972318da0c1SPierre Schweitzer         ccb->reserving = false;
4973c2c66affSColin Finck         ccb->lxss = called_from_lxss();
4974c2c66affSColin Finck 
4975c2c66affSColin Finck #ifdef DEBUG_FCB_REFCOUNTS
4976c2c66affSColin Finck         rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
4977c2c66affSColin Finck         WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);
4978c2c66affSColin Finck #else
4979c2c66affSColin Finck         InterlockedIncrement(&Vcb->volume_fcb->refcount);
4980c2c66affSColin Finck #endif
4981c2c66affSColin Finck         IrpSp->FileObject->FsContext = Vcb->volume_fcb;
4982c2c66affSColin Finck         IrpSp->FileObject->FsContext2 = ccb;
4983c2c66affSColin Finck 
4984c2c66affSColin Finck         IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
4985c2c66affSColin Finck 
4986c2c66affSColin Finck         if (!IrpSp->FileObject->Vpb)
4987c2c66affSColin Finck             IrpSp->FileObject->Vpb = DeviceObject->Vpb;
4988c2c66affSColin Finck 
4989c2c66affSColin Finck         InterlockedIncrement(&Vcb->open_files);
4990c2c66affSColin Finck 
4991c2c66affSColin Finck         Irp->IoStatus.Information = FILE_OPENED;
4992c2c66affSColin Finck         Status = STATUS_SUCCESS;
4993c2c66affSColin Finck     } else {
4994c2c66affSColin Finck         LIST_ENTRY rollback;
4995318da0c1SPierre Schweitzer         bool skip_lock;
4996c2c66affSColin Finck 
4997c2c66affSColin Finck         InitializeListHead(&rollback);
4998c2c66affSColin Finck 
4999194ea909SVictor Perevertkin         TRACE("file name: %.*S\n", (int)(IrpSp->FileObject->FileName.Length / sizeof(WCHAR)), IrpSp->FileObject->FileName.Buffer);
5000c2c66affSColin Finck 
5001c2c66affSColin Finck         if (IrpSp->FileObject->RelatedFileObject)
500262e630deSPierre Schweitzer             TRACE("related file = %p\n", IrpSp->FileObject->RelatedFileObject);
5003c2c66affSColin Finck 
5004c2c66affSColin Finck         // Don't lock again if we're being called from within CcCopyRead etc.
5005c2c66affSColin Finck         skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
5006c2c66affSColin Finck 
5007c2c66affSColin Finck         if (!skip_lock)
5008318da0c1SPierre Schweitzer             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5009c2c66affSColin Finck 
5010318da0c1SPierre Schweitzer         ExAcquireResourceSharedLite(&Vcb->fileref_lock, true);
5011883b1f31SPierre Schweitzer 
50126e0cf03dSVincent Franchomme         Status = open_file(DeviceObject, Vcb, Irp, &rollback, &opctx);
5013c2c66affSColin Finck 
5014c2c66affSColin Finck         if (!NT_SUCCESS(Status))
5015c2c66affSColin Finck             do_rollback(Vcb, &rollback);
5016c2c66affSColin Finck         else
5017c2c66affSColin Finck             clear_rollback(&rollback);
5018c2c66affSColin Finck 
5019883b1f31SPierre Schweitzer         ExReleaseResourceLite(&Vcb->fileref_lock);
5020883b1f31SPierre Schweitzer 
5021c2c66affSColin Finck         if (!skip_lock)
5022c2c66affSColin Finck             ExReleaseResourceLite(&Vcb->tree_lock);
5023c2c66affSColin Finck     }
5024c2c66affSColin Finck 
5025c2c66affSColin Finck exit:
50266e0cf03dSVincent Franchomme     if (Status != STATUS_PENDING) {
5027c2c66affSColin Finck         Irp->IoStatus.Status = Status;
5028c2c66affSColin Finck         IoCompleteRequest(Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
50296e0cf03dSVincent Franchomme     }
5030c2c66affSColin Finck 
5031c2c66affSColin Finck     if (locked)
5032c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->load_lock);
5033c2c66affSColin Finck 
50346e0cf03dSVincent Franchomme     if (Status == STATUS_PENDING) {
50356e0cf03dSVincent Franchomme         KeWaitForSingleObject(&opctx->event, Executive, KernelMode, false, NULL);
50366e0cf03dSVincent Franchomme         Status = opctx->Status;
50376e0cf03dSVincent Franchomme         ExFreePool(opctx);
50386e0cf03dSVincent Franchomme     }
50396e0cf03dSVincent Franchomme 
50406e0cf03dSVincent Franchomme     TRACE("create returning %08lx\n", Status);
50416e0cf03dSVincent Franchomme 
5042c2c66affSColin Finck     if (top_level)
5043c2c66affSColin Finck         IoSetTopLevelIrp(NULL);
5044c2c66affSColin Finck 
5045c2c66affSColin Finck     FsRtlExitFileSystem();
5046c2c66affSColin Finck 
5047c2c66affSColin Finck     return Status;
5048c2c66affSColin Finck }
5049