[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / fsctl.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 "btrfs_drv.h"
19 #include "btrfsioctl.h"
20 #ifndef __REACTOS__
21 #include <winioctl.h>
22 #endif
23
24 #ifndef FSCTL_CSV_CONTROL
25 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
26 #endif
27
28 #define DOTDOT ".."
29
30 static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
31 btrfs_get_file_ids* bgfi;
32 fcb* fcb;
33
34 if (length < sizeof(btrfs_get_file_ids))
35 return STATUS_BUFFER_OVERFLOW;
36
37 if (!FileObject)
38 return STATUS_INVALID_PARAMETER;
39
40 fcb = FileObject->FsContext;
41
42 if (!fcb)
43 return STATUS_INVALID_PARAMETER;
44
45 bgfi = data;
46
47 bgfi->subvol = fcb->subvol->id;
48 bgfi->inode = fcb->inode;
49 bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
50
51 return STATUS_SUCCESS;
52 }
53
54 static void get_uuid(BTRFS_UUID* uuid) {
55 LARGE_INTEGER seed;
56 UINT8 i;
57
58 seed = KeQueryPerformanceCounter(NULL);
59
60 for (i = 0; i < 16; i+=2) {
61 ULONG rand = RtlRandomEx(&seed.LowPart);
62
63 uuid->uuid[i] = (rand & 0xff00) >> 8;
64 uuid->uuid[i+1] = rand & 0xff;
65 }
66 }
67
68 static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) {
69 UINT8* buf;
70 NTSTATUS Status;
71 write_tree_context* wtc;
72 LIST_ENTRY* le;
73 tree t;
74 tree_header* th;
75 chunk* c;
76
77 buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
78 if (!buf) {
79 ERR("out of memory\n");
80 return STATUS_INSUFFICIENT_RESOURCES;
81 }
82
83 wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_tree_context), ALLOC_TAG);
84 if (!wtc) {
85 ERR("out of memory\n");
86 ExFreePool(buf);
87 return STATUS_INSUFFICIENT_RESOURCES;
88 }
89
90 Status = read_tree(Vcb, addr, buf);
91 if (!NT_SUCCESS(Status)) {
92 ERR("read_tree returned %08x\n", Status);
93 goto end;
94 }
95
96 th = (tree_header*)buf;
97
98 RtlZeroMemory(&t, sizeof(tree));
99 t.flags = dupflags;
100 t.root = subvol;
101 t.header.level = th->level;
102 t.header.tree_id = t.root->id;
103
104 Status = get_tree_new_address(Vcb, &t, rollback);
105 if (!NT_SUCCESS(Status)) {
106 ERR("get_tree_new_address returned %08x\n", Status);
107 goto end;
108 }
109
110 if (!t.has_new_address) {
111 ERR("tree new address not set\n");
112 Status = STATUS_INTERNAL_ERROR;
113 goto end;
114 }
115
116 c = get_chunk_from_address(Vcb, t.new_address);
117
118 if (c) {
119 increase_chunk_usage(c, Vcb->superblock.node_size);
120 } else {
121 ERR("could not find chunk for address %llx\n", t.new_address);
122 Status = STATUS_INTERNAL_ERROR;
123 goto end;
124 }
125
126 th->address = t.new_address;
127 th->tree_id = subvol->id;
128 th->generation = Vcb->superblock.generation;
129
130 if (th->level == 0) {
131 UINT32 i;
132 leaf_node* ln = (leaf_node*)&th[1];
133
134 for (i = 0; i < th->num_items; i++) {
135 if (ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA) && ln[i].offset + ln[i].size <= Vcb->superblock.node_size - sizeof(tree_header)) {
136 EXTENT_DATA* ed = (EXTENT_DATA*)(((UINT8*)&th[1]) + ln[i].offset);
137
138 // FIXME - what are we supposed to do with prealloc here? Replace it with sparse extents, or do new preallocation?
139 if (ed->type == EXTENT_TYPE_REGULAR && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
140 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
141
142 if (ed2->size != 0) { // not sparse
143 Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback);
144
145 if (!NT_SUCCESS(Status)) {
146 ERR("increase_extent_refcount_data returned %08x\n", Status);
147 goto end;
148 }
149 }
150 }
151 }
152 }
153 } else {
154 UINT32 i;
155 UINT64 newaddr;
156 internal_node* in = (internal_node*)&th[1];
157
158 for (i = 0; i < th->num_items; i++) {
159 Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, rollback);
160
161 if (!NT_SUCCESS(Status)) {
162 ERR("snapshot_tree_copy returned %08x\n", Status);
163 goto end;
164 }
165
166 in[i].generation = Vcb->superblock.generation;
167 in[i].address = newaddr;
168 }
169 }
170
171 *((UINT32*)buf) = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
172
173 KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
174 InitializeListHead(&wtc->stripes);
175
176 Status = write_tree(Vcb, t.new_address, buf, wtc);
177 if (!NT_SUCCESS(Status)) {
178 ERR("write_tree returned %08x\n", Status);
179 goto end;
180 }
181
182 if (wtc->stripes.Flink != &wtc->stripes) {
183 // launch writes and wait
184 le = wtc->stripes.Flink;
185 while (le != &wtc->stripes) {
186 write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
187
188 IoCallDriver(stripe->device->devobj, stripe->Irp);
189
190 le = le->Flink;
191 }
192
193 KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
194
195 le = wtc->stripes.Flink;
196 while (le != &wtc->stripes) {
197 write_tree_stripe* stripe = CONTAINING_RECORD(le, write_tree_stripe, list_entry);
198
199 if (!NT_SUCCESS(stripe->iosb.Status)) {
200 Status = stripe->iosb.Status;
201 break;
202 }
203
204 le = le->Flink;
205 }
206
207 free_write_tree_stripes(wtc);
208 buf = NULL;
209 }
210
211 if (NT_SUCCESS(Status))
212 *newaddr = t.new_address;
213
214 end:
215 ExFreePool(wtc);
216
217 if (buf)
218 ExFreePool(buf);
219
220 return Status;
221 }
222
223 static void flush_subvol_fcbs(root* subvol) {
224 LIST_ENTRY* le = subvol->fcbs.Flink;
225
226 if (IsListEmpty(&subvol->fcbs))
227 return;
228
229 while (le != &subvol->fcbs) {
230 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
231 IO_STATUS_BLOCK iosb;
232
233 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
234 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
235
236 le = le->Flink;
237 }
238 }
239
240 static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8) {
241 LIST_ENTRY rollback;
242 UINT64 id;
243 NTSTATUS Status;
244 root *r, *subvol = subvol_fcb->subvol;
245 KEY searchkey;
246 traverse_ptr tp;
247 UINT64 address, dirpos, *root_num;
248 LARGE_INTEGER time;
249 BTRFS_TIME now;
250 fcb* fcb = parent->FsContext;
251 ULONG disize, rrsize;
252 DIR_ITEM *di, *di2;
253 ROOT_REF *rr, *rr2;
254 INODE_ITEM* ii;
255
256 InitializeListHead(&rollback);
257
258 acquire_tree_lock(Vcb, TRUE);
259
260 // flush open files on this subvol
261
262 flush_subvol_fcbs(subvol);
263
264 // flush metadata
265
266 if (Vcb->write_trees > 0)
267 do_write(Vcb, &rollback);
268
269 free_trees(Vcb);
270
271 clear_rollback(&rollback);
272
273 InitializeListHead(&rollback);
274
275 // create new root
276
277 if (Vcb->root_root->lastinode == 0)
278 get_last_inode(Vcb, Vcb->root_root);
279
280 id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
281 Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback);
282
283 if (!NT_SUCCESS(Status)) {
284 ERR("create_root returned %08x\n", Status);
285 goto end;
286 }
287
288 if (!Vcb->uuid_root) {
289 root* uuid_root;
290
291 TRACE("uuid root doesn't exist, creating it\n");
292
293 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
294
295 if (!NT_SUCCESS(Status)) {
296 ERR("create_root returned %08x\n", Status);
297 goto end;
298 }
299
300 Vcb->uuid_root = uuid_root;
301 }
302
303 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
304 if (!root_num) {
305 ERR("out of memory\n");
306 Status = STATUS_INSUFFICIENT_RESOURCES;
307 goto end;
308 }
309
310 tp.tree = NULL;
311
312 do {
313 get_uuid(&r->root_item.uuid);
314
315 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
316 searchkey.obj_type = TYPE_SUBVOL_UUID;
317 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
318
319 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
320 } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
321
322 *root_num = r->id;
323
324 if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
325 ERR("insert_tree_item failed\n");
326 ExFreePool(root_num);
327 Status = STATUS_INTERNAL_ERROR;
328 goto end;
329 }
330
331 searchkey.obj_id = r->id;
332 searchkey.obj_type = TYPE_ROOT_ITEM;
333 searchkey.offset = 0xffffffffffffffff;
334
335 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
336 if (!NT_SUCCESS(Status)) {
337 ERR("error - find_item returned %08x\n", Status);
338 goto end;
339 }
340
341 Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback);
342 if (!NT_SUCCESS(Status)) {
343 ERR("snapshot_tree_copy returned %08x\n", Status);
344 goto end;
345 }
346
347 KeQuerySystemTime(&time);
348 win_time_to_unix(time, &now);
349
350 r->root_item.inode.generation = 1;
351 r->root_item.inode.st_size = 3;
352 r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
353 r->root_item.inode.st_nlink = 1;
354 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
355 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
356 r->root_item.generation = Vcb->superblock.generation;
357 r->root_item.objid = subvol->root_item.objid;
358 r->root_item.block_number = address;
359 r->root_item.bytes_used = subvol->root_item.bytes_used;
360 r->root_item.last_snapshot_generation = Vcb->superblock.generation;
361 r->root_item.root_level = subvol->root_item.root_level;
362 r->root_item.generation2 = Vcb->superblock.generation;
363 r->root_item.parent_uuid = subvol->root_item.uuid;
364 r->root_item.ctransid = subvol->root_item.ctransid;
365 r->root_item.otransid = Vcb->superblock.generation;
366 r->root_item.ctime = subvol->root_item.ctime;
367 r->root_item.otime = now;
368
369 r->treeholder.address = address;
370
371 // FIXME - do we need to copy over the send and receive fields too?
372
373 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
374 ERR("error - could not find ROOT_ITEM for subvol %llx\n", r->id);
375 Status = STATUS_INTERNAL_ERROR;
376 goto end;
377 }
378
379 RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM));
380 Vcb->root_root->lastinode = r->id;
381
382 // update ROOT_ITEM of original subvol
383
384 subvol->root_item.last_snapshot_generation = Vcb->superblock.generation;
385
386 // We also rewrite the top of the old subvolume tree, for some reason
387 searchkey.obj_id = 0;
388 searchkey.obj_type = 0;
389 searchkey.offset = 0;
390
391 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
392 if (!NT_SUCCESS(Status)) {
393 ERR("error - find_item returned %08x\n", Status);
394 goto end;
395 }
396
397 if (!subvol->treeholder.tree->write) {
398 subvol->treeholder.tree->write = TRUE;
399 Vcb->write_trees++;
400 }
401
402 // add DIR_ITEM
403
404 dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode);
405 if (dirpos == 0) {
406 ERR("find_next_dir_index failed\n");
407 Status = STATUS_INTERNAL_ERROR;
408 goto end;
409 }
410
411 disize = sizeof(DIR_ITEM) - 1 + utf8->Length;
412 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
413 if (!di) {
414 ERR("out of memory\n");
415 Status = STATUS_INSUFFICIENT_RESOURCES;
416 goto end;
417 }
418
419 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
420 if (!di2) {
421 ERR("out of memory\n");
422 Status = STATUS_INSUFFICIENT_RESOURCES;
423 ExFreePool(di);
424 goto end;
425 }
426
427 di->key.obj_id = id;
428 di->key.obj_type = TYPE_ROOT_ITEM;
429 di->key.offset = 0xffffffffffffffff;
430 di->transid = Vcb->superblock.generation;
431 di->m = 0;
432 di->n = utf8->Length;
433 di->type = BTRFS_TYPE_DIRECTORY;
434 RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
435
436 RtlCopyMemory(di2, di, disize);
437
438 Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback);
439 if (!NT_SUCCESS(Status)) {
440 ERR("add_dir_item returned %08x\n", Status);
441 goto end;
442 }
443
444 // add DIR_INDEX
445
446 if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) {
447 ERR("insert_tree_item failed\n");
448 Status = STATUS_INTERNAL_ERROR;
449 goto end;
450 }
451
452 // add ROOT_REF
453
454 rrsize = sizeof(ROOT_REF) - 1 + utf8->Length;
455 rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
456 if (!rr) {
457 ERR("out of memory\n");
458 Status = STATUS_INSUFFICIENT_RESOURCES;
459 goto end;
460 }
461
462 rr->dir = fcb->inode;
463 rr->index = dirpos;
464 rr->n = utf8->Length;
465 RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
466
467 if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) {
468 ERR("insert_tree_item failed\n");
469 Status = STATUS_INTERNAL_ERROR;
470 goto end;
471 }
472
473 // add ROOT_BACKREF
474
475 rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
476 if (!rr2) {
477 ERR("out of memory\n");
478 Status = STATUS_INSUFFICIENT_RESOURCES;
479 goto end;
480 }
481
482 RtlCopyMemory(rr2, rr, rrsize);
483
484 if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) {
485 ERR("insert_tree_item failed\n");
486 Status = STATUS_INTERNAL_ERROR;
487 goto end;
488 }
489
490 // change fcb's INODE_ITEM
491
492 // unlike when we create a file normally, the seq of the parent doesn't appear to change
493 fcb->inode_item.transid = Vcb->superblock.generation;
494 fcb->inode_item.st_size += utf8->Length * 2;
495 fcb->inode_item.st_ctime = now;
496 fcb->inode_item.st_mtime = now;
497
498 searchkey.obj_id = fcb->inode;
499 searchkey.obj_type = TYPE_INODE_ITEM;
500 searchkey.offset = 0;
501
502 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
503 if (!NT_SUCCESS(Status)) {
504 ERR("error - find_item returned %08x\n", Status);
505 goto end;
506 }
507
508 if (keycmp(&searchkey, &tp.item->key)) {
509 ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
510 Status = STATUS_INTERNAL_ERROR;
511 goto end;
512 }
513
514 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
515 if (!ii) {
516 ERR("out of memory\n");
517 Status = STATUS_INSUFFICIENT_RESOURCES;
518 goto end;
519 }
520
521 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
522 delete_tree_item(Vcb, &tp, &rollback);
523
524 insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback);
525
526 fcb->subvol->root_item.ctime = now;
527 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
528
529 if (Vcb->write_trees > 0)
530 do_write(Vcb, &rollback);
531
532 free_trees(Vcb);
533
534 Status = STATUS_SUCCESS;
535
536 end:
537 if (NT_SUCCESS(Status))
538 clear_rollback(&rollback);
539 else
540 do_rollback(Vcb, &rollback);
541
542 release_tree_lock(Vcb, TRUE);
543
544 return Status;
545 }
546
547 static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
548 PFILE_OBJECT subvol_obj;
549 NTSTATUS Status;
550 btrfs_create_snapshot* bcs = data;
551 fcb* subvol_fcb;
552 ANSI_STRING utf8;
553 UNICODE_STRING nameus;
554 ULONG len;
555 UINT32 crc32;
556 fcb* fcb;
557 ccb* ccb;
558 file_ref* fileref;
559
560 if (length < offsetof(btrfs_create_snapshot, name))
561 return STATUS_INVALID_PARAMETER;
562
563 if (length < offsetof(btrfs_create_snapshot, name) + bcs->namelen)
564 return STATUS_INVALID_PARAMETER;
565
566 if (!bcs->subvol)
567 return STATUS_INVALID_PARAMETER;
568
569 if (!FileObject || !FileObject->FsContext)
570 return STATUS_INVALID_PARAMETER;
571
572 fcb = FileObject->FsContext;
573 ccb = FileObject->FsContext2;
574
575 if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
576 return STATUS_INVALID_PARAMETER;
577
578 fileref = ccb->fileref;
579
580 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
581 WARN("insufficient privileges\n");
582 return STATUS_ACCESS_DENIED;
583 }
584
585 nameus.Buffer = bcs->name;
586 nameus.Length = nameus.MaximumLength = bcs->namelen;
587
588 if (!is_file_name_valid(&nameus))
589 return STATUS_OBJECT_NAME_INVALID;
590
591 utf8.Buffer = NULL;
592
593 Status = RtlUnicodeToUTF8N(NULL, 0, &len, bcs->name, bcs->namelen);
594 if (!NT_SUCCESS(Status)) {
595 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
596 return Status;
597 }
598
599 if (len == 0) {
600 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
601 return STATUS_INTERNAL_ERROR;
602 }
603
604 utf8.MaximumLength = utf8.Length = len;
605 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
606
607 if (!utf8.Buffer) {
608 ERR("out of memory\n");
609 return STATUS_INSUFFICIENT_RESOURCES;
610 }
611
612 Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, bcs->name, bcs->namelen);
613 if (!NT_SUCCESS(Status)) {
614 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
615 goto end2;
616 }
617
618 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
619
620 if (find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) {
621 WARN("file already exists\n");
622 Status = STATUS_OBJECT_NAME_COLLISION;
623 goto end2;
624 }
625
626 Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL);
627 if (!NT_SUCCESS(Status)) {
628 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
629 goto end2;
630 }
631
632 subvol_fcb = subvol_obj->FsContext;
633 if (!subvol_fcb) {
634 Status = STATUS_INVALID_PARAMETER;
635 goto end;
636 }
637
638 if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
639 WARN("handle inode was %llx, expected %llx\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
640 Status = STATUS_INVALID_PARAMETER;
641 goto end;
642 }
643
644 ccb = subvol_obj->FsContext2;
645
646 if (!ccb) {
647 Status = STATUS_INVALID_PARAMETER;
648 goto end;
649 }
650
651 if (!(ccb->access & FILE_TRAVERSE)) {
652 WARN("insufficient privileges\n");
653 Status = STATUS_ACCESS_DENIED;
654 goto end;
655 }
656
657 Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8);
658
659 if (NT_SUCCESS(Status)) {
660 UNICODE_STRING ffn;
661
662 ffn.Length = fileref->full_filename.Length + bcs->namelen;
663 if (fcb != fcb->Vcb->root_fileref->fcb)
664 ffn.Length += sizeof(WCHAR);
665
666 ffn.MaximumLength = ffn.Length;
667 ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG);
668
669 if (ffn.Buffer) {
670 ULONG i;
671
672 RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length);
673 i = fileref->full_filename.Length;
674
675 if (fcb != fcb->Vcb->root_fileref->fcb) {
676 ffn.Buffer[i / sizeof(WCHAR)] = '\\';
677 i += sizeof(WCHAR);
678 }
679
680 RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], bcs->name, bcs->namelen);
681
682 TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer);
683
684 FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL,
685 FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
686
687 ExFreePool(ffn.Buffer);
688 } else
689 ERR("out of memory\n");
690 }
691
692 end:
693 ObDereferenceObject(subvol_obj);
694
695 end2:
696 ExFreePool(utf8.Buffer);
697
698 return Status;
699 }
700
701 static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) {
702 fcb* fcb;
703 ccb* ccb;
704 file_ref* fileref;
705 NTSTATUS Status;
706 LIST_ENTRY rollback;
707 UINT64 id;
708 root* r;
709 LARGE_INTEGER time;
710 BTRFS_TIME now;
711 ULONG len, disize, rrsize, irsize;
712 UNICODE_STRING nameus;
713 ANSI_STRING utf8;
714 UINT64 dirpos;
715 DIR_ITEM *di, *di2;
716 UINT32 crc32;
717 ROOT_REF *rr, *rr2;
718 INODE_ITEM* ii;
719 INODE_REF* ir;
720 KEY searchkey;
721 traverse_ptr tp;
722 SECURITY_DESCRIPTOR* sd = NULL;
723 SECURITY_SUBJECT_CONTEXT subjcont;
724 PSID owner;
725 BOOLEAN defaulted;
726 UINT64* root_num;
727
728 fcb = FileObject->FsContext;
729 if (!fcb) {
730 ERR("error - fcb was NULL\n");
731 return STATUS_INTERNAL_ERROR;
732 }
733
734 ccb = FileObject->FsContext2;
735 if (!ccb) {
736 ERR("error - ccb was NULL\n");
737 return STATUS_INTERNAL_ERROR;
738 }
739
740 fileref = ccb->fileref;
741
742 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
743 ERR("parent FCB was not a directory\n");
744 return STATUS_NOT_A_DIRECTORY;
745 }
746
747 if (fileref->deleted || fcb->deleted) {
748 ERR("parent has been deleted\n");
749 return STATUS_FILE_DELETED;
750 }
751
752 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
753 WARN("insufficient privileges\n");
754 return STATUS_ACCESS_DENIED;
755 }
756
757 nameus.Length = nameus.MaximumLength = length;
758 nameus.Buffer = name;
759
760 if (!is_file_name_valid(&nameus))
761 return STATUS_OBJECT_NAME_INVALID;
762
763 utf8.Buffer = NULL;
764
765 Status = RtlUnicodeToUTF8N(NULL, 0, &len, name, length);
766 if (!NT_SUCCESS(Status)) {
767 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
768 return Status;
769 }
770
771 if (len == 0) {
772 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
773 return STATUS_INTERNAL_ERROR;
774 }
775
776 utf8.MaximumLength = utf8.Length = len;
777 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
778
779 if (!utf8.Buffer) {
780 ERR("out of memory\n");
781 return STATUS_INSUFFICIENT_RESOURCES;
782 }
783
784 Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, name, length);
785 if (!NT_SUCCESS(Status)) {
786 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
787 goto end2;
788 }
789
790 acquire_tree_lock(Vcb, TRUE);
791
792 KeQuerySystemTime(&time);
793 win_time_to_unix(time, &now);
794
795 InitializeListHead(&rollback);
796
797 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
798
799 if (find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fcb->subvol, fcb->inode, NULL, NULL, NULL, NULL)) {
800 WARN("file already exists\n");
801 Status = STATUS_OBJECT_NAME_COLLISION;
802 goto end;
803 }
804
805 if (Vcb->root_root->lastinode == 0)
806 get_last_inode(Vcb, Vcb->root_root);
807
808 // FIXME - make sure rollback removes new roots from internal structures
809
810 id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
811 Status = create_root(Vcb, id, &r, FALSE, 0, &rollback);
812
813 if (!NT_SUCCESS(Status)) {
814 ERR("create_root returned %08x\n", Status);
815 goto end;
816 }
817
818 TRACE("created root %llx\n", id);
819
820 if (!Vcb->uuid_root) {
821 root* uuid_root;
822
823 TRACE("uuid root doesn't exist, creating it\n");
824
825 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
826
827 if (!NT_SUCCESS(Status)) {
828 ERR("create_root returned %08x\n", Status);
829 goto end;
830 }
831
832 Vcb->uuid_root = uuid_root;
833 }
834
835 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
836 if (!root_num) {
837 ERR("out of memory\n");
838 Status = STATUS_INSUFFICIENT_RESOURCES;
839 goto end;
840 }
841
842 tp.tree = NULL;
843
844 do {
845 get_uuid(&r->root_item.uuid);
846
847 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
848 searchkey.obj_type = TYPE_SUBVOL_UUID;
849 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
850
851 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
852 } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
853
854 *root_num = r->id;
855
856 if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
857 ERR("insert_tree_item failed\n");
858 Status = STATUS_INTERNAL_ERROR;
859 goto end;
860 }
861
862 r->root_item.inode.generation = 1;
863 r->root_item.inode.st_size = 3;
864 r->root_item.inode.st_blocks = Vcb->superblock.node_size;
865 r->root_item.inode.st_nlink = 1;
866 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
867 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
868
869 r->root_item.objid = SUBVOL_ROOT_INODE;
870 r->root_item.bytes_used = Vcb->superblock.node_size;
871 r->root_item.ctransid = Vcb->superblock.generation;
872 r->root_item.otransid = Vcb->superblock.generation;
873 r->root_item.ctime = now;
874 r->root_item.otime = now;
875
876 // add .. inode to new subvol
877
878 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
879 if (!ii) {
880 ERR("out of memory\n");
881 Status = STATUS_INSUFFICIENT_RESOURCES;
882 goto end;
883 }
884
885 RtlZeroMemory(ii, sizeof(INODE_ITEM));
886 ii->generation = Vcb->superblock.generation;
887 ii->transid = Vcb->superblock.generation;
888 ii->st_nlink = 1;
889 ii->st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
890 ii->st_atime = ii->st_ctime = ii->st_mtime = ii->otime = now;
891 ii->st_gid = GID_NOBODY; // FIXME?
892
893 SeCaptureSubjectContext(&subjcont);
894
895 Status = SeAssignSecurity(fcb->sd, NULL, (void**)&sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
896
897 if (!NT_SUCCESS(Status)) {
898 ERR("SeAssignSecurity returned %08x\n", Status);
899 goto end;
900 }
901
902 if (!sd) {
903 ERR("SeAssignSecurity returned NULL security descriptor\n");
904 Status = STATUS_INTERNAL_ERROR;
905 goto end;
906 }
907
908 Status = RtlGetOwnerSecurityDescriptor(sd, &owner, &defaulted);
909 if (!NT_SUCCESS(Status)) {
910 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
911 ii->st_uid = UID_NOBODY;
912 } else {
913 ii->st_uid = sid_to_uid(&owner);
914 }
915
916 if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, &rollback)) {
917 ERR("insert_tree_item failed\n");
918 Status = STATUS_INTERNAL_ERROR;
919 goto end;
920 }
921
922 // add security.NTACL xattr
923
924 Status = set_xattr(Vcb, r, r->root_item.objid, EA_NTACL, EA_NTACL_HASH, (UINT8*)sd, RtlLengthSecurityDescriptor(fcb->sd), &rollback);
925 if (!NT_SUCCESS(Status)) {
926 ERR("set_xattr returned %08x\n", Status);
927 goto end;
928 }
929
930 ExFreePool(sd);
931
932 // add INODE_REF
933
934 irsize = sizeof(INODE_REF) - 1 + strlen(DOTDOT);
935 ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
936 if (!ir) {
937 ERR("out of memory\n");
938 Status = STATUS_INSUFFICIENT_RESOURCES;
939 goto end;
940 }
941
942 ir->index = 0;
943 ir->n = strlen(DOTDOT);
944 RtlCopyMemory(ir->name, DOTDOT, ir->n);
945
946 if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, &rollback)) {
947 ERR("insert_tree_item failed\n");
948 Status = STATUS_INTERNAL_ERROR;
949 goto end;
950 }
951
952 // add DIR_ITEM
953
954 dirpos = find_next_dir_index(Vcb, fcb->subvol, fcb->inode);
955 if (dirpos == 0) {
956 ERR("find_next_dir_index failed\n");
957 Status = STATUS_INTERNAL_ERROR;
958 goto end;
959 }
960
961 disize = sizeof(DIR_ITEM) - 1 + utf8.Length;
962 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
963 if (!di) {
964 ERR("out of memory\n");
965 Status = STATUS_INSUFFICIENT_RESOURCES;
966 goto end;
967 }
968
969 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
970 if (!di2) {
971 ERR("out of memory\n");
972 Status = STATUS_INSUFFICIENT_RESOURCES;
973 ExFreePool(di);
974 goto end;
975 }
976
977 di->key.obj_id = id;
978 di->key.obj_type = TYPE_ROOT_ITEM;
979 di->key.offset = 0;
980 di->transid = Vcb->superblock.generation;
981 di->m = 0;
982 di->n = utf8.Length;
983 di->type = BTRFS_TYPE_DIRECTORY;
984 RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
985
986 RtlCopyMemory(di2, di, disize);
987
988 Status = add_dir_item(Vcb, fcb->subvol, fcb->inode, crc32, di, disize, &rollback);
989 if (!NT_SUCCESS(Status)) {
990 ERR("add_dir_item returned %08x\n", Status);
991 goto end;
992 }
993
994 // add DIR_INDEX
995
996 if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_DIR_INDEX, dirpos, di2, disize, NULL, &rollback)) {
997 ERR("insert_tree_item failed\n");
998 Status = STATUS_INTERNAL_ERROR;
999 goto end;
1000 }
1001
1002 // add ROOT_REF
1003
1004 rrsize = sizeof(ROOT_REF) - 1 + utf8.Length;
1005 rr = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
1006 if (!rr) {
1007 ERR("out of memory\n");
1008 Status = STATUS_INSUFFICIENT_RESOURCES;
1009 goto end;
1010 }
1011
1012 rr->dir = fcb->inode;
1013 rr->index = dirpos;
1014 rr->n = utf8.Length;
1015 RtlCopyMemory(rr->name, utf8.Buffer, utf8.Length);
1016
1017 if (!insert_tree_item(Vcb, Vcb->root_root, fcb->subvol->id, TYPE_ROOT_REF, r->id, rr, rrsize, NULL, &rollback)) {
1018 ERR("insert_tree_item failed\n");
1019 Status = STATUS_INTERNAL_ERROR;
1020 goto end;
1021 }
1022
1023 // add ROOT_BACKREF
1024
1025 rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
1026 if (!rr2) {
1027 ERR("out of memory\n");
1028 Status = STATUS_INSUFFICIENT_RESOURCES;
1029 goto end;
1030 }
1031
1032 RtlCopyMemory(rr2, rr, rrsize);
1033
1034 if (!insert_tree_item(Vcb, Vcb->root_root, r->id, TYPE_ROOT_BACKREF, fcb->subvol->id, rr2, rrsize, NULL, &rollback)) {
1035 ERR("insert_tree_item failed\n");
1036 Status = STATUS_INTERNAL_ERROR;
1037 goto end;
1038 }
1039
1040 // change fcb->subvol's ROOT_ITEM
1041
1042 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1043 fcb->subvol->root_item.ctime = now;
1044
1045 // change fcb's INODE_ITEM
1046
1047 // unlike when we create a file normally, the times and seq of the parent don't appear to change
1048 fcb->inode_item.transid = Vcb->superblock.generation;
1049 fcb->inode_item.st_size += utf8.Length * 2;
1050
1051 searchkey.obj_id = fcb->inode;
1052 searchkey.obj_type = TYPE_INODE_ITEM;
1053 searchkey.offset = 0;
1054
1055 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1056 if (!NT_SUCCESS(Status)) {
1057 ERR("error - find_item returned %08x\n", Status);
1058 goto end;
1059 }
1060
1061 if (keycmp(&searchkey, &tp.item->key)) {
1062 ERR("error - could not find INODE_ITEM for directory %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
1063 Status = STATUS_INTERNAL_ERROR;
1064 goto end;
1065 }
1066
1067 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1068 if (!ii) {
1069 ERR("out of memory\n");
1070 Status = STATUS_INSUFFICIENT_RESOURCES;
1071 goto end;
1072 }
1073
1074 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
1075 delete_tree_item(Vcb, &tp, &rollback);
1076
1077 insert_tree_item(Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ii, sizeof(INODE_ITEM), NULL, &rollback);
1078
1079 Vcb->root_root->lastinode = id;
1080
1081 Status = STATUS_SUCCESS;
1082
1083 end:
1084 if (NT_SUCCESS(Status))
1085 Status = consider_write(Vcb);
1086
1087 if (!NT_SUCCESS(Status))
1088 do_rollback(Vcb, &rollback);
1089 else
1090 clear_rollback(&rollback);
1091
1092 release_tree_lock(Vcb, TRUE);
1093
1094 if (NT_SUCCESS(Status)) {
1095 UNICODE_STRING ffn;
1096
1097 ffn.Length = fileref->full_filename.Length + length;
1098 if (fcb != fcb->Vcb->root_fileref->fcb)
1099 ffn.Length += sizeof(WCHAR);
1100
1101 ffn.MaximumLength = ffn.Length;
1102 ffn.Buffer = ExAllocatePoolWithTag(PagedPool, ffn.Length, ALLOC_TAG);
1103
1104 if (ffn.Buffer) {
1105 ULONG i;
1106
1107 RtlCopyMemory(ffn.Buffer, fileref->full_filename.Buffer, fileref->full_filename.Length);
1108 i = fileref->full_filename.Length;
1109
1110 if (fcb != fcb->Vcb->root_fileref->fcb) {
1111 ffn.Buffer[i / sizeof(WCHAR)] = '\\';
1112 i += sizeof(WCHAR);
1113 }
1114
1115 RtlCopyMemory(&ffn.Buffer[i / sizeof(WCHAR)], name, length);
1116
1117 TRACE("full filename = %.*S\n", ffn.Length / sizeof(WCHAR), ffn.Buffer);
1118
1119 FsRtlNotifyFullReportChange(Vcb->NotifySync, &Vcb->DirNotifyList, (PSTRING)&ffn, i, NULL, NULL,
1120 FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
1121
1122 ExFreePool(ffn.Buffer);
1123 } else
1124 ERR("out of memory\n");
1125 }
1126
1127 end2:
1128 if (utf8.Buffer)
1129 ExFreePool(utf8.Buffer);
1130
1131 return Status;
1132 }
1133
1134 static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) {
1135 UINT64 i, num_devices;
1136 NTSTATUS Status;
1137 ULONG cc;
1138 IO_STATUS_BLOCK iosb;
1139 BOOL verify = FALSE;
1140
1141 num_devices = Vcb->superblock.num_devices;
1142 for (i = 0; i < num_devices; i++) {
1143 if (Vcb->devices[i].devobj && Vcb->devices[i].removable) {
1144 Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), FALSE, &iosb);
1145
1146 if (iosb.Information != sizeof(ULONG))
1147 cc = 0;
1148
1149 if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != Vcb->devices[i].change_count)) {
1150 Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
1151 verify = TRUE;
1152 }
1153
1154 if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1155 Vcb->devices[i].change_count = cc;
1156
1157 if (!NT_SUCCESS(Status) || verify) {
1158 IoSetHardErrorOrVerifyDevice(Irp, Vcb->devices[i].devobj);
1159
1160 return verify ? STATUS_VERIFY_REQUIRED : Status;
1161 }
1162 }
1163 }
1164
1165 return STATUS_SUCCESS;
1166 }
1167
1168 static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
1169 FILESYSTEM_STATISTICS* fss;
1170
1171 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1172
1173 // This is hideously wrong, but at least it stops SMB from breaking
1174
1175 if (buflen < sizeof(FILESYSTEM_STATISTICS))
1176 return STATUS_BUFFER_TOO_SMALL;
1177
1178 fss = buffer;
1179 RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1180
1181 fss->Version = 1;
1182 fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
1183 fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS);
1184
1185 *retlen = sizeof(FILESYSTEM_STATISTICS);
1186
1187 return STATUS_SUCCESS;
1188 }
1189
1190 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
1191 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1192 NTSTATUS Status;
1193
1194 switch (type) {
1195 case FSCTL_REQUEST_OPLOCK_LEVEL_1:
1196 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
1197 Status = STATUS_NOT_IMPLEMENTED;
1198 break;
1199
1200 case FSCTL_REQUEST_OPLOCK_LEVEL_2:
1201 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
1202 Status = STATUS_NOT_IMPLEMENTED;
1203 break;
1204
1205 case FSCTL_REQUEST_BATCH_OPLOCK:
1206 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
1207 Status = STATUS_NOT_IMPLEMENTED;
1208 break;
1209
1210 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
1211 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
1212 Status = STATUS_NOT_IMPLEMENTED;
1213 break;
1214
1215 case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
1216 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
1217 Status = STATUS_NOT_IMPLEMENTED;
1218 break;
1219
1220 case FSCTL_OPLOCK_BREAK_NOTIFY:
1221 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
1222 Status = STATUS_NOT_IMPLEMENTED;
1223 break;
1224
1225 case FSCTL_LOCK_VOLUME:
1226 WARN("STUB: FSCTL_LOCK_VOLUME\n");
1227 Status = STATUS_NOT_IMPLEMENTED;
1228 break;
1229
1230 case FSCTL_UNLOCK_VOLUME:
1231 WARN("STUB: FSCTL_UNLOCK_VOLUME\n");
1232 Status = STATUS_NOT_IMPLEMENTED;
1233 break;
1234
1235 case FSCTL_DISMOUNT_VOLUME:
1236 WARN("STUB: FSCTL_DISMOUNT_VOLUME\n");
1237 Status = STATUS_NOT_IMPLEMENTED;
1238 break;
1239
1240 case FSCTL_IS_VOLUME_MOUNTED:
1241 Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp);
1242 break;
1243
1244 case FSCTL_IS_PATHNAME_VALID:
1245 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
1246 Status = STATUS_NOT_IMPLEMENTED;
1247 break;
1248
1249 case FSCTL_MARK_VOLUME_DIRTY:
1250 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
1251 Status = STATUS_NOT_IMPLEMENTED;
1252 break;
1253
1254 case FSCTL_QUERY_RETRIEVAL_POINTERS:
1255 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
1256 Status = STATUS_NOT_IMPLEMENTED;
1257 break;
1258
1259 case FSCTL_GET_COMPRESSION:
1260 WARN("STUB: FSCTL_GET_COMPRESSION\n");
1261 Status = STATUS_NOT_IMPLEMENTED;
1262 break;
1263
1264 case FSCTL_SET_COMPRESSION:
1265 WARN("STUB: FSCTL_SET_COMPRESSION\n");
1266 Status = STATUS_NOT_IMPLEMENTED;
1267 break;
1268
1269 case FSCTL_SET_BOOTLOADER_ACCESSED:
1270 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
1271 Status = STATUS_NOT_IMPLEMENTED;
1272 break;
1273
1274 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
1275 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
1276 Status = STATUS_NOT_IMPLEMENTED;
1277 break;
1278
1279 case FSCTL_INVALIDATE_VOLUMES:
1280 WARN("STUB: FSCTL_INVALIDATE_VOLUMES\n");
1281 Status = STATUS_NOT_IMPLEMENTED;
1282 break;
1283
1284 case FSCTL_QUERY_FAT_BPB:
1285 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
1286 Status = STATUS_NOT_IMPLEMENTED;
1287 break;
1288
1289 case FSCTL_REQUEST_FILTER_OPLOCK:
1290 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
1291 Status = STATUS_NOT_IMPLEMENTED;
1292 break;
1293
1294 case FSCTL_FILESYSTEM_GET_STATISTICS:
1295 Status = fs_get_statistics(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
1296 IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
1297 break;
1298
1299 case FSCTL_GET_NTFS_VOLUME_DATA:
1300 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
1301 Status = STATUS_NOT_IMPLEMENTED;
1302 break;
1303
1304 case FSCTL_GET_NTFS_FILE_RECORD:
1305 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
1306 Status = STATUS_NOT_IMPLEMENTED;
1307 break;
1308
1309 case FSCTL_GET_VOLUME_BITMAP:
1310 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
1311 Status = STATUS_NOT_IMPLEMENTED;
1312 break;
1313
1314 case FSCTL_GET_RETRIEVAL_POINTERS:
1315 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
1316 Status = STATUS_NOT_IMPLEMENTED;
1317 break;
1318
1319 case FSCTL_MOVE_FILE:
1320 WARN("STUB: FSCTL_MOVE_FILE\n");
1321 Status = STATUS_NOT_IMPLEMENTED;
1322 break;
1323
1324 case FSCTL_IS_VOLUME_DIRTY:
1325 WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
1326 Status = STATUS_NOT_IMPLEMENTED;
1327 break;
1328
1329 case FSCTL_ALLOW_EXTENDED_DASD_IO:
1330 WARN("STUB: FSCTL_ALLOW_EXTENDED_DASD_IO\n");
1331 Status = STATUS_NOT_IMPLEMENTED;
1332 break;
1333
1334 case FSCTL_FIND_FILES_BY_SID:
1335 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
1336 Status = STATUS_NOT_IMPLEMENTED;
1337 break;
1338
1339 case FSCTL_SET_OBJECT_ID:
1340 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
1341 Status = STATUS_NOT_IMPLEMENTED;
1342 break;
1343
1344 case FSCTL_GET_OBJECT_ID:
1345 WARN("STUB: FSCTL_GET_OBJECT_ID\n");
1346 Status = STATUS_NOT_IMPLEMENTED;
1347 break;
1348
1349 case FSCTL_DELETE_OBJECT_ID:
1350 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
1351 Status = STATUS_NOT_IMPLEMENTED;
1352 break;
1353
1354 case FSCTL_SET_REPARSE_POINT:
1355 Status = set_reparse_point(DeviceObject, Irp);
1356 break;
1357
1358 case FSCTL_GET_REPARSE_POINT:
1359 Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
1360 IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
1361 break;
1362
1363 case FSCTL_DELETE_REPARSE_POINT:
1364 Status = delete_reparse_point(DeviceObject, Irp);
1365 break;
1366
1367 case FSCTL_ENUM_USN_DATA:
1368 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
1369 Status = STATUS_NOT_IMPLEMENTED;
1370 break;
1371
1372 case FSCTL_SECURITY_ID_CHECK:
1373 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
1374 Status = STATUS_NOT_IMPLEMENTED;
1375 break;
1376
1377 case FSCTL_READ_USN_JOURNAL:
1378 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
1379 Status = STATUS_NOT_IMPLEMENTED;
1380 break;
1381
1382 case FSCTL_SET_OBJECT_ID_EXTENDED:
1383 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
1384 Status = STATUS_NOT_IMPLEMENTED;
1385 break;
1386
1387 case FSCTL_CREATE_OR_GET_OBJECT_ID:
1388 WARN("STUB: FSCTL_CREATE_OR_GET_OBJECT_ID\n");
1389 Status = STATUS_NOT_IMPLEMENTED;
1390 break;
1391
1392 case FSCTL_SET_SPARSE:
1393 WARN("STUB: FSCTL_SET_SPARSE\n");
1394 Status = STATUS_NOT_IMPLEMENTED;
1395 break;
1396
1397 case FSCTL_SET_ZERO_DATA:
1398 WARN("STUB: FSCTL_SET_ZERO_DATA\n");
1399 Status = STATUS_NOT_IMPLEMENTED;
1400 break;
1401
1402 case FSCTL_QUERY_ALLOCATED_RANGES:
1403 WARN("STUB: FSCTL_QUERY_ALLOCATED_RANGES\n");
1404 Status = STATUS_NOT_IMPLEMENTED;
1405 break;
1406
1407 case FSCTL_ENABLE_UPGRADE:
1408 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
1409 Status = STATUS_NOT_IMPLEMENTED;
1410 break;
1411
1412 case FSCTL_SET_ENCRYPTION:
1413 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
1414 Status = STATUS_NOT_IMPLEMENTED;
1415 break;
1416
1417 case FSCTL_ENCRYPTION_FSCTL_IO:
1418 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
1419 Status = STATUS_NOT_IMPLEMENTED;
1420 break;
1421
1422 case FSCTL_WRITE_RAW_ENCRYPTED:
1423 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
1424 Status = STATUS_NOT_IMPLEMENTED;
1425 break;
1426
1427 case FSCTL_READ_RAW_ENCRYPTED:
1428 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
1429 Status = STATUS_NOT_IMPLEMENTED;
1430 break;
1431
1432 case FSCTL_CREATE_USN_JOURNAL:
1433 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
1434 Status = STATUS_NOT_IMPLEMENTED;
1435 break;
1436
1437 case FSCTL_READ_FILE_USN_DATA:
1438 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
1439 Status = STATUS_NOT_IMPLEMENTED;
1440 break;
1441
1442 case FSCTL_WRITE_USN_CLOSE_RECORD:
1443 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
1444 Status = STATUS_NOT_IMPLEMENTED;
1445 break;
1446
1447 case FSCTL_EXTEND_VOLUME:
1448 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
1449 Status = STATUS_NOT_IMPLEMENTED;
1450 break;
1451
1452 case FSCTL_QUERY_USN_JOURNAL:
1453 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
1454 Status = STATUS_NOT_IMPLEMENTED;
1455 break;
1456
1457 case FSCTL_DELETE_USN_JOURNAL:
1458 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
1459 Status = STATUS_NOT_IMPLEMENTED;
1460 break;
1461
1462 case FSCTL_MARK_HANDLE:
1463 WARN("STUB: FSCTL_MARK_HANDLE\n");
1464 Status = STATUS_NOT_IMPLEMENTED;
1465 break;
1466
1467 case FSCTL_SIS_COPYFILE:
1468 WARN("STUB: FSCTL_SIS_COPYFILE\n");
1469 Status = STATUS_NOT_IMPLEMENTED;
1470 break;
1471
1472 case FSCTL_SIS_LINK_FILES:
1473 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
1474 Status = STATUS_NOT_IMPLEMENTED;
1475 break;
1476
1477 case FSCTL_RECALL_FILE:
1478 WARN("STUB: FSCTL_RECALL_FILE\n");
1479 Status = STATUS_NOT_IMPLEMENTED;
1480 break;
1481
1482 case FSCTL_READ_FROM_PLEX:
1483 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
1484 Status = STATUS_NOT_IMPLEMENTED;
1485 break;
1486
1487 case FSCTL_FILE_PREFETCH:
1488 WARN("STUB: FSCTL_FILE_PREFETCH\n");
1489 Status = STATUS_NOT_IMPLEMENTED;
1490 break;
1491
1492 #if WIN32_WINNT >= 0x0600
1493 case FSCTL_MAKE_MEDIA_COMPATIBLE:
1494 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
1495 Status = STATUS_NOT_IMPLEMENTED;
1496 break;
1497
1498 case FSCTL_SET_DEFECT_MANAGEMENT:
1499 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
1500 Status = STATUS_NOT_IMPLEMENTED;
1501 break;
1502
1503 case FSCTL_QUERY_SPARING_INFO:
1504 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
1505 Status = STATUS_NOT_IMPLEMENTED;
1506 break;
1507
1508 case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
1509 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
1510 Status = STATUS_NOT_IMPLEMENTED;
1511 break;
1512
1513 case FSCTL_SET_VOLUME_COMPRESSION_STATE:
1514 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
1515 Status = STATUS_NOT_IMPLEMENTED;
1516 break;
1517
1518 case FSCTL_TXFS_MODIFY_RM:
1519 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
1520 Status = STATUS_NOT_IMPLEMENTED;
1521 break;
1522
1523 case FSCTL_TXFS_QUERY_RM_INFORMATION:
1524 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
1525 Status = STATUS_NOT_IMPLEMENTED;
1526 break;
1527
1528 case FSCTL_TXFS_ROLLFORWARD_REDO:
1529 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
1530 Status = STATUS_NOT_IMPLEMENTED;
1531 break;
1532
1533 case FSCTL_TXFS_ROLLFORWARD_UNDO:
1534 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
1535 Status = STATUS_NOT_IMPLEMENTED;
1536 break;
1537
1538 case FSCTL_TXFS_START_RM:
1539 WARN("STUB: FSCTL_TXFS_START_RM\n");
1540 Status = STATUS_NOT_IMPLEMENTED;
1541 break;
1542
1543 case FSCTL_TXFS_SHUTDOWN_RM:
1544 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
1545 Status = STATUS_NOT_IMPLEMENTED;
1546 break;
1547
1548 case FSCTL_TXFS_READ_BACKUP_INFORMATION:
1549 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
1550 Status = STATUS_NOT_IMPLEMENTED;
1551 break;
1552
1553 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
1554 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
1555 Status = STATUS_NOT_IMPLEMENTED;
1556 break;
1557
1558 case FSCTL_TXFS_CREATE_SECONDARY_RM:
1559 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
1560 Status = STATUS_NOT_IMPLEMENTED;
1561 break;
1562
1563 case FSCTL_TXFS_GET_METADATA_INFO:
1564 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
1565 Status = STATUS_NOT_IMPLEMENTED;
1566 break;
1567
1568 case FSCTL_TXFS_GET_TRANSACTED_VERSION:
1569 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
1570 Status = STATUS_NOT_IMPLEMENTED;
1571 break;
1572
1573 case FSCTL_TXFS_SAVEPOINT_INFORMATION:
1574 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
1575 Status = STATUS_NOT_IMPLEMENTED;
1576 break;
1577
1578 case FSCTL_TXFS_CREATE_MINIVERSION:
1579 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
1580 Status = STATUS_NOT_IMPLEMENTED;
1581 break;
1582
1583 case FSCTL_TXFS_TRANSACTION_ACTIVE:
1584 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
1585 Status = STATUS_NOT_IMPLEMENTED;
1586 break;
1587
1588 case FSCTL_SET_ZERO_ON_DEALLOCATION:
1589 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
1590 Status = STATUS_NOT_IMPLEMENTED;
1591 break;
1592
1593 case FSCTL_SET_REPAIR:
1594 WARN("STUB: FSCTL_SET_REPAIR\n");
1595 Status = STATUS_NOT_IMPLEMENTED;
1596 break;
1597
1598 case FSCTL_GET_REPAIR:
1599 WARN("STUB: FSCTL_GET_REPAIR\n");
1600 Status = STATUS_NOT_IMPLEMENTED;
1601 break;
1602
1603 case FSCTL_WAIT_FOR_REPAIR:
1604 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
1605 Status = STATUS_NOT_IMPLEMENTED;
1606 break;
1607
1608 case FSCTL_INITIATE_REPAIR:
1609 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
1610 Status = STATUS_NOT_IMPLEMENTED;
1611 break;
1612
1613 case FSCTL_CSC_INTERNAL:
1614 WARN("STUB: FSCTL_CSC_INTERNAL\n");
1615 Status = STATUS_NOT_IMPLEMENTED;
1616 break;
1617
1618 case FSCTL_SHRINK_VOLUME:
1619 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
1620 Status = STATUS_NOT_IMPLEMENTED;
1621 break;
1622
1623 case FSCTL_SET_SHORT_NAME_BEHAVIOR:
1624 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
1625 Status = STATUS_NOT_IMPLEMENTED;
1626 break;
1627
1628 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
1629 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
1630 Status = STATUS_NOT_IMPLEMENTED;
1631 break;
1632
1633 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
1634 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
1635 Status = STATUS_NOT_IMPLEMENTED;
1636 break;
1637
1638 case FSCTL_TXFS_LIST_TRANSACTIONS:
1639 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
1640 Status = STATUS_NOT_IMPLEMENTED;
1641 break;
1642
1643 case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
1644 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
1645 Status = STATUS_NOT_IMPLEMENTED;
1646 break;
1647
1648 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
1649 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
1650 Status = STATUS_NOT_IMPLEMENTED;
1651 break;
1652
1653 case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
1654 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
1655 Status = STATUS_NOT_IMPLEMENTED;
1656 break;
1657
1658 case FSCTL_CSV_CONTROL:
1659 WARN("STUB: FSCTL_CSV_CONTROL\n");
1660 Status = STATUS_NOT_IMPLEMENTED;
1661 break;
1662 #endif
1663 case FSCTL_BTRFS_GET_FILE_IDS:
1664 Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
1665 break;
1666
1667 case FSCTL_BTRFS_CREATE_SUBVOL:
1668 Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
1669 break;
1670
1671 case FSCTL_BTRFS_CREATE_SNAPSHOT:
1672 Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
1673 break;
1674
1675 default:
1676 TRACE("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
1677 IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
1678 (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
1679 IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
1680 Status = STATUS_NOT_IMPLEMENTED;
1681 break;
1682 }
1683
1684 return Status;
1685 }