[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / create.c
1 /* Copyright (c) Mark Harmstone 2016
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <sys/stat.h>
19 #include "btrfs_drv.h"
20
21 extern PDEVICE_OBJECT devobj;
22
23 BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r,
24 UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
25 DIR_ITEM* di;
26 KEY searchkey;
27 traverse_ptr tp, tp2, next_tp;
28 BOOL b;
29 NTSTATUS Status;
30 ULONG stringlen;
31
32 TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type);
33
34 searchkey.obj_id = parinode;
35 searchkey.obj_type = TYPE_DIR_ITEM;
36 searchkey.offset = crc32;
37
38 Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
39 if (!NT_SUCCESS(Status)) {
40 ERR("error - find_item returned %08x\n", Status);
41 return FALSE;
42 }
43
44 TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
45
46 if (!keycmp(&searchkey, &tp.item->key)) {
47 UINT32 size = tp.item->size;
48
49 // found by hash
50
51 if (tp.item->size < sizeof(DIR_ITEM)) {
52 WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", r->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
53 } else {
54 di = (DIR_ITEM*)tp.item->data;
55
56 while (size > 0) {
57 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
58 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
59 break;
60 }
61
62 size -= sizeof(DIR_ITEM) - sizeof(char);
63 size -= di->n;
64 size -= di->m;
65
66 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
67 if (!NT_SUCCESS(Status)) {
68 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
69 } else {
70 WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
71 UNICODE_STRING us;
72
73 if (!utf16) {
74 ERR("out of memory\n");
75 free_traverse_ptr(&tp);
76 return FALSE;
77 }
78
79 Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
80
81 if (!NT_SUCCESS(Status)) {
82 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
83 } else {
84 us.Buffer = utf16;
85 us.Length = us.MaximumLength = (USHORT)stringlen;
86
87 if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
88 if (di->key.obj_type == TYPE_ROOT_ITEM) {
89 root* fcbroot = Vcb->roots;
90 while (fcbroot && fcbroot->id != di->key.obj_id)
91 fcbroot = fcbroot->next;
92
93 *subvol = fcbroot;
94 *inode = SUBVOL_ROOT_INODE;
95 *type = BTRFS_TYPE_DIRECTORY;
96 } else {
97 *subvol = r;
98 *inode = di->key.obj_id;
99 *type = di->type;
100 }
101
102 if (utf8) {
103 utf8->MaximumLength = di->n;
104 utf8->Length = utf8->MaximumLength;
105 utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
106 if (!utf8->Buffer) {
107 ERR("out of memory\n");
108 free_traverse_ptr(&tp);
109 ExFreePool(utf16);
110 return FALSE;
111 }
112
113 RtlCopyMemory(utf8->Buffer, di->name, di->n);
114 }
115
116 free_traverse_ptr(&tp);
117 ExFreePool(utf16);
118
119 TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
120
121 return TRUE;
122 }
123 }
124
125 ExFreePool(utf16);
126 }
127
128 di = (DIR_ITEM*)&di->name[di->n + di->m];
129 }
130 }
131 }
132
133 searchkey.obj_id = parinode;
134 searchkey.obj_type = TYPE_DIR_INDEX;
135 searchkey.offset = 2;
136
137 Status = find_item(Vcb, r, &tp2, &searchkey, FALSE);
138 if (!NT_SUCCESS(Status)) {
139 ERR("error - find_item returned %08x\n", Status);
140 free_traverse_ptr(&tp);
141 return FALSE;
142 }
143
144 free_traverse_ptr(&tp);
145 tp = tp2;
146
147 TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
148
149 if (keycmp(&tp.item->key, &searchkey) == -1) {
150 if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
151 free_traverse_ptr(&tp);
152 tp = next_tp;
153
154 TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
155 }
156 }
157
158 if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
159 free_traverse_ptr(&tp);
160 return FALSE;
161 }
162
163 b = TRUE;
164 do {
165 TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
166 di = (DIR_ITEM*)tp.item->data;
167
168 if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
169 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
170 } else {
171 TRACE("%.*s\n", di->n, di->name);
172
173 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
174 if (!NT_SUCCESS(Status)) {
175 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
176 } else {
177 WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
178 UNICODE_STRING us;
179
180 if (!utf16) {
181 ERR("out of memory\n");
182
183 free_traverse_ptr(&tp);
184 return FALSE;
185 }
186
187 Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
188
189 if (!NT_SUCCESS(Status)) {
190 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
191 } else {
192 us.Buffer = utf16;
193 us.Length = us.MaximumLength = (USHORT)stringlen;
194
195 if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
196 if (di->key.obj_type == TYPE_ROOT_ITEM) {
197 root* fcbroot = Vcb->roots;
198 while (fcbroot && fcbroot->id != di->key.obj_id)
199 fcbroot = fcbroot->next;
200
201 *subvol = fcbroot;
202 *inode = SUBVOL_ROOT_INODE;
203 *type = BTRFS_TYPE_DIRECTORY;
204 } else {
205 *subvol = r;
206 *inode = di->key.obj_id;
207 *type = di->type;
208 }
209 TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
210
211 if (utf8) {
212 utf8->MaximumLength = di->n;
213 utf8->Length = utf8->MaximumLength;
214 utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
215 if (!utf8->Buffer) {
216 ERR("out of memory\n");
217 free_traverse_ptr(&tp);
218 ExFreePool(utf16);
219
220 return FALSE;
221 }
222
223 RtlCopyMemory(utf8->Buffer, di->name, di->n);
224 }
225
226 free_traverse_ptr(&tp);
227 ExFreePool(utf16);
228
229 return TRUE;
230 }
231 }
232
233 ExFreePool(utf16);
234 }
235 }
236
237 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
238
239 if (b) {
240 free_traverse_ptr(&tp);
241 tp = next_tp;
242
243 b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
244 }
245 } while (b);
246
247 free_traverse_ptr(&tp);
248
249 return FALSE;
250 }
251
252 fcb* create_fcb() {
253 fcb* fcb;
254
255 fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
256 if (!fcb) {
257 ERR("out of memory\n");
258 return NULL;
259 }
260
261 #ifdef DEBUG_FCB_REFCOUNTS
262 WARN("allocating fcb %p\n", fcb);
263 #endif
264 RtlZeroMemory(fcb, sizeof(struct _fcb));
265
266 fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
267 fcb->Header.NodeByteSize = sizeof(struct _fcb);
268
269 fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
270 if (!fcb->nonpaged) {
271 ERR("out of memory\n");
272 ExFreePool(fcb);
273 return NULL;
274 }
275 RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
276
277 ExInitializeResourceLite(&fcb->nonpaged->paging_resource);
278 fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
279
280 ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
281 FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
282
283 fcb->refcount = 1;
284 #ifdef DEBUG_FCB_REFCOUNTS
285 WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
286 #endif
287
288 ExInitializeResourceLite(&fcb->nonpaged->resource);
289 fcb->Header.Resource = &fcb->nonpaged->resource;
290
291 FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
292
293 InitializeListHead(&fcb->children);
294
295 return fcb;
296 }
297
298 static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
299 UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
300 char* fn;
301 UINT32 crc32;
302 BOOL ret;
303 ULONG utf8len;
304 NTSTATUS Status;
305
306 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
307 if (!NT_SUCCESS(Status)) {
308 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
309 return FALSE;
310 }
311
312 fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
313 if (!fn) {
314 ERR("out of memory\n");
315 return FALSE;
316 }
317
318 Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
319 if (!NT_SUCCESS(Status)) {
320 ExFreePool(fn);
321 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
322 return FALSE;
323 }
324
325 TRACE("%.*s\n", utf8len, fn);
326
327 crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
328 TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
329
330 ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8);
331
332 return ret;
333 }
334
335 static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, UINT32* hash, PANSI_STRING xattr) {
336 NTSTATUS Status;
337 ULONG utf8len;
338 char* utf8;
339 UINT32 crc32;
340 KEY searchkey;
341 traverse_ptr tp, next_tp;
342 BOOL success = FALSE, b;
343
344 static char xapref[] = "user.";
345 ULONG xapreflen = strlen(xapref);
346
347 TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
348
349 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
350 if (!NT_SUCCESS(Status)) {
351 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
352 return FALSE;
353 }
354
355 TRACE("utf8len = %u\n", utf8len);
356
357 utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
358 if (!utf8) {
359 ERR("out of memory\n");
360 goto end;
361 }
362
363 RtlCopyMemory(utf8, xapref, xapreflen);
364
365 Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
366 if (!NT_SUCCESS(Status)) {
367 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
368 goto end;
369 }
370
371 utf8len += xapreflen;
372 utf8[utf8len] = 0;
373
374 TRACE("utf8 = %s\n", utf8);
375
376 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
377 TRACE("crc32 = %08x\n", crc32);
378
379 searchkey.obj_id = fcb->inode;
380 searchkey.obj_type = TYPE_XATTR_ITEM;
381 searchkey.offset = crc32;
382
383 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
384 if (!NT_SUCCESS(Status)) {
385 ERR("error - find_item returned %08x\n", Status);
386 goto end;
387 }
388
389 if (!keycmp(&tp.item->key, &searchkey)) {
390 if (tp.item->size < sizeof(DIR_ITEM)) {
391 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
392 } else {
393 ULONG len = tp.item->size, xasize;
394 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
395
396 TRACE("found match on hash\n");
397
398 while (len > 0) {
399 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
400 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
401 break;
402 }
403
404 if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
405 TRACE("found exact match for %s\n", utf8);
406
407 *size = di->m;
408 *hash = tp.item->key.offset;
409
410 xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
411 if (!xattr->Buffer) {
412 ERR("out of memory\n");
413 free_traverse_ptr(&tp);
414 goto end;
415 }
416
417 xattr->Length = xattr->MaximumLength = di->n;
418 RtlCopyMemory(xattr->Buffer, di->name, di->n);
419 xattr->Buffer[di->n] = 0;
420
421 free_traverse_ptr(&tp);
422
423 success = TRUE;
424 goto end;
425 }
426
427 xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
428
429 if (len > xasize) {
430 len -= xasize;
431 di = (DIR_ITEM*)&di->name[di->m + di->n];
432 } else
433 break;
434 }
435 }
436 }
437
438 free_traverse_ptr(&tp);
439
440 searchkey.offset = 0;
441
442 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
443 if (!NT_SUCCESS(Status)) {
444 ERR("error - find_item returned %08x\n", Status);
445 goto end;
446 }
447
448 do {
449 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
450 if (tp.item->size < sizeof(DIR_ITEM)) {
451 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
452 } else {
453 ULONG len = tp.item->size, xasize;
454 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
455 ULONG utf16len;
456
457 TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
458
459 while (len > 0) {
460 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
461 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
462 break;
463 }
464
465 if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
466 TRACE("found potential xattr %.*s\n", di->n, di->name);
467 }
468
469 Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
470 if (!NT_SUCCESS(Status)) {
471 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
472 } else {
473 WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
474 if (!utf16) {
475 ERR("out of memory\n");
476 free_traverse_ptr(&tp);
477 goto end;
478 }
479
480 Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
481
482 if (!NT_SUCCESS(Status)) {
483 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
484 } else {
485 UNICODE_STRING us;
486
487 us.Buffer = utf16;
488 us.Length = us.MaximumLength = (USHORT)utf16len;
489
490 if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
491 TRACE("found case-insensitive match for %s\n", utf8);
492
493 *newstreamname = us;
494 *size = di->m;
495 *hash = tp.item->key.offset;
496
497 xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
498 if (!xattr->Buffer) {
499 ERR("out of memory\n");
500 free_traverse_ptr(&tp);
501 ExFreePool(utf16);
502 goto end;
503 }
504
505 xattr->Length = xattr->MaximumLength = di->n;
506 RtlCopyMemory(xattr->Buffer, di->name, di->n);
507 xattr->Buffer[di->n] = 0;
508
509 free_traverse_ptr(&tp);
510
511 success = TRUE;
512 goto end;
513 }
514 }
515
516 ExFreePool(utf16);
517 }
518
519 xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
520
521 if (len > xasize) {
522 len -= xasize;
523 di = (DIR_ITEM*)&di->name[di->m + di->n];
524 } else
525 break;
526 }
527 }
528 }
529
530 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
531 if (b) {
532 free_traverse_ptr(&tp);
533 tp = next_tp;
534
535 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
536 break;
537 }
538 } while (b);
539
540 free_traverse_ptr(&tp);
541
542 end:
543 ExFreePool(utf8);
544
545 return success;
546 }
547
548 static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
549 ULONG len, i, j, np;
550 BOOL has_stream;
551 UNICODE_STRING* ps;
552 WCHAR* buf;
553
554 np = 1;
555
556 len = path->Length / sizeof(WCHAR);
557 if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
558 len--;
559
560 has_stream = FALSE;
561 for (i = 0; i < len; i++) {
562 if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
563 np++;
564 has_stream = FALSE;
565 } else if (path->Buffer[i] == ':') {
566 has_stream = TRUE;
567 }
568 }
569
570 if (has_stream)
571 np++;
572
573 ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
574 if (!ps) {
575 ERR("out of memory\n");
576 return STATUS_INSUFFICIENT_RESOURCES;
577 }
578
579 RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
580
581 buf = path->Buffer;
582
583 j = 0;
584 for (i = 0; i < len; i++) {
585 if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
586 ps[j].Buffer = buf;
587 ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
588 ps[j].MaximumLength = ps[j].Length;
589
590 buf = &path->Buffer[i+1];
591 j++;
592 }
593 }
594
595 ps[j].Buffer = buf;
596 ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
597 ps[j].MaximumLength = ps[j].Length;
598
599 if (has_stream) {
600 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
601 UNICODE_STRING dsus;
602
603 dsus.Buffer = datasuf;
604 dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
605
606 for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
607 if (ps[j].Buffer[i] == ':') {
608 ps[j+1].Buffer = &ps[j].Buffer[i+1];
609 ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
610
611 ps[j].Length = i * sizeof(WCHAR);
612 ps[j].MaximumLength = ps[j].Length;
613
614 j++;
615
616 break;
617 }
618 }
619
620 // FIXME - should comparison be case-insensitive?
621 // remove :$DATA suffix
622 if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
623 ps[j].Length -= dsus.Length;
624
625 if (ps[j].Length == 0) {
626 np--;
627 has_stream = FALSE;
628 }
629 }
630
631 // if path is just stream name, remove first empty item
632 if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
633 ps[0] = ps[1];
634 np--;
635 }
636
637 // for (i = 0; i < np; i++) {
638 // ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
639 // }
640
641 *num_parts = np;
642 *parts = ps;
643 *stream = has_stream;
644
645 return STATUS_SUCCESS;
646 }
647
648 static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
649 LIST_ENTRY* le;
650 fcb *c, *deleted = NULL;
651
652 le = dir->children.Flink;
653 while (le != &dir->children) {
654 c = CONTAINING_RECORD(le, fcb, list_entry);
655
656 if (c->refcount > 0 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
657 if (c->deleted) {
658 deleted = c;
659 } else {
660 c->refcount++;
661 #ifdef DEBUG_FCB_REFCOUNTS
662 WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
663 #endif
664 return c;
665 }
666 }
667
668 le = le->Flink;
669 }
670
671 return deleted;
672 }
673
674 #ifdef DEBUG_FCB_REFCOUNTS
675 static void print_fcbs(device_extension* Vcb) {
676 fcb* fcb = Vcb->fcbs;
677
678 while (fcb) {
679 ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
680
681 fcb = fcb->next;
682 }
683 }
684 #endif
685
686 NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
687 fcb *dir, *sf, *sf2;
688 ULONG i, num_parts;
689 UNICODE_STRING fnus2;
690 UNICODE_STRING* parts = NULL;
691 BOOL has_stream;
692 NTSTATUS Status;
693
694 TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
695
696 #ifdef DEBUG_FCB_REFCOUNTS
697 print_fcbs(Vcb);
698 #endif
699
700 fnus2 = *fnus;
701
702 if (fnus2.Length < sizeof(WCHAR) && !relatedfcb) {
703 ERR("error - fnus was too short\n");
704 return STATUS_INTERNAL_ERROR;
705 }
706
707 if (relatedfcb) {
708 dir = relatedfcb;
709 } else {
710 if (fnus2.Buffer[0] != '\\') {
711 ERR("error - filename %.*S did not begin with \\\n", fnus2.Length / sizeof(WCHAR), fnus2.Buffer);
712 return STATUS_OBJECT_PATH_NOT_FOUND;
713 }
714
715 if (fnus2.Length == sizeof(WCHAR)) {
716 *pfcb = Vcb->root_fcb;
717 Vcb->root_fcb->refcount++;
718 #ifdef DEBUG_FCB_REFCOUNTS
719 WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, Vcb->root_fcb->refcount);
720 #endif
721 return STATUS_SUCCESS;
722 }
723
724 dir = Vcb->root_fcb;
725
726 fnus2.Buffer++;
727 fnus2.Length -= sizeof(WCHAR);
728 fnus2.MaximumLength -= sizeof(WCHAR);
729 }
730
731 if (dir->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
732 WARN("passed relatedfcb which isn't a directory (%.*S) (fnus = %.*S)\n",
733 relatedfcb->full_filename.Length / sizeof(WCHAR), relatedfcb->full_filename.Buffer, fnus->Length / sizeof(WCHAR), fnus->Buffer);
734 return STATUS_OBJECT_PATH_NOT_FOUND;
735 }
736
737 if (fnus->Length == 0) {
738 num_parts = 0;
739 } else {
740 Status = split_path(&fnus2, &parts, &num_parts, &has_stream);
741 if (!NT_SUCCESS(Status)) {
742 ERR("split_path returned %08x\n", Status);
743 return Status;
744 }
745 }
746
747 // FIXME - handle refcounts(?)
748 sf = dir;
749 dir->refcount++;
750 #ifdef DEBUG_FCB_REFCOUNTS
751 WARN("fcb %p: refcount now %i (%.*S)\n", dir, dir->refcount, dir->full_filename.Length / sizeof(WCHAR), dir->full_filename.Buffer);
752 #endif
753
754 if (parent) {
755 num_parts--;
756
757 if (has_stream) {
758 num_parts--;
759 has_stream = FALSE;
760 }
761 }
762
763 if (num_parts == 0) {
764 Status = STATUS_SUCCESS;
765 *pfcb = dir;
766 goto end2;
767 }
768
769 for (i = 0; i < num_parts; i++) {
770 BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
771
772 sf2 = search_fcb_children(sf, &parts[i]);
773
774 if (sf2 && sf2->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
775 WARN("passed path including file as subdirectory\n");
776
777 Status = STATUS_OBJECT_PATH_NOT_FOUND;
778 goto end;
779 }
780
781 if (!sf2) {
782 if (has_stream && i == num_parts - 1) {
783 UNICODE_STRING streamname;
784 ANSI_STRING xattr;
785 UINT32 streamsize, streamhash;
786
787 streamname.Buffer = NULL;
788 streamname.Length = streamname.MaximumLength = 0;
789 xattr.Buffer = NULL;
790 xattr.Length = xattr.MaximumLength = 0;
791
792 if (!find_stream(Vcb, sf, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
793 TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
794
795 Status = STATUS_OBJECT_NAME_NOT_FOUND;
796 goto end;
797 } else {
798 ULONG fnlen;
799
800 if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
801 RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
802 WARN("not allowing user.DOSATTRIB to be opened as stream\n");
803
804 Status = STATUS_OBJECT_NAME_NOT_FOUND;
805 goto end;
806 }
807
808 sf2 = create_fcb();
809 if (!sf2) {
810 ERR("out of memory\n");
811 Status = STATUS_INSUFFICIENT_RESOURCES;
812 goto end;
813 }
814
815 sf2->Vcb = Vcb;
816
817 if (streamname.Buffer) // case has changed
818 sf2->filepart = streamname;
819 else {
820 sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
821 sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
822 if (!sf2->filepart.Buffer) {
823 ERR("out of memory\n");
824 free_fcb(sf2);
825 Status = STATUS_INSUFFICIENT_RESOURCES;
826 goto end;
827 }
828
829 RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
830 }
831
832 sf2->par = sf;
833
834 sf->refcount++;
835 #ifdef DEBUG_FCB_REFCOUNTS
836 WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
837 #endif
838
839 sf2->subvol = sf->subvol;
840 sf2->inode = sf->inode;
841 sf2->type = sf->type;
842 sf2->ads = TRUE;
843 sf2->adssize = streamsize;
844 sf2->adshash = streamhash;
845 sf2->adsxattr = xattr;
846
847 TRACE("stream found: size = %x, hash = %08x\n", sf2->adssize, sf2->adshash);
848
849 if (Vcb->fcbs)
850 Vcb->fcbs->prev = sf2;
851
852 sf2->next = Vcb->fcbs;
853 Vcb->fcbs = sf2;
854
855 sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
856
857 if (sf != Vcb->root_fcb)
858 sf2->name_offset++;
859
860 fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
861
862 sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
863 if (!sf2->full_filename.Buffer) {
864 ERR("out of memory\n");
865 free_fcb(sf2);
866 Status = STATUS_INSUFFICIENT_RESOURCES;
867 goto end;
868 }
869
870 sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
871 RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
872
873 sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':';
874
875 RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
876
877 // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
878
879 TRACE("found stream %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, sf->subvol);
880
881 InsertTailList(&sf->children, &sf2->list_entry);
882 }
883 } else {
884 root* subvol;
885 UINT64 inode;
886 UINT8 type;
887 ANSI_STRING utf8;
888 KEY searchkey;
889 traverse_ptr tp;
890
891 if (!find_file_in_dir(Vcb, &parts[i], sf->subvol, sf->inode, &subvol, &inode, &type, &utf8)) {
892 TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
893
894 Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
895 goto end;
896 } else if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
897 WARN("passed path including file as subdirectory\n");
898
899 Status = STATUS_OBJECT_PATH_NOT_FOUND;
900 goto end;
901 } else {
902 ULONG fnlen, strlen;
903
904 sf2 = create_fcb();
905 if (!sf2) {
906 ERR("out of memory\n");
907 Status = STATUS_INSUFFICIENT_RESOURCES;
908 goto end;
909 }
910
911 sf2->Vcb = Vcb;
912
913 Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
914 if (!NT_SUCCESS(Status)) {
915 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
916 free_fcb(sf2);
917 goto end;
918 } else {
919 sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
920 sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
921 if (!sf2->filepart.Buffer) {
922 ERR("out of memory\n");
923 free_fcb(sf2);
924 Status = STATUS_INSUFFICIENT_RESOURCES;
925 goto end;
926 }
927
928 Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
929
930 if (!NT_SUCCESS(Status)) {
931 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
932 free_fcb(sf2);
933 goto end;
934 }
935 }
936
937 sf2->par = sf;
938
939 sf->refcount++;
940 #ifdef DEBUG_FCB_REFCOUNTS
941 WARN("fcb %p: refcount now %i (%.*S)\n", sf, sf->refcount, sf->full_filename.Length / sizeof(WCHAR), sf->full_filename.Buffer);
942 #endif
943
944 sf2->subvol = subvol;
945 sf2->inode = inode;
946 sf2->type = type;
947
948 if (Vcb->fcbs)
949 Vcb->fcbs->prev = sf2;
950
951 sf2->next = Vcb->fcbs;
952 Vcb->fcbs = sf2;
953
954 sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
955
956 if (sf != Vcb->root_fcb)
957 sf2->name_offset++;
958
959 fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
960
961 sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
962 if (!sf2->full_filename.Buffer) {
963 ERR("out of memory\n");
964 free_fcb(sf2);
965 Status = STATUS_INSUFFICIENT_RESOURCES;
966 goto end;
967 }
968
969 sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
970 RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
971
972 if (sf != Vcb->root_fcb)
973 sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
974
975 RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
976
977 sf2->utf8 = utf8;
978
979 searchkey.obj_id = sf2->inode;
980 searchkey.obj_type = TYPE_INODE_ITEM;
981 searchkey.offset = 0xffffffffffffffff;
982
983 Status = find_item(sf2->Vcb, sf2->subvol, &tp, &searchkey, FALSE);
984 if (!NT_SUCCESS(Status)) {
985 ERR("error - find_item returned %08x\n", Status);
986 free_fcb(sf2);
987 goto end;
988 }
989
990 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
991 ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id);
992 Status = STATUS_INTERNAL_ERROR;
993 free_fcb(sf2);
994 free_traverse_ptr(&tp);
995 goto end;
996 }
997
998 if (tp.item->size > 0)
999 RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
1000
1001 free_traverse_ptr(&tp);
1002
1003 sf2->atts = get_file_attributes(Vcb, &sf2->inode_item, sf2->subvol, sf2->inode, sf2->type, sf2->filepart.Buffer[0] == '.', FALSE);
1004
1005 fcb_get_sd(sf2);
1006
1007 TRACE("found %.*S (subvol = %p)\n", sf2->full_filename.Length / sizeof(WCHAR), sf2->full_filename.Buffer, subvol);
1008
1009 InsertTailList(&sf->children, &sf2->list_entry);
1010 }
1011 }
1012 }
1013
1014 if (i == num_parts - 1)
1015 break;
1016
1017 free_fcb(sf);
1018 sf = sf2;
1019 }
1020
1021 Status = STATUS_SUCCESS;
1022 *pfcb = sf2;
1023
1024 end:
1025 free_fcb(sf);
1026
1027 end2:
1028 if (parts)
1029 ExFreePool(parts);
1030
1031 #ifdef DEBUG_FCB_REFCOUNTS
1032 print_fcbs(Vcb);
1033 #endif
1034
1035 TRACE("returning %08x\n", Status);
1036
1037 return Status;
1038 }
1039
1040 static NTSTATUS STDCALL attach_fcb_to_fileobject(device_extension* Vcb, fcb* fcb, PFILE_OBJECT FileObject) {
1041 FileObject->FsContext = fcb;
1042 // FileObject->FsContext2 = 0x0badc0de;//NULL;
1043
1044 // FIXME - cache stuff
1045
1046 return STATUS_SUCCESS;
1047 }
1048
1049 static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, fcb* parfcb, ULONG options, fcb** pfcb, LIST_ENTRY* rollback) {
1050 NTSTATUS Status;
1051 fcb* fcb;
1052 ULONG utf8len;
1053 char* utf8 = NULL;
1054 UINT32 crc32;
1055 UINT64 dirpos, inode;
1056 KEY searchkey;
1057 traverse_ptr tp;
1058 INODE_ITEM *dirii, *ii;
1059 UINT8 type;
1060 ULONG disize;
1061 DIR_ITEM *di, *di2;
1062 LARGE_INTEGER time;
1063 BTRFS_TIME now;
1064 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1065 ANSI_STRING utf8as;
1066 ULONG defda;
1067
1068 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
1069 if (!NT_SUCCESS(Status))
1070 return Status;
1071
1072 utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1073 if (!utf8) {
1074 ERR("out of memory\n");
1075 return STATUS_INSUFFICIENT_RESOURCES;
1076 }
1077
1078 Status = RtlUnicodeToUTF8N(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
1079 if (!NT_SUCCESS(Status)) {
1080 ExFreePool(utf8);
1081 return Status;
1082 }
1083
1084 utf8[utf8len] = 0;
1085
1086 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
1087
1088 dirpos = find_next_dir_index(Vcb, parfcb->subvol, parfcb->inode);
1089 if (dirpos == 0) {
1090 Status = STATUS_INTERNAL_ERROR;
1091 ExFreePool(utf8);
1092 return Status;
1093 }
1094
1095 TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos);
1096
1097 KeQuerySystemTime(&time);
1098 win_time_to_unix(time, &now);
1099
1100 TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
1101 parfcb->inode_item.st_size += utf8len * 2;
1102 TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
1103 parfcb->inode_item.transid = Vcb->superblock.generation;
1104 parfcb->inode_item.sequence++;
1105 parfcb->inode_item.st_ctime = now;
1106 parfcb->inode_item.st_mtime = now;
1107
1108 searchkey.obj_id = parfcb->inode;
1109 searchkey.obj_type = TYPE_INODE_ITEM;
1110 searchkey.offset = 0;
1111
1112 Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
1113 if (!NT_SUCCESS(Status)) {
1114 ERR("error - find_item returned %08x\n", Status);
1115 ExFreePool(utf8);
1116 return Status;
1117 }
1118
1119 if (keycmp(&searchkey, &tp.item->key)) {
1120 ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfcb->inode, parfcb->subvol->id);
1121 free_traverse_ptr(&tp);
1122 ExFreePool(utf8);
1123 return STATUS_INTERNAL_ERROR;
1124 }
1125
1126 dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1127 if (!dirii) {
1128 ERR("out of memory\n");
1129 free_traverse_ptr(&tp);
1130 ExFreePool(utf8);
1131 return STATUS_INSUFFICIENT_RESOURCES;
1132 }
1133
1134 RtlCopyMemory(dirii, &parfcb->inode_item, sizeof(INODE_ITEM));
1135 delete_tree_item(Vcb, &tp, rollback);
1136
1137 insert_tree_item(Vcb, parfcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
1138
1139 free_traverse_ptr(&tp);
1140
1141 if (parfcb->subvol->lastinode == 0)
1142 get_last_inode(Vcb, parfcb->subvol);
1143
1144 inode = parfcb->subvol->lastinode + 1;
1145
1146 type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
1147
1148 disize = sizeof(DIR_ITEM) - 1 + utf8len;
1149 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
1150 if (!di) {
1151 ERR("out of memory\n");
1152 ExFreePool(utf8);
1153 return STATUS_INSUFFICIENT_RESOURCES;
1154 }
1155
1156 di->key.obj_id = inode;
1157 di->key.obj_type = TYPE_INODE_ITEM;
1158 di->key.offset = 0;
1159 di->transid = Vcb->superblock.generation;
1160 di->m = 0;
1161 di->n = (UINT16)utf8len;
1162 di->type = type;
1163 RtlCopyMemory(di->name, utf8, utf8len);
1164
1165 insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback);
1166
1167 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
1168 if (!di2) {
1169 ERR("out of memory\n");
1170 ExFreePool(utf8);
1171 return STATUS_INSUFFICIENT_RESOURCES;
1172 }
1173
1174 RtlCopyMemory(di2, di, disize);
1175
1176 Status = add_dir_item(Vcb, parfcb->subvol, parfcb->inode, crc32, di2, disize, rollback);
1177 if (!NT_SUCCESS(Status)) {
1178 ERR("add_dir_item returned %08x\n", Status);
1179 ExFreePool(utf8);
1180 return Status;
1181 }
1182
1183 // FIXME - handle Irp->Overlay.AllocationSize
1184
1185 utf8as.Buffer = utf8;
1186 utf8as.Length = utf8as.MaximumLength = utf8len;
1187
1188 Status = add_inode_ref(Vcb, parfcb->subvol, inode, parfcb->inode, dirpos, &utf8as, rollback);
1189 if (!NT_SUCCESS(Status)) {
1190 ERR("add_inode_ref returned %08x\n", Status);
1191 ExFreePool(utf8);
1192 return Status;
1193 }
1194
1195 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
1196
1197 TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
1198
1199 IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
1200
1201 defda = 0;
1202
1203 if (utf8[0] == '.')
1204 defda |= FILE_ATTRIBUTE_HIDDEN;
1205
1206 if (options & FILE_DIRECTORY_FILE) {
1207 defda |= FILE_ATTRIBUTE_DIRECTORY;
1208 IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1209 }
1210
1211 TRACE("defda = %x\n", defda);
1212
1213 if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
1214 IrpSp->Parameters.Create.FileAttributes = defda;
1215
1216 if (IrpSp->Parameters.Create.FileAttributes != defda) {
1217 char val[64];
1218
1219 sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
1220
1221 Status = set_xattr(Vcb, parfcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
1222 if (!NT_SUCCESS(Status)) {
1223 ERR("set_xattr returned %08x\n", Status);
1224 ExFreePool(utf8);
1225 return Status;
1226 }
1227 }
1228
1229 parfcb->subvol->lastinode++;
1230
1231 fcb = create_fcb();
1232 if (!fcb) {
1233 ERR("out of memory\n");
1234 ExFreePool(utf8);
1235 return STATUS_INSUFFICIENT_RESOURCES;
1236 }
1237
1238 fcb->Vcb = Vcb;
1239
1240 RtlZeroMemory(&fcb->inode_item, sizeof(INODE_ITEM));
1241 fcb->inode_item.generation = Vcb->superblock.generation;
1242 fcb->inode_item.transid = Vcb->superblock.generation;
1243 fcb->inode_item.st_size = 0;
1244 fcb->inode_item.st_blocks = 0;
1245 fcb->inode_item.block_group = 0;
1246 fcb->inode_item.st_nlink = 1;
1247 // fcb->inode_item.st_uid = UID_NOBODY; // FIXME?
1248 fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
1249 fcb->inode_item.st_mode = parfcb ? (parfcb->inode_item.st_mode & ~S_IFDIR) : 0755; // use parent's permissions by default
1250 fcb->inode_item.st_rdev = 0;
1251 fcb->inode_item.flags = 0;
1252 fcb->inode_item.sequence = 1;
1253 fcb->inode_item.st_atime = now;
1254 fcb->inode_item.st_ctime = now;
1255 fcb->inode_item.st_mtime = now;
1256 fcb->inode_item.otime = now;
1257
1258 if (type == BTRFS_TYPE_DIRECTORY)
1259 fcb->inode_item.st_mode |= S_IFDIR;
1260 else {
1261 fcb->inode_item.st_mode |= S_IFREG;
1262 fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
1263 }
1264
1265 // inherit nodatacow flag from parent directory
1266 if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
1267 fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
1268
1269 if (type != BTRFS_TYPE_DIRECTORY)
1270 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
1271 }
1272
1273 // fcb->Header.IsFastIoPossible = TRUE;
1274 fcb->Header.AllocationSize.QuadPart = 0;
1275 fcb->Header.FileSize.QuadPart = 0;
1276 fcb->Header.ValidDataLength.QuadPart = 0;
1277
1278 fcb->atts = IrpSp->Parameters.Create.FileAttributes;
1279
1280 if (options & FILE_DELETE_ON_CLOSE)
1281 fcb->delete_on_close = TRUE;
1282
1283 fcb->par = parfcb;
1284 parfcb->refcount++;
1285 #ifdef DEBUG_FCB_REFCOUNTS
1286 WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
1287 #endif
1288 fcb->subvol = parfcb->subvol;
1289 fcb->inode = inode;
1290 fcb->type = type;
1291
1292 fcb->utf8.MaximumLength = fcb->utf8.Length = utf8len;
1293 fcb->utf8.Buffer = utf8;
1294
1295 Status = fcb_get_new_sd(fcb, IrpSp->Parameters.Create.SecurityContext->AccessState);
1296
1297 if (!NT_SUCCESS(Status)) {
1298 ERR("fcb_get_new_sd returned %08x\n", Status);
1299 ExFreePool(utf8);
1300 return Status;
1301 }
1302
1303 fcb->filepart = *fpus;
1304
1305 Status = set_xattr(Vcb, parfcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
1306 if (!NT_SUCCESS(Status)) {
1307 ERR("set_xattr returned %08x\n", Status);
1308 ExFreePool(utf8);
1309 return Status;
1310 }
1311
1312 fcb->full_filename.Length = parfcb->full_filename.Length + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fcb->filepart.Length;
1313 fcb->full_filename.MaximumLength = fcb->full_filename.Length;
1314 fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.Length, ALLOC_TAG);
1315 if (!fcb->full_filename.Buffer) {
1316 ERR("out of memory\n");
1317 ExFreePool(utf8);
1318 return STATUS_INSUFFICIENT_RESOURCES;
1319 }
1320
1321 RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
1322
1323 if (parfcb->full_filename.Length > sizeof(WCHAR))
1324 fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = '\\';
1325
1326 RtlCopyMemory(&fcb->full_filename.Buffer[(parfcb->full_filename.Length / sizeof(WCHAR)) + (parfcb->full_filename.Length == sizeof(WCHAR) ? 0 : 1)], fcb->filepart.Buffer, fcb->filepart.Length);
1327
1328 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1329 if (!ii) {
1330 ERR("out of memory\n");
1331 ExFreePool(utf8);
1332 return STATUS_INSUFFICIENT_RESOURCES;
1333 }
1334
1335 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
1336 insert_tree_item(Vcb, parfcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
1337
1338 *pfcb = fcb;
1339
1340 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1341 fcb->subvol->root_item.ctime = now;
1342
1343 InsertTailList(&fcb->par->children, &fcb->list_entry);
1344
1345 TRACE("created new file %.*S in subvol %llx, inode %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->subvol->id, fcb->inode);
1346
1347 return STATUS_SUCCESS;
1348 }
1349
1350 static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
1351 NTSTATUS Status;
1352 fcb *fcb, *parfcb = NULL;
1353 ULONG i, j;
1354 ULONG utf8len;
1355 ccb* ccb;
1356 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
1357 UNICODE_STRING dsus, fpus, stream;
1358
1359 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
1360
1361 if (Vcb->readonly)
1362 return STATUS_MEDIA_WRITE_PROTECTED;
1363
1364 dsus.Buffer = datasuf;
1365 dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
1366 fpus.Buffer = NULL;
1367
1368 // FIXME - apparently you can open streams using RelatedFileObject. How can we test this?
1369
1370 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1371 Status = get_fcb(Vcb, &parfcb, fnus, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, TRUE);
1372 ExReleaseResourceLite(&Vcb->fcb_lock);
1373 if (!NT_SUCCESS(Status))
1374 goto end;
1375
1376 if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
1377 Status = STATUS_OBJECT_PATH_NOT_FOUND;
1378 goto end;
1379 }
1380
1381 if (parfcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
1382 Status = STATUS_ACCESS_DENIED;
1383 goto end;
1384 }
1385
1386 i = (fnus->Length / sizeof(WCHAR))-1;
1387 while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
1388
1389 j = i;
1390
1391 while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
1392
1393 fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
1394 fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
1395 if (!fpus.Buffer) {
1396 ERR("out of memory\n");
1397 Status = STATUS_INSUFFICIENT_RESOURCES;
1398 goto end;
1399 }
1400
1401 fpus.Length = (j - i + 1) * sizeof(WCHAR);
1402
1403 RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
1404 fpus.Buffer[j - i + 1] = 0;
1405
1406 if (fpus.Length > dsus.Length) { // check for :$DATA suffix
1407 UNICODE_STRING lb;
1408
1409 lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
1410 lb.Length = lb.MaximumLength = dsus.Length;
1411
1412 TRACE("lb = %.*S\n", lb.Length/sizeof(WCHAR), lb.Buffer);
1413
1414 if (FsRtlAreNamesEqual(&dsus, &lb, TRUE, NULL)) {
1415 TRACE("ignoring :$DATA suffix\n");
1416
1417 fpus.Length -= lb.Length;
1418
1419 if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
1420 fpus.Length -= sizeof(WCHAR);
1421
1422 TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
1423 }
1424 }
1425
1426 stream.Length = 0;
1427
1428 for (i = 0; i < fpus.Length/sizeof(WCHAR); i++) {
1429 if (fpus.Buffer[i] == ':') {
1430 stream.Length = fpus.Length - (i*sizeof(WCHAR)) - sizeof(WCHAR);
1431 stream.Buffer = &fpus.Buffer[i+1];
1432 fpus.Buffer[i] = 0;
1433 fpus.Length = i * sizeof(WCHAR);
1434 break;
1435 }
1436 }
1437
1438 if (stream.Length > 0) {
1439 struct _fcb* newpar;
1440 static char xapref[] = "user.";
1441 ULONG xapreflen = strlen(xapref), fnlen;
1442 LARGE_INTEGER time;
1443 BTRFS_TIME now;
1444 KEY searchkey;
1445 traverse_ptr tp;
1446 INODE_ITEM* ii;
1447
1448 TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
1449 TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
1450
1451 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1452 Status = get_fcb(Vcb, &newpar, &fpus, parfcb, FALSE);
1453 ExReleaseResourceLite(&Vcb->fcb_lock);
1454
1455 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
1456 Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &newpar, rollback);
1457
1458 if (!NT_SUCCESS(Status)) {
1459 ERR("file_create2 returned %08x\n", Status);
1460 goto end;
1461 }
1462 } else if (!NT_SUCCESS(Status)) {
1463 ERR("get_fcb returned %08x\n", Status);
1464 goto end;
1465 }
1466
1467 free_fcb(parfcb);
1468 parfcb = newpar;
1469
1470 if (newpar->type != BTRFS_TYPE_FILE && newpar->type != BTRFS_TYPE_SYMLINK) {
1471 WARN("parent not file or symlink\n");
1472 Status = STATUS_INVALID_PARAMETER;
1473 goto end;
1474 }
1475
1476 if (options & FILE_DIRECTORY_FILE) {
1477 WARN("tried to create directory as stream\n");
1478 Status = STATUS_INVALID_PARAMETER;
1479 goto end;
1480 }
1481
1482 fcb = create_fcb();
1483 if (!fcb) {
1484 ERR("out of memory\n");
1485 Status = STATUS_INSUFFICIENT_RESOURCES;
1486 goto end;
1487 }
1488
1489 fcb->Vcb = Vcb;
1490
1491 // fcb->Header.IsFastIoPossible = TRUE;
1492 fcb->Header.AllocationSize.QuadPart = 0;
1493 fcb->Header.FileSize.QuadPart = 0;
1494 fcb->Header.ValidDataLength.QuadPart = 0;
1495
1496 if (options & FILE_DELETE_ON_CLOSE)
1497 fcb->delete_on_close = TRUE;
1498
1499 fcb->par = parfcb;
1500 parfcb->refcount++;
1501 #ifdef DEBUG_FCB_REFCOUNTS
1502 WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
1503 #endif
1504 fcb->subvol = parfcb->subvol;
1505 fcb->inode = parfcb->inode;
1506 fcb->type = parfcb->type;
1507
1508 fcb->ads = TRUE;
1509 fcb->adssize = 0;
1510
1511 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
1512 if (!NT_SUCCESS(Status))
1513 goto end;
1514
1515 fcb->adsxattr.Length = utf8len + xapreflen;
1516 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
1517 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
1518 if (!fcb->adsxattr.Buffer) {
1519 ERR("out of memory\n");
1520 free_fcb(fcb);
1521 Status = STATUS_INSUFFICIENT_RESOURCES;
1522 goto end;
1523 }
1524
1525 RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
1526
1527 Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
1528 if (!NT_SUCCESS(Status)) {
1529 free_fcb(fcb);
1530 goto end;
1531 }
1532
1533 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
1534
1535 TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
1536
1537 fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
1538 TRACE("adshash = %08x\n", fcb->adshash);
1539
1540 fcb->name_offset = parfcb->full_filename.Length / sizeof(WCHAR);
1541 if (parfcb != Vcb->root_fcb)
1542 fcb->name_offset++;
1543
1544 fcb->filepart.MaximumLength = fcb->filepart.Length = stream.Length;
1545 fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.MaximumLength, ALLOC_TAG);
1546 if (!fcb->filepart.Buffer) {
1547 ERR("out of memory\n");
1548 free_fcb(fcb);
1549 Status = STATUS_INSUFFICIENT_RESOURCES;
1550 goto end;
1551 }
1552
1553 RtlCopyMemory(fcb->filepart.Buffer, stream.Buffer, stream.Length);
1554
1555 fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
1556
1557 fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
1558 if (!fcb->full_filename.Buffer) {
1559 ERR("out of memory\n");
1560 free_fcb(fcb);
1561 Status = STATUS_INSUFFICIENT_RESOURCES;
1562 goto end;
1563 }
1564
1565 fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
1566 RtlCopyMemory(fcb->full_filename.Buffer, parfcb->full_filename.Buffer, parfcb->full_filename.Length);
1567
1568 fcb->full_filename.Buffer[parfcb->full_filename.Length / sizeof(WCHAR)] = ':';
1569
1570 RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
1571 TRACE("full_filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1572
1573 InsertTailList(&fcb->par->children, &fcb->list_entry);
1574
1575 Status = set_xattr(Vcb, parfcb->subvol, parfcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback);
1576 if (!NT_SUCCESS(Status)) {
1577 ERR("set_xattr returned %08x\n", Status);
1578 free_fcb(fcb);
1579 goto end;
1580 }
1581
1582 KeQuerySystemTime(&time);
1583 win_time_to_unix(time, &now);
1584
1585 parfcb->inode_item.transid = Vcb->superblock.generation;
1586 parfcb->inode_item.sequence++;
1587 parfcb->inode_item.st_ctime = now;
1588
1589 searchkey.obj_id = parfcb->inode;
1590 searchkey.obj_type = TYPE_INODE_ITEM;
1591 searchkey.offset = 0xffffffffffffffff;
1592
1593 Status = find_item(Vcb, parfcb->subvol, &tp, &searchkey, FALSE);
1594 if (!NT_SUCCESS(Status)) {
1595 ERR("error - find_item returned %08x\n", Status);
1596 goto end;
1597 }
1598
1599 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1600 delete_tree_item(Vcb, &tp, rollback);
1601 } else {
1602 WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfcb->subvol->id);
1603 }
1604
1605 free_traverse_ptr(&tp);
1606
1607 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1608 if (!ii) {
1609 ERR("out of memory\n");
1610 Status = STATUS_INSUFFICIENT_RESOURCES;
1611 goto end;
1612 }
1613
1614 RtlCopyMemory(ii, &parfcb->inode_item, sizeof(INODE_ITEM));
1615
1616 insert_tree_item(Vcb, parfcb->subvol, parfcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
1617
1618 parfcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1619 parfcb->subvol->root_item.ctime = now;
1620
1621 ExFreePool(fpus.Buffer);
1622 fpus.Buffer = NULL;
1623 } else {
1624 Status = file_create2(Irp, Vcb, &fpus, parfcb, options, &fcb, rollback);
1625
1626 if (!NT_SUCCESS(Status)) {
1627 ERR("file_create2 returned %08x\n", Status);
1628 goto end;
1629 }
1630 }
1631
1632 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1633
1634 if (Vcb->fcbs)
1635 Vcb->fcbs->prev = fcb;
1636
1637 fcb->next = Vcb->fcbs;
1638 Vcb->fcbs = fcb;
1639
1640 ExReleaseResourceLite(&Vcb->fcb_lock);
1641
1642 Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
1643
1644 ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
1645 if (!ccb) {
1646 ERR("out of memory\n");
1647 Status = STATUS_INSUFFICIENT_RESOURCES;
1648 goto end;
1649 }
1650
1651 RtlZeroMemory(ccb, sizeof(*ccb));
1652 ccb->NodeType = BTRFS_NODE_TYPE_CCB;
1653 ccb->NodeSize = sizeof(ccb);
1654 ccb->disposition = disposition;
1655 ccb->options = options;
1656 ccb->query_dir_offset = 0;
1657 RtlInitUnicodeString(&ccb->query_string, NULL);
1658 ccb->has_wildcard = FALSE;
1659 ccb->specific_file = FALSE;
1660
1661 InterlockedIncrement(&fcb->open_count);
1662
1663 FileObject->FsContext2 = ccb;
1664
1665 FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
1666
1667 TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
1668
1669 Status = consider_write(Vcb);
1670
1671 if (NT_SUCCESS(Status)) {
1672 ULONG fnlen;
1673
1674 fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
1675
1676 if (fcb->par != Vcb->root_fcb)
1677 fcb->name_offset++;
1678
1679 fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
1680
1681 fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
1682 if (!fcb->full_filename.Buffer) {
1683 ERR("out of memory\n");
1684 Status = STATUS_INSUFFICIENT_RESOURCES;
1685 goto end;
1686 }
1687
1688 fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
1689 RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
1690
1691 if (fcb->par != Vcb->root_fcb)
1692 fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
1693
1694 RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
1695
1696 FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
1697 options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
1698 FILE_ACTION_ADDED, NULL);
1699
1700 goto end2;
1701 }
1702
1703 end:
1704 if (fpus.Buffer)
1705 ExFreePool(fpus.Buffer);
1706
1707 end2:
1708 if (parfcb)
1709 free_fcb(parfcb);
1710
1711 return Status;
1712 }
1713
1714 static __inline void debug_create_options(ULONG RequestedOptions) {
1715 if (RequestedOptions != 0) {
1716 ULONG options = RequestedOptions;
1717
1718 TRACE("requested options:\n");
1719
1720 if (options & FILE_DIRECTORY_FILE) {
1721 TRACE(" FILE_DIRECTORY_FILE\n");
1722 options &= ~FILE_DIRECTORY_FILE;
1723 }
1724
1725 if (options & FILE_WRITE_THROUGH) {
1726 TRACE(" FILE_WRITE_THROUGH\n");
1727 options &= ~FILE_WRITE_THROUGH;
1728 }
1729
1730 if (options & FILE_SEQUENTIAL_ONLY) {
1731 TRACE(" FILE_SEQUENTIAL_ONLY\n");
1732 options &= ~FILE_SEQUENTIAL_ONLY;
1733 }
1734
1735 if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
1736 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
1737 options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
1738 }
1739
1740 if (options & FILE_SYNCHRONOUS_IO_ALERT) {
1741 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
1742 options &= ~FILE_SYNCHRONOUS_IO_ALERT;
1743 }
1744
1745 if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
1746 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
1747 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
1748 }
1749
1750 if (options & FILE_NON_DIRECTORY_FILE) {
1751 TRACE(" FILE_NON_DIRECTORY_FILE\n");
1752 options &= ~FILE_NON_DIRECTORY_FILE;
1753 }
1754
1755 if (options & FILE_CREATE_TREE_CONNECTION) {
1756 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
1757 options &= ~FILE_CREATE_TREE_CONNECTION;
1758 }
1759
1760 if (options & FILE_COMPLETE_IF_OPLOCKED) {
1761 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
1762 options &= ~FILE_COMPLETE_IF_OPLOCKED;
1763 }
1764
1765 if (options & FILE_NO_EA_KNOWLEDGE) {
1766 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
1767 options &= ~FILE_NO_EA_KNOWLEDGE;
1768 }
1769
1770 if (options & FILE_OPEN_REMOTE_INSTANCE) {
1771 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
1772 options &= ~FILE_OPEN_REMOTE_INSTANCE;
1773 }
1774
1775 if (options & FILE_RANDOM_ACCESS) {
1776 TRACE(" FILE_RANDOM_ACCESS\n");
1777 options &= ~FILE_RANDOM_ACCESS;
1778 }
1779
1780 if (options & FILE_DELETE_ON_CLOSE) {
1781 TRACE(" FILE_DELETE_ON_CLOSE\n");
1782 options &= ~FILE_DELETE_ON_CLOSE;
1783 }
1784
1785 if (options & FILE_OPEN_BY_FILE_ID) {
1786 TRACE(" FILE_OPEN_BY_FILE_ID\n");
1787 options &= ~FILE_OPEN_BY_FILE_ID;
1788 }
1789
1790 if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
1791 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
1792 options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
1793 }
1794
1795 if (options & FILE_NO_COMPRESSION) {
1796 TRACE(" FILE_NO_COMPRESSION\n");
1797 options &= ~FILE_NO_COMPRESSION;
1798 }
1799
1800 #if NTDDI_VERSION >= NTDDI_WIN7
1801 if (options & FILE_OPEN_REQUIRING_OPLOCK) {
1802 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
1803 options &= ~FILE_OPEN_REQUIRING_OPLOCK;
1804 }
1805
1806 if (options & FILE_DISALLOW_EXCLUSIVE) {
1807 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
1808 options &= ~FILE_DISALLOW_EXCLUSIVE;
1809 }
1810 #endif
1811
1812 if (options & FILE_RESERVE_OPFILTER) {
1813 TRACE(" FILE_RESERVE_OPFILTER\n");
1814 options &= ~FILE_RESERVE_OPFILTER;
1815 }
1816
1817 if (options & FILE_OPEN_REPARSE_POINT) {
1818 TRACE(" FILE_OPEN_REPARSE_POINT\n");
1819 options &= ~FILE_OPEN_REPARSE_POINT;
1820 }
1821
1822 if (options & FILE_OPEN_NO_RECALL) {
1823 TRACE(" FILE_OPEN_NO_RECALL\n");
1824 options &= ~FILE_OPEN_NO_RECALL;
1825 }
1826
1827 if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
1828 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
1829 options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
1830 }
1831
1832 if (options)
1833 TRACE(" unknown options: %x\n", options);
1834 } else {
1835 TRACE("requested options: (none)\n");
1836 }
1837 }
1838
1839 static NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) {
1840 KEY searchkey;
1841 traverse_ptr tp;
1842 INODE_ITEM* newii;
1843 NTSTATUS Status;
1844
1845 searchkey.obj_id = inode;
1846 searchkey.obj_type = TYPE_INODE_ITEM;
1847 searchkey.offset = 0xffffffffffffffff;
1848
1849 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
1850 if (!NT_SUCCESS(Status)) {
1851 ERR("error - find_item returned %08x\n", Status);
1852 return Status;
1853 }
1854
1855 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1856 delete_tree_item(Vcb, &tp, rollback);
1857 } else {
1858 WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
1859 }
1860
1861 free_traverse_ptr(&tp);
1862
1863 newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1864 if (!newii) {
1865 ERR("out of memory\n");
1866 return STATUS_INSUFFICIENT_RESOURCES;
1867 }
1868
1869 RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
1870
1871 insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, 0, newii, sizeof(INODE_ITEM), NULL, rollback);
1872
1873 return STATUS_SUCCESS;
1874 }
1875
1876 static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
1877 PFILE_OBJECT FileObject;
1878 ULONG RequestedDisposition;
1879 ULONG options;
1880 NTSTATUS Status;
1881 fcb* fcb;
1882 ccb* ccb;
1883 device_extension* Vcb = DeviceObject->DeviceExtension;
1884 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
1885 ULONG access;
1886 PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState;
1887
1888 Irp->IoStatus.Information = 0;
1889
1890 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
1891 options = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
1892
1893 if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
1894 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
1895 Status = STATUS_INVALID_PARAMETER;
1896 goto exit;
1897 }
1898
1899 FileObject = Stack->FileObject;
1900
1901 debug_create_options(options);
1902
1903 switch (RequestedDisposition) {
1904 case FILE_SUPERSEDE:
1905 TRACE("requested disposition: FILE_SUPERSEDE\n");
1906 break;
1907
1908 case FILE_CREATE:
1909 TRACE("requested disposition: FILE_CREATE\n");
1910 break;
1911
1912 case FILE_OPEN:
1913 TRACE("requested disposition: FILE_OPEN\n");
1914 break;
1915
1916 case FILE_OPEN_IF:
1917 TRACE("requested disposition: FILE_OPEN_IF\n");
1918 break;
1919
1920 case FILE_OVERWRITE:
1921 TRACE("requested disposition: FILE_OVERWRITE\n");
1922 break;
1923
1924 case FILE_OVERWRITE_IF:
1925 TRACE("requested disposition: FILE_OVERWRITE_IF\n");
1926 break;
1927
1928 default:
1929 ERR("unknown disposition: %x\n", RequestedDisposition);
1930 Status = STATUS_NOT_IMPLEMENTED;
1931 goto exit;
1932 }
1933
1934 TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
1935 TRACE("FileObject = %p\n", FileObject);
1936
1937 if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
1938 Status = STATUS_MEDIA_WRITE_PROTECTED;
1939 goto exit;
1940 }
1941
1942 // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
1943
1944 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1945 Status = get_fcb(Vcb, &fcb, &FileObject->FileName, FileObject->RelatedFileObject ? FileObject->RelatedFileObject->FsContext : NULL, Stack->Flags & SL_OPEN_TARGET_DIRECTORY);
1946 ExReleaseResourceLite(&Vcb->fcb_lock);
1947
1948 if (NT_SUCCESS(Status) && fcb->deleted) {
1949 free_fcb(fcb);
1950 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1951 }
1952
1953 if (NT_SUCCESS(Status)) {
1954 if (RequestedDisposition == FILE_CREATE) {
1955 TRACE("file %.*S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1956 Status = STATUS_OBJECT_NAME_COLLISION;
1957 free_fcb(fcb);
1958 goto exit;
1959 }
1960 } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
1961 if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
1962 TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
1963 goto exit;
1964 }
1965 } else {
1966 TRACE("get_fcb returned %08x\n", Status);
1967 goto exit;
1968 }
1969
1970 if (NT_SUCCESS(Status)) { // file already exists
1971 struct _fcb* sf;
1972
1973 if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
1974 Status = STATUS_MEDIA_WRITE_PROTECTED;
1975 free_fcb(fcb);
1976 goto exit;
1977 }
1978
1979 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
1980 RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
1981 Status = STATUS_ACCESS_DENIED;
1982 free_fcb(fcb);
1983 goto exit;
1984 }
1985
1986 TRACE("deleted = %s\n", fcb->deleted ? "TRUE" : "FALSE");
1987
1988 sf = fcb;
1989 while (sf) {
1990 if (sf->delete_on_close) {
1991 WARN("could not open as deletion pending\n");
1992 Status = STATUS_DELETE_PENDING;
1993 free_fcb(fcb);
1994 goto exit;
1995 }
1996 sf = sf->par;
1997 }
1998
1999 if (fcb->type == BTRFS_TYPE_SYMLINK && !(options & FILE_OPEN_REPARSE_POINT)) {
2000 if (!follow_symlink(fcb, FileObject)) {
2001 ERR("follow_symlink failed\n");
2002 Status = STATUS_INTERNAL_ERROR;
2003 free_fcb(fcb);
2004 goto exit;
2005 }
2006
2007 Status = STATUS_REPARSE;
2008 Irp->IoStatus.Information = IO_REPARSE;
2009 free_fcb(fcb);
2010 goto exit;
2011 }
2012
2013 if (!SeAccessCheck(fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
2014 IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
2015 WARN("SeAccessCheck failed, returning %08x\n", Status);
2016 free_fcb(fcb);
2017 goto exit;
2018 }
2019
2020 if (fcb->open_count > 0) {
2021 Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access, TRUE);
2022
2023 if (!NT_SUCCESS(Status)) {
2024 WARN("IoCheckShareAccess failed, returning %08x\n", Status);
2025 free_fcb(fcb);
2026 goto exit;
2027 }
2028 } else {
2029 IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fcb->share_access);
2030 }
2031
2032 if (access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
2033 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForWrite)) {
2034 Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
2035 free_fcb(fcb);
2036 goto exit;
2037 }
2038 }
2039
2040 if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
2041 ULONG defda;
2042 LIST_ENTRY changed_sector_list;
2043
2044 if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fcb->atts & FILE_ATTRIBUTE_READONLY) {
2045 WARN("cannot overwrite readonly file\n");
2046 Status = STATUS_ACCESS_DENIED;
2047 free_fcb(fcb);
2048 goto exit;
2049 }
2050
2051 // FIXME - where did we get this from?
2052 // if (fcb->refcount > 1) {
2053 // WARN("cannot overwrite open file (fcb = %p, refcount = %u)\n", fcb, fcb->refcount);
2054 // Status = STATUS_ACCESS_DENIED;
2055 // free_fcb(fcb);
2056 // goto exit;
2057 // }
2058 InitializeListHead(&changed_sector_list);
2059
2060 // FIXME - make sure not ADS!
2061 Status = truncate_file(fcb, fcb->inode_item.st_size, rollback);
2062 if (!NT_SUCCESS(Status)) {
2063 ERR("truncate_file returned %08x\n", Status);
2064 free_fcb(fcb);
2065 goto exit;
2066 }
2067
2068 Status = update_inode_item(Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback);
2069 if (!NT_SUCCESS(Status)) {
2070 ERR("update_inode_item returned %08x\n", Status);
2071 free_fcb(fcb);
2072 goto exit;
2073 }
2074
2075 defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
2076
2077 if (RequestedDisposition == FILE_SUPERSEDE)
2078 fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
2079 else
2080 fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
2081
2082 if (Stack->Parameters.Create.FileAttributes != defda) {
2083 char val[64];
2084
2085 sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
2086
2087 Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
2088 if (!NT_SUCCESS(Status)) {
2089 ERR("set_xattr returned %08x\n", Status);
2090 free_fcb(fcb);
2091 goto exit;
2092 }
2093 } else
2094 delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
2095
2096 // FIXME - truncate streams
2097 // FIXME - do we need to alter parent directory's times?
2098 // FIXME - send notifications
2099
2100 Status = consider_write(fcb->Vcb);
2101 if (!NT_SUCCESS(Status)) {
2102 ERR("consider_write returned %08x\n", Status);
2103 free_fcb(fcb);
2104 goto exit;
2105 }
2106 }
2107
2108 // fcb->Header.IsFastIoPossible = TRUE;
2109
2110 if (fcb->ads) {
2111 fcb->Header.AllocationSize.QuadPart = fcb->adssize;
2112 fcb->Header.FileSize.QuadPart = fcb->adssize;
2113 fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
2114 } else if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
2115 fcb->Header.AllocationSize.QuadPart = 0;
2116 fcb->Header.FileSize.QuadPart = 0;
2117 fcb->Header.ValidDataLength.QuadPart = 0;
2118 } else {
2119 KEY searchkey;
2120 traverse_ptr tp;
2121 EXTENT_DATA* ed;
2122
2123 searchkey.obj_id = fcb->inode;
2124 searchkey.obj_type = TYPE_EXTENT_DATA;
2125 searchkey.offset = 0xffffffffffffffff;
2126
2127 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
2128 if (!NT_SUCCESS(Status)) {
2129 ERR("error - find_item returned %08x\n", Status);
2130 free_fcb(fcb);
2131 goto exit;
2132 }
2133
2134 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
2135 ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
2136 free_traverse_ptr(&tp);
2137 free_fcb(fcb);
2138 Status = STATUS_INTERNAL_ERROR;
2139 goto exit;
2140 }
2141
2142 if (tp.item->size < sizeof(EXTENT_DATA)) {
2143 ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
2144 tp.item->size, sizeof(EXTENT_DATA));
2145 free_traverse_ptr(&tp);
2146 free_fcb(fcb);
2147 Status = STATUS_INTERNAL_ERROR;
2148 goto exit;
2149 }
2150
2151 ed = (EXTENT_DATA*)tp.item->data;
2152
2153 if (ed->type == EXTENT_TYPE_INLINE)
2154 fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
2155 else
2156 fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
2157
2158 fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
2159 fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
2160
2161 free_traverse_ptr(&tp);
2162 }
2163
2164 if (options & FILE_NON_DIRECTORY_FILE && fcb->type == BTRFS_TYPE_DIRECTORY) {
2165 free_fcb(fcb);
2166 Status = STATUS_FILE_IS_A_DIRECTORY;
2167 goto exit;
2168 } else if (options & FILE_DIRECTORY_FILE && fcb->type != BTRFS_TYPE_DIRECTORY) {
2169 TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, path = %.*S)\n", fcb->type, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
2170 free_fcb(fcb);
2171 Status = STATUS_NOT_A_DIRECTORY;
2172 goto exit;
2173 }
2174
2175 Status = attach_fcb_to_fileobject(Vcb, fcb, FileObject);
2176
2177 if (options & FILE_DELETE_ON_CLOSE)
2178 fcb->delete_on_close = TRUE;
2179
2180 ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
2181 if (!ccb) {
2182 ERR("out of memory\n");
2183 free_fcb(fcb);
2184 Status = STATUS_INSUFFICIENT_RESOURCES;
2185 goto exit;
2186 }
2187
2188 RtlZeroMemory(ccb, sizeof(*ccb));
2189 ccb->NodeType = BTRFS_NODE_TYPE_CCB;
2190 ccb->NodeSize = sizeof(ccb);
2191 ccb->disposition = RequestedDisposition;
2192 ccb->options = options;
2193 ccb->query_dir_offset = 0;
2194 RtlInitUnicodeString(&ccb->query_string, NULL);
2195 ccb->has_wildcard = FALSE;
2196 ccb->specific_file = FALSE;
2197
2198 FileObject->FsContext2 = ccb;
2199
2200 FileObject->SectionObjectPointer = &fcb->nonpaged->segment_object;
2201
2202 if (NT_SUCCESS(Status)) {
2203 switch (RequestedDisposition) {
2204 case FILE_SUPERSEDE:
2205 Irp->IoStatus.Information = FILE_SUPERSEDED;
2206 break;
2207
2208 case FILE_OPEN:
2209 case FILE_OPEN_IF:
2210 Irp->IoStatus.Information = FILE_OPENED;
2211 break;
2212
2213 case FILE_OVERWRITE:
2214 case FILE_OVERWRITE_IF:
2215 Irp->IoStatus.Information = FILE_OVERWRITTEN;
2216 break;
2217 }
2218 }
2219
2220 InterlockedIncrement(&fcb->open_count);
2221 } else {
2222 Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
2223 Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
2224
2225 // if (!NT_SUCCESS(Status))
2226 // free_fcb(fcb);
2227 }
2228
2229 if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
2230 FileObject->Flags |= FO_CACHE_SUPPORTED;
2231
2232 exit:
2233 if (NT_SUCCESS(Status)) {
2234 if (!FileObject->Vpb)
2235 FileObject->Vpb = DeviceObject->Vpb;
2236 } else {
2237 if (Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
2238 TRACE("returning %08x\n", Status);
2239 }
2240
2241 return Status;
2242 }
2243
2244 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2245 NTSTATUS Status;
2246 PIO_STACK_LOCATION IrpSp;
2247 device_extension* Vcb = NULL;
2248 BOOL top_level;
2249 LIST_ENTRY rollback;
2250
2251 TRACE("create (flags = %x)\n", Irp->Flags);
2252
2253 InitializeListHead(&rollback);
2254
2255 FsRtlEnterFileSystem();
2256
2257 top_level = is_top_level(Irp);
2258
2259 /* return success if just called for FS device object */
2260 if (DeviceObject == devobj) {
2261 TRACE("create called for FS device object\n");
2262
2263 Irp->IoStatus.Information = FILE_OPENED;
2264 Status = STATUS_SUCCESS;
2265
2266 goto exit;
2267 }
2268
2269 Vcb = DeviceObject->DeviceExtension;
2270 ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
2271
2272 IrpSp = IoGetCurrentIrpStackLocation(Irp);
2273
2274 if (IrpSp->Flags != 0) {
2275 UINT32 flags = IrpSp->Flags;
2276
2277 TRACE("flags:\n");
2278
2279 if (flags & SL_CASE_SENSITIVE) {
2280 TRACE("SL_CASE_SENSITIVE\n");
2281 flags &= ~SL_CASE_SENSITIVE;
2282 }
2283
2284 if (flags & SL_FORCE_ACCESS_CHECK) {
2285 TRACE("SL_FORCE_ACCESS_CHECK\n");
2286 flags &= ~SL_FORCE_ACCESS_CHECK;
2287 }
2288
2289 if (flags & SL_OPEN_PAGING_FILE) {
2290 TRACE("SL_OPEN_PAGING_FILE\n");
2291 flags &= ~SL_OPEN_PAGING_FILE;
2292 }
2293
2294 if (flags & SL_OPEN_TARGET_DIRECTORY) {
2295 TRACE("SL_OPEN_TARGET_DIRECTORY\n");
2296 flags &= ~SL_OPEN_TARGET_DIRECTORY;
2297 }
2298
2299 if (flags & SL_STOP_ON_SYMLINK) {
2300 TRACE("SL_STOP_ON_SYMLINK\n");
2301 flags &= ~SL_STOP_ON_SYMLINK;
2302 }
2303
2304 if (flags)
2305 WARN("unknown flags: %x\n", flags);
2306 } else {
2307 TRACE("flags: (none)\n");
2308 }
2309
2310 // Vpb = DeviceObject->DeviceExtension;
2311
2312 // TRACE("create called for something other than FS device object\n");
2313
2314 // opening volume
2315 // FIXME - also check if RelatedFileObject is Vcb
2316 if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
2317 ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
2318 ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
2319
2320 TRACE("open operation for volume\n");
2321
2322 if (RequestedDisposition != FILE_OPEN &&
2323 RequestedDisposition != FILE_OPEN_IF)
2324 {
2325 Status = STATUS_ACCESS_DENIED;
2326 goto exit;
2327 }
2328
2329 if (RequestedOptions & FILE_DIRECTORY_FILE)
2330 {
2331 Status = STATUS_NOT_A_DIRECTORY;
2332 goto exit;
2333 }
2334
2335 Vcb->volume_fcb->refcount++;
2336 #ifdef DEBUG_FCB_REFCOUNTS
2337 WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, Vcb->volume_fcb->refcount);
2338 #endif
2339 attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
2340 // // NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
2341 // // DeviceExt->VolumeFcb->RefCount++;
2342 //
2343 Irp->IoStatus.Information = FILE_OPENED;
2344 Status = STATUS_SUCCESS;
2345 } else {
2346 BOOL exclusive;
2347 ULONG disposition;
2348
2349 TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
2350
2351 if (IrpSp->FileObject->RelatedFileObject) {
2352 fcb* relfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
2353
2354 if (relfcb)
2355 TRACE("related file name = %.*S\n", relfcb->full_filename.Length / sizeof(WCHAR), relfcb->full_filename.Buffer);
2356 }
2357
2358 disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
2359
2360 // We acquire the lock exclusively if there's the possibility we might be writing
2361 exclusive = disposition != FILE_OPEN;
2362
2363 acquire_tree_lock(Vcb, exclusive);
2364
2365 // ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
2366 // Status = NtfsCreateFile(DeviceObject,
2367 // Irp);
2368 Status = create_file(DeviceObject, Irp, &rollback);
2369 // ExReleaseResourceLite(&Vpb->DirResource);
2370
2371 if (exclusive && !NT_SUCCESS(Status))
2372 do_rollback(Vcb, &rollback);
2373 else
2374 clear_rollback(&rollback);
2375
2376 release_tree_lock(Vcb, exclusive);
2377
2378 // Status = STATUS_ACCESS_DENIED;
2379 }
2380
2381 exit:
2382 Irp->IoStatus.Status = Status;
2383 IoCompleteRequest( Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT );
2384 // IoCompleteRequest( Irp, IO_DISK_INCREMENT );
2385
2386 TRACE("create returning %08x\n", Status);
2387
2388 ExReleaseResourceLite(&Vcb->load_lock);
2389
2390 if (top_level)
2391 IoSetTopLevelIrp(NULL);
2392
2393 FsRtlExitFileSystem();
2394
2395 return Status;
2396 }