[REISERFS]
[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 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
31
32 extern LIST_ENTRY VcbList;
33 extern ERESOURCE global_loading_lock;
34 extern LIST_ENTRY volumes;
35 extern ERESOURCE volumes_lock;
36
37 static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
38 btrfs_get_file_ids* bgfi;
39 fcb* fcb;
40
41 if (length < sizeof(btrfs_get_file_ids))
42 return STATUS_BUFFER_OVERFLOW;
43
44 if (!FileObject)
45 return STATUS_INVALID_PARAMETER;
46
47 fcb = FileObject->FsContext;
48
49 if (!fcb)
50 return STATUS_INVALID_PARAMETER;
51
52 bgfi = data;
53
54 bgfi->subvol = fcb->subvol->id;
55 bgfi->inode = fcb->inode;
56 bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
57
58 return STATUS_SUCCESS;
59 }
60
61 static void get_uuid(BTRFS_UUID* uuid) {
62 LARGE_INTEGER seed;
63 UINT8 i;
64
65 seed = KeQueryPerformanceCounter(NULL);
66
67 for (i = 0; i < 16; i+=2) {
68 ULONG rand = RtlRandomEx(&seed.LowPart);
69
70 uuid->uuid[i] = (rand & 0xff00) >> 8;
71 uuid->uuid[i+1] = rand & 0xff;
72 }
73 }
74
75 static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
76 UINT8* buf;
77 NTSTATUS Status;
78 write_data_context* wtc;
79 LIST_ENTRY* le;
80 tree t;
81 tree_header* th;
82 chunk* c;
83
84 buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
85 if (!buf) {
86 ERR("out of memory\n");
87 return STATUS_INSUFFICIENT_RESOURCES;
88 }
89
90 wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
91 if (!wtc) {
92 ERR("out of memory\n");
93 ExFreePool(buf);
94 return STATUS_INSUFFICIENT_RESOURCES;
95 }
96
97 Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL, Irp, FALSE);
98 if (!NT_SUCCESS(Status)) {
99 ERR("read_data returned %08x\n", Status);
100 goto end;
101 }
102
103 th = (tree_header*)buf;
104
105 RtlZeroMemory(&t, sizeof(tree));
106 t.root = subvol;
107 t.header.level = th->level;
108 t.header.tree_id = t.root->id;
109
110 Status = get_tree_new_address(Vcb, &t, Irp, rollback);
111 if (!NT_SUCCESS(Status)) {
112 ERR("get_tree_new_address returned %08x\n", Status);
113 goto end;
114 }
115
116 if (!t.has_new_address) {
117 ERR("tree new address not set\n");
118 Status = STATUS_INTERNAL_ERROR;
119 goto end;
120 }
121
122 c = get_chunk_from_address(Vcb, t.new_address);
123
124 if (c) {
125 increase_chunk_usage(c, Vcb->superblock.node_size);
126 } else {
127 ERR("could not find chunk for address %llx\n", t.new_address);
128 Status = STATUS_INTERNAL_ERROR;
129 goto end;
130 }
131
132 th->address = t.new_address;
133 th->tree_id = subvol->id;
134 th->generation = Vcb->superblock.generation;
135 th->fs_uuid = Vcb->superblock.uuid;
136
137 if (th->level == 0) {
138 UINT32 i;
139 leaf_node* ln = (leaf_node*)&th[1];
140
141 for (i = 0; i < th->num_items; i++) {
142 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)) {
143 EXTENT_DATA* ed = (EXTENT_DATA*)(((UINT8*)&th[1]) + ln[i].offset);
144
145 if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
146 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
147
148 if (ed2->size != 0) { // not sparse
149 Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp, rollback);
150
151 if (!NT_SUCCESS(Status)) {
152 ERR("increase_extent_refcount_data returned %08x\n", Status);
153 goto end;
154 }
155 }
156 }
157 }
158 }
159 } else {
160 UINT32 i;
161 internal_node* in = (internal_node*)&th[1];
162
163 for (i = 0; i < th->num_items; i++) {
164 TREE_BLOCK_REF tbr;
165
166 tbr.offset = subvol->id;
167
168 Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp, rollback);
169 if (!NT_SUCCESS(Status)) {
170 ERR("increase_extent_refcount returned %08x\n", Status);
171 goto end;
172 }
173 }
174 }
175
176 *((UINT32*)buf) = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
177
178 KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
179 InitializeListHead(&wtc->stripes);
180 wtc->tree = TRUE;
181 wtc->stripes_left = 0;
182
183 Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL);
184 if (!NT_SUCCESS(Status)) {
185 ERR("write_data returned %08x\n", Status);
186 goto end;
187 }
188
189 if (wtc->stripes.Flink != &wtc->stripes) {
190 // launch writes and wait
191 le = wtc->stripes.Flink;
192 while (le != &wtc->stripes) {
193 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
194
195 if (stripe->status != WriteDataStatus_Ignore)
196 IoCallDriver(stripe->device->devobj, stripe->Irp);
197
198 le = le->Flink;
199 }
200
201 KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
202
203 le = wtc->stripes.Flink;
204 while (le != &wtc->stripes) {
205 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
206
207 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
208 Status = stripe->iosb.Status;
209 break;
210 }
211
212 le = le->Flink;
213 }
214
215 free_write_data_stripes(wtc);
216 buf = NULL;
217 }
218
219 if (NT_SUCCESS(Status))
220 *newaddr = t.new_address;
221
222 end:
223 ExFreePool(wtc);
224
225 if (buf)
226 ExFreePool(buf);
227
228 return Status;
229 }
230
231 static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) {
232 LIST_ENTRY* le = subvol->fcbs.Flink;
233
234 if (IsListEmpty(&subvol->fcbs))
235 return;
236
237 while (le != &subvol->fcbs) {
238 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
239 IO_STATUS_BLOCK iosb;
240
241 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
242 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
243
244 le = le->Flink;
245 }
246 }
247
248 static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, PIRP Irp) {
249 LIST_ENTRY rollback;
250 UINT64 id;
251 NTSTATUS Status;
252 root *r, *subvol = subvol_fcb->subvol;
253 KEY searchkey;
254 traverse_ptr tp;
255 UINT64 address, dirpos, *root_num;
256 LARGE_INTEGER time;
257 BTRFS_TIME now;
258 fcb* fcb = parent->FsContext;
259 ccb* ccb = parent->FsContext2;
260 LIST_ENTRY* le;
261 file_ref *fileref, *fr;
262 dir_child* dc = NULL;
263
264 if (!ccb) {
265 ERR("error - ccb was NULL\n");
266 return STATUS_INTERNAL_ERROR;
267 }
268
269 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
270 WARN("insufficient privileges\n");
271 return STATUS_ACCESS_DENIED;
272 }
273
274 fileref = ccb->fileref;
275
276 InitializeListHead(&rollback);
277
278 // flush open files on this subvol
279
280 flush_subvol_fcbs(subvol, &rollback);
281
282 // flush metadata
283
284 if (Vcb->need_write)
285 do_write(Vcb, Irp, &rollback);
286
287 free_trees(Vcb);
288
289 clear_rollback(Vcb, &rollback);
290
291 InitializeListHead(&rollback);
292
293 // create new root
294
295 id = InterlockedIncrement64(&Vcb->root_root->lastinode);
296 Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback);
297
298 if (!NT_SUCCESS(Status)) {
299 ERR("create_root returned %08x\n", Status);
300 goto end;
301 }
302
303 r->lastinode = subvol->lastinode;
304
305 if (!Vcb->uuid_root) {
306 root* uuid_root;
307
308 TRACE("uuid root doesn't exist, creating it\n");
309
310 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
311
312 if (!NT_SUCCESS(Status)) {
313 ERR("create_root returned %08x\n", Status);
314 goto end;
315 }
316
317 Vcb->uuid_root = uuid_root;
318 }
319
320 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
321 if (!root_num) {
322 ERR("out of memory\n");
323 Status = STATUS_INSUFFICIENT_RESOURCES;
324 goto end;
325 }
326
327 tp.tree = NULL;
328
329 do {
330 get_uuid(&r->root_item.uuid);
331
332 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
333 searchkey.obj_type = TYPE_SUBVOL_UUID;
334 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
335
336 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
337 } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
338
339 *root_num = r->id;
340
341 if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
342 ERR("insert_tree_item failed\n");
343 ExFreePool(root_num);
344 Status = STATUS_INTERNAL_ERROR;
345 goto end;
346 }
347
348 searchkey.obj_id = r->id;
349 searchkey.obj_type = TYPE_ROOT_ITEM;
350 searchkey.offset = 0xffffffffffffffff;
351
352 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
353 if (!NT_SUCCESS(Status)) {
354 ERR("error - find_item returned %08x\n", Status);
355 goto end;
356 }
357
358 Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, &address, Irp, &rollback);
359 if (!NT_SUCCESS(Status)) {
360 ERR("snapshot_tree_copy returned %08x\n", Status);
361 goto end;
362 }
363
364 KeQuerySystemTime(&time);
365 win_time_to_unix(time, &now);
366
367 r->root_item.inode.generation = 1;
368 r->root_item.inode.st_size = 3;
369 r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
370 r->root_item.inode.st_nlink = 1;
371 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
372 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
373 r->root_item.generation = Vcb->superblock.generation;
374 r->root_item.objid = subvol->root_item.objid;
375 r->root_item.block_number = address;
376 r->root_item.bytes_used = subvol->root_item.bytes_used;
377 r->root_item.last_snapshot_generation = Vcb->superblock.generation;
378 r->root_item.root_level = subvol->root_item.root_level;
379 r->root_item.generation2 = Vcb->superblock.generation;
380 r->root_item.parent_uuid = subvol->root_item.uuid;
381 r->root_item.ctransid = subvol->root_item.ctransid;
382 r->root_item.otransid = Vcb->superblock.generation;
383 r->root_item.ctime = subvol->root_item.ctime;
384 r->root_item.otime = now;
385
386 r->treeholder.address = address;
387
388 // FIXME - do we need to copy over the send and receive fields too?
389
390 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
391 ERR("error - could not find ROOT_ITEM for subvol %llx\n", r->id);
392 Status = STATUS_INTERNAL_ERROR;
393 goto end;
394 }
395
396 RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM));
397
398 // update ROOT_ITEM of original subvol
399
400 subvol->root_item.last_snapshot_generation = Vcb->superblock.generation;
401
402 // We also rewrite the top of the old subvolume tree, for some reason
403 searchkey.obj_id = 0;
404 searchkey.obj_type = 0;
405 searchkey.offset = 0;
406
407 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
408 if (!NT_SUCCESS(Status)) {
409 ERR("error - find_item returned %08x\n", Status);
410 goto end;
411 }
412
413 subvol->treeholder.tree->write = TRUE;
414
415 // create fileref for entry in other subvolume
416
417 fr = create_fileref();
418 if (!fr) {
419 ERR("out of memory\n");
420 Status = STATUS_INSUFFICIENT_RESOURCES;
421 goto end;
422 }
423
424 fr->utf8.Length = fr->utf8.MaximumLength = utf8->Length;
425 fr->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, fr->utf8.MaximumLength, ALLOC_TAG);
426 if (!fr->utf8.Buffer) {
427 ERR("out of memory\n");
428 free_fileref(fr);
429 Status = STATUS_INSUFFICIENT_RESOURCES;
430 goto end;
431 }
432
433 RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length);
434
435 Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, PagedPool, Irp);
436 if (!NT_SUCCESS(Status)) {
437 ERR("open_fcb returned %08x\n", Status);
438 free_fileref(fr);
439 goto end;
440 }
441
442 Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
443 if (!NT_SUCCESS(Status)) {
444 ERR("fcb_get_last_dir_index returned %08x\n", Status);
445 free_fileref(fr);
446 goto end;
447 }
448
449 fr->index = dirpos;
450
451 fr->filepart.MaximumLength = fr->filepart.Length = name->Length;
452
453 fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
454 if (!fr->filepart.Buffer) {
455 ERR("out of memory\n");
456 free_fileref(fr);
457 Status = STATUS_INSUFFICIENT_RESOURCES;
458 goto end;
459 }
460
461 RtlCopyMemory(fr->filepart.Buffer, name->Buffer, name->Length);
462
463 Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
464 if (!NT_SUCCESS(Status)) {
465 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
466 free_fileref(fr);
467 goto end;
468 }
469
470 fr->parent = fileref;
471
472 Status = add_dir_child(fileref->fcb, r->id, TRUE, dirpos, utf8, &fr->filepart, &fr->filepart_uc, BTRFS_TYPE_DIRECTORY, &dc);
473 if (!NT_SUCCESS(Status))
474 WARN("add_dir_child returned %08x\n", Status);
475
476 fr->dc = dc;
477 dc->fileref = fr;
478
479 insert_fileref_child(fileref, fr, TRUE);
480 increase_fileref_refcount(fileref);
481
482 fr->created = TRUE;
483 mark_fileref_dirty(fr);
484
485 if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
486 fr->fcb->fileref = fr;
487
488 free_fileref(fr);
489
490 // change fcb's INODE_ITEM
491
492 fcb->inode_item.transid = Vcb->superblock.generation;
493 fcb->inode_item.sequence++;
494 fcb->inode_item.st_size += utf8->Length * 2;
495
496 if (!ccb->user_set_change_time)
497 fcb->inode_item.st_ctime = now;
498
499 if (!ccb->user_set_write_time)
500 fcb->inode_item.st_mtime = now;
501
502 fcb->inode_item_changed = TRUE;
503 mark_fcb_dirty(fcb);
504
505 fcb->subvol->root_item.ctime = now;
506 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
507
508 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED);
509 send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
510
511 le = subvol->fcbs.Flink;
512 while (le != &subvol->fcbs) {
513 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
514 LIST_ENTRY* le2 = fcb2->extents.Flink;
515
516 while (le2 != &fcb2->extents) {
517 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
518
519 if (!ext->ignore)
520 ext->unique = FALSE;
521
522 le2 = le2->Flink;
523 }
524
525 le = le->Flink;
526 }
527
528 do_write(Vcb, Irp, &rollback);
529
530 free_trees(Vcb);
531
532 Status = STATUS_SUCCESS;
533
534 end:
535 if (NT_SUCCESS(Status))
536 clear_rollback(Vcb, &rollback);
537 else
538 do_rollback(Vcb, &rollback);
539
540 return Status;
541 }
542
543 static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
544 PFILE_OBJECT subvol_obj;
545 NTSTATUS Status;
546 btrfs_create_snapshot* bcs = data;
547 fcb* subvol_fcb;
548 ANSI_STRING utf8;
549 UNICODE_STRING nameus;
550 ULONG len;
551 fcb* fcb;
552 ccb* ccb;
553 file_ref *fileref, *fr2;
554
555 if (length < offsetof(btrfs_create_snapshot, name))
556 return STATUS_INVALID_PARAMETER;
557
558 if (length < offsetof(btrfs_create_snapshot, name) + bcs->namelen)
559 return STATUS_INVALID_PARAMETER;
560
561 if (!bcs->subvol)
562 return STATUS_INVALID_PARAMETER;
563
564 if (!FileObject || !FileObject->FsContext)
565 return STATUS_INVALID_PARAMETER;
566
567 fcb = FileObject->FsContext;
568 ccb = FileObject->FsContext2;
569
570 if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
571 return STATUS_INVALID_PARAMETER;
572
573 fileref = ccb->fileref;
574
575 if (!fileref) {
576 ERR("fileref was NULL\n");
577 return STATUS_INVALID_PARAMETER;
578 }
579
580 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
581 WARN("insufficient privileges\n");
582 return STATUS_ACCESS_DENIED;
583 }
584
585 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
586 return STATUS_ACCESS_DENIED;
587
588 nameus.Buffer = bcs->name;
589 nameus.Length = nameus.MaximumLength = bcs->namelen;
590
591 if (!is_file_name_valid(&nameus))
592 return STATUS_OBJECT_NAME_INVALID;
593
594 utf8.Buffer = NULL;
595
596 Status = RtlUnicodeToUTF8N(NULL, 0, &len, bcs->name, bcs->namelen);
597 if (!NT_SUCCESS(Status)) {
598 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
599 return Status;
600 }
601
602 if (len == 0) {
603 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
604 return STATUS_INTERNAL_ERROR;
605 }
606
607 utf8.MaximumLength = utf8.Length = len;
608 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
609
610 if (!utf8.Buffer) {
611 ERR("out of memory\n");
612 return STATUS_INSUFFICIENT_RESOURCES;
613 }
614
615 Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, bcs->name, bcs->namelen);
616 if (!NT_SUCCESS(Status)) {
617 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
618 goto end2;
619 }
620
621 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
622
623 // no need for fcb_lock as we have tree_lock exclusively
624 Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, PagedPool, FALSE, Irp);
625
626 if (NT_SUCCESS(Status)) {
627 if (!fr2->deleted) {
628 WARN("file already exists\n");
629 free_fileref(fr2);
630 Status = STATUS_OBJECT_NAME_COLLISION;
631 goto end3;
632 } else
633 free_fileref(fr2);
634 } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
635 ERR("open_fileref returned %08x\n", Status);
636 goto end3;
637 }
638
639 Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL);
640 if (!NT_SUCCESS(Status)) {
641 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
642 goto end3;
643 }
644
645 subvol_fcb = subvol_obj->FsContext;
646 if (!subvol_fcb) {
647 Status = STATUS_INVALID_PARAMETER;
648 goto end;
649 }
650
651 if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
652 WARN("handle inode was %llx, expected %llx\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
653 Status = STATUS_INVALID_PARAMETER;
654 goto end;
655 }
656
657 ccb = subvol_obj->FsContext2;
658
659 if (!ccb) {
660 Status = STATUS_INVALID_PARAMETER;
661 goto end;
662 }
663
664 if (!(ccb->access & FILE_TRAVERSE)) {
665 WARN("insufficient privileges\n");
666 Status = STATUS_ACCESS_DENIED;
667 goto end;
668 }
669
670 // clear unique flag on extents of open files in subvol
671 if (!IsListEmpty(&subvol_fcb->subvol->fcbs)) {
672 LIST_ENTRY* le = subvol_fcb->subvol->fcbs.Flink;
673
674 while (le != &subvol_fcb->subvol->fcbs) {
675 struct _fcb* openfcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
676 LIST_ENTRY* le2;
677
678 le2 = openfcb->extents.Flink;
679
680 while (le2 != &openfcb->extents) {
681 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
682
683 ext->unique = FALSE;
684
685 le2 = le2->Flink;
686 }
687
688 le = le->Flink;
689 }
690 }
691
692 Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, Irp);
693
694 if (NT_SUCCESS(Status)) {
695 file_ref* fr;
696
697 Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL, NULL, PagedPool, FALSE, Irp);
698
699 if (!NT_SUCCESS(Status)) {
700 ERR("open_fileref returned %08x\n", Status);
701 Status = STATUS_SUCCESS;
702 } else {
703 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED);
704 free_fileref(fr);
705 }
706 }
707
708 end:
709 ObDereferenceObject(subvol_obj);
710
711 end3:
712 ExReleaseResourceLite(&Vcb->tree_lock);
713
714 end2:
715 ExFreePool(utf8.Buffer);
716
717 return Status;
718 }
719
720 static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length, PIRP Irp) {
721 fcb *fcb, *rootfcb;
722 ccb* ccb;
723 file_ref* fileref;
724 NTSTATUS Status;
725 LIST_ENTRY rollback;
726 UINT64 id;
727 root* r;
728 LARGE_INTEGER time;
729 BTRFS_TIME now;
730 ULONG len, irsize;
731 UNICODE_STRING nameus;
732 ANSI_STRING utf8;
733 UINT64 dirpos;
734 INODE_REF* ir;
735 KEY searchkey;
736 traverse_ptr tp;
737 SECURITY_SUBJECT_CONTEXT subjcont;
738 PSID owner;
739 BOOLEAN defaulted;
740 UINT64* root_num;
741 file_ref *fr = NULL, *fr2;
742 dir_child* dc = NULL;
743
744 fcb = FileObject->FsContext;
745 if (!fcb) {
746 ERR("error - fcb was NULL\n");
747 return STATUS_INTERNAL_ERROR;
748 }
749
750 ccb = FileObject->FsContext2;
751 if (!ccb) {
752 ERR("error - ccb was NULL\n");
753 return STATUS_INTERNAL_ERROR;
754 }
755
756 fileref = ccb->fileref;
757
758 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
759 ERR("parent FCB was not a directory\n");
760 return STATUS_NOT_A_DIRECTORY;
761 }
762
763 if (!fileref) {
764 ERR("fileref was NULL\n");
765 return STATUS_INVALID_PARAMETER;
766 }
767
768 if (fileref->deleted || fcb->deleted) {
769 ERR("parent has been deleted\n");
770 return STATUS_FILE_DELETED;
771 }
772
773 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
774 WARN("insufficient privileges\n");
775 return STATUS_ACCESS_DENIED;
776 }
777
778 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
779 return STATUS_ACCESS_DENIED;
780
781 nameus.Length = nameus.MaximumLength = length;
782 nameus.Buffer = name;
783
784 if (!is_file_name_valid(&nameus))
785 return STATUS_OBJECT_NAME_INVALID;
786
787 utf8.Buffer = NULL;
788
789 Status = RtlUnicodeToUTF8N(NULL, 0, &len, name, length);
790 if (!NT_SUCCESS(Status)) {
791 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
792 return Status;
793 }
794
795 if (len == 0) {
796 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
797 return STATUS_INTERNAL_ERROR;
798 }
799
800 utf8.MaximumLength = utf8.Length = len;
801 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
802
803 if (!utf8.Buffer) {
804 ERR("out of memory\n");
805 return STATUS_INSUFFICIENT_RESOURCES;
806 }
807
808 Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, name, length);
809 if (!NT_SUCCESS(Status)) {
810 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
811 goto end2;
812 }
813
814 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
815
816 KeQuerySystemTime(&time);
817 win_time_to_unix(time, &now);
818
819 InitializeListHead(&rollback);
820
821 // no need for fcb_lock as we have tree_lock exclusively
822 Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, PagedPool, FALSE, Irp);
823
824 if (NT_SUCCESS(Status)) {
825 if (!fr2->deleted) {
826 WARN("file already exists\n");
827 free_fileref(fr2);
828 Status = STATUS_OBJECT_NAME_COLLISION;
829 goto end;
830 } else
831 free_fileref(fr2);
832 } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
833 ERR("open_fileref returned %08x\n", Status);
834 goto end;
835 }
836
837 // FIXME - make sure rollback removes new roots from internal structures
838
839 id = InterlockedIncrement64(&Vcb->root_root->lastinode);
840 Status = create_root(Vcb, id, &r, FALSE, 0, Irp, &rollback);
841
842 if (!NT_SUCCESS(Status)) {
843 ERR("create_root returned %08x\n", Status);
844 goto end;
845 }
846
847 TRACE("created root %llx\n", id);
848
849 if (!Vcb->uuid_root) {
850 root* uuid_root;
851
852 TRACE("uuid root doesn't exist, creating it\n");
853
854 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
855
856 if (!NT_SUCCESS(Status)) {
857 ERR("create_root returned %08x\n", Status);
858 goto end;
859 }
860
861 Vcb->uuid_root = uuid_root;
862 }
863
864 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64), ALLOC_TAG);
865 if (!root_num) {
866 ERR("out of memory\n");
867 Status = STATUS_INSUFFICIENT_RESOURCES;
868 goto end;
869 }
870
871 tp.tree = NULL;
872
873 do {
874 get_uuid(&r->root_item.uuid);
875
876 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(UINT64));
877 searchkey.obj_type = TYPE_SUBVOL_UUID;
878 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
879
880 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
881 } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
882
883 *root_num = r->id;
884
885 if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
886 ERR("insert_tree_item failed\n");
887 Status = STATUS_INTERNAL_ERROR;
888 goto end;
889 }
890
891 r->root_item.inode.generation = 1;
892 r->root_item.inode.st_size = 3;
893 r->root_item.inode.st_blocks = Vcb->superblock.node_size;
894 r->root_item.inode.st_nlink = 1;
895 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
896 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
897
898 r->root_item.objid = SUBVOL_ROOT_INODE;
899 r->root_item.bytes_used = Vcb->superblock.node_size;
900 r->root_item.ctransid = Vcb->superblock.generation;
901 r->root_item.otransid = Vcb->superblock.generation;
902 r->root_item.ctime = now;
903 r->root_item.otime = now;
904
905 // add .. inode to new subvol
906
907 rootfcb = create_fcb(PagedPool);
908 if (!rootfcb) {
909 ERR("out of memory\n");
910 Status = STATUS_INSUFFICIENT_RESOURCES;
911 goto end;
912 }
913
914 rootfcb->Vcb = Vcb;
915
916 rootfcb->subvol = r;
917 rootfcb->inode = SUBVOL_ROOT_INODE;
918 rootfcb->type = BTRFS_TYPE_DIRECTORY;
919
920 rootfcb->inode_item.generation = Vcb->superblock.generation;
921 rootfcb->inode_item.transid = Vcb->superblock.generation;
922 rootfcb->inode_item.st_nlink = 1;
923 rootfcb->inode_item.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
924 rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
925 rootfcb->inode_item.st_gid = GID_NOBODY; // FIXME?
926
927 rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE, Irp);
928
929 SeCaptureSubjectContext(&subjcont);
930
931 Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
932
933 if (!NT_SUCCESS(Status)) {
934 ERR("SeAssignSecurity returned %08x\n", Status);
935 goto end;
936 }
937
938 if (!rootfcb->sd) {
939 ERR("SeAssignSecurity returned NULL security descriptor\n");
940 Status = STATUS_INTERNAL_ERROR;
941 goto end;
942 }
943
944 Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted);
945 if (!NT_SUCCESS(Status)) {
946 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
947 rootfcb->inode_item.st_uid = UID_NOBODY;
948 } else {
949 rootfcb->inode_item.st_uid = sid_to_uid(owner);
950 }
951
952 rootfcb->sd_dirty = TRUE;
953 rootfcb->inode_item_changed = TRUE;
954
955 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
956 InsertTailList(&r->fcbs, &rootfcb->list_entry);
957 InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
958 ExReleaseResourceLite(&Vcb->fcb_lock);
959
960 rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
961 rootfcb->Header.AllocationSize.QuadPart = 0;
962 rootfcb->Header.FileSize.QuadPart = 0;
963 rootfcb->Header.ValidDataLength.QuadPart = 0;
964
965 rootfcb->created = TRUE;
966
967 r->lastinode = rootfcb->inode;
968
969 // add INODE_REF
970
971 irsize = sizeof(INODE_REF) - 1 + strlen(DOTDOT);
972 ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
973 if (!ir) {
974 ERR("out of memory\n");
975 Status = STATUS_INSUFFICIENT_RESOURCES;
976 goto end;
977 }
978
979 ir->index = 0;
980 ir->n = strlen(DOTDOT);
981 RtlCopyMemory(ir->name, DOTDOT, ir->n);
982
983 if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp, &rollback)) {
984 ERR("insert_tree_item failed\n");
985 Status = STATUS_INTERNAL_ERROR;
986 goto end;
987 }
988
989 // create fileref for entry in other subvolume
990
991 fr = create_fileref();
992 if (!fr) {
993 ERR("out of memory\n");
994
995 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
996 free_fcb(rootfcb);
997 ExReleaseResourceLite(&Vcb->fcb_lock);
998
999 Status = STATUS_INSUFFICIENT_RESOURCES;
1000 goto end;
1001 }
1002
1003 fr->fcb = rootfcb;
1004
1005 mark_fcb_dirty(rootfcb);
1006
1007 Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
1008 if (!NT_SUCCESS(Status)) {
1009 ERR("fcb_get_last_dir_index returned %08x\n", Status);
1010 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1011 free_fileref(fr);
1012 ExReleaseResourceLite(&Vcb->fcb_lock);
1013 goto end;
1014 }
1015
1016 fr->index = dirpos;
1017 fr->utf8 = utf8;
1018
1019 fr->filepart.MaximumLength = fr->filepart.Length = nameus.Length;
1020 fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
1021 if (!fr->filepart.Buffer) {
1022 ERR("out of memory\n");
1023 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1024 free_fileref(fr);
1025 ExReleaseResourceLite(&Vcb->fcb_lock);
1026 Status = STATUS_INSUFFICIENT_RESOURCES;
1027 goto end;
1028 }
1029
1030 RtlCopyMemory(fr->filepart.Buffer, nameus.Buffer, nameus.Length);
1031
1032 Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
1033 if (!NT_SUCCESS(Status)) {
1034 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1035 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1036 free_fileref(fr);
1037 ExReleaseResourceLite(&Vcb->fcb_lock);
1038 goto end;
1039 }
1040
1041 fr->parent = fileref;
1042
1043 Status = add_dir_child(fileref->fcb, r->id, TRUE, dirpos, &utf8, &fr->filepart, &fr->filepart_uc, BTRFS_TYPE_DIRECTORY, &dc);
1044 if (!NT_SUCCESS(Status))
1045 WARN("add_dir_child returned %08x\n", Status);
1046
1047 fr->dc = dc;
1048 dc->fileref = fr;
1049
1050 fr->fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1051 if (!fr->fcb->hash_ptrs) {
1052 ERR("out of memory\n");
1053 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1054 free_fileref(fr);
1055 ExReleaseResourceLite(&Vcb->fcb_lock);
1056 Status = STATUS_INSUFFICIENT_RESOURCES;
1057 goto end;
1058 }
1059
1060 RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1061
1062 fr->fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1063 if (!fcb->hash_ptrs_uc) {
1064 ERR("out of memory\n");
1065 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1066 free_fileref(fr);
1067 ExReleaseResourceLite(&Vcb->fcb_lock);
1068 Status = STATUS_INSUFFICIENT_RESOURCES;
1069 goto end;
1070 }
1071
1072 RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1073
1074 insert_fileref_child(fileref, fr, TRUE);
1075 increase_fileref_refcount(fileref);
1076
1077 if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1078 fr->fcb->fileref = fr;
1079
1080 fr->created = TRUE;
1081 mark_fileref_dirty(fr);
1082
1083 // change fcb->subvol's ROOT_ITEM
1084
1085 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1086 fcb->subvol->root_item.ctime = now;
1087
1088 // change fcb's INODE_ITEM
1089
1090 fcb->inode_item.transid = Vcb->superblock.generation;
1091 fcb->inode_item.st_size += utf8.Length * 2;
1092 fcb->inode_item.sequence++;
1093
1094 if (!ccb->user_set_change_time)
1095 fcb->inode_item.st_ctime = now;
1096
1097 if (!ccb->user_set_write_time)
1098 fcb->inode_item.st_mtime = now;
1099
1100 fcb->inode_item_changed = TRUE;
1101 mark_fcb_dirty(fcb);
1102
1103 Status = STATUS_SUCCESS;
1104
1105 end:
1106 if (!NT_SUCCESS(Status))
1107 do_rollback(Vcb, &rollback);
1108 else
1109 clear_rollback(Vcb, &rollback);
1110
1111 ExReleaseResourceLite(&Vcb->tree_lock);
1112
1113 if (NT_SUCCESS(Status)) {
1114 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED);
1115 send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
1116 }
1117
1118 end2:
1119 if (fr) {
1120 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1121 free_fileref(fr);
1122 ExReleaseResourceLite(&Vcb->fcb_lock);
1123 }
1124
1125 return Status;
1126 }
1127
1128 static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
1129 btrfs_inode_info* bii = data;
1130 fcb* fcb;
1131 ccb* ccb;
1132
1133 if (length < sizeof(btrfs_inode_info))
1134 return STATUS_BUFFER_OVERFLOW;
1135
1136 if (!FileObject)
1137 return STATUS_INVALID_PARAMETER;
1138
1139 fcb = FileObject->FsContext;
1140
1141 if (!fcb)
1142 return STATUS_INVALID_PARAMETER;
1143
1144 ccb = FileObject->FsContext2;
1145
1146 if (!ccb)
1147 return STATUS_INVALID_PARAMETER;
1148
1149 if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1150 WARN("insufficient privileges\n");
1151 return STATUS_ACCESS_DENIED;
1152 }
1153
1154 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
1155
1156 bii->subvol = fcb->subvol->id;
1157 bii->inode = fcb->inode;
1158 bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
1159 bii->type = fcb->type;
1160 bii->st_uid = fcb->inode_item.st_uid;
1161 bii->st_gid = fcb->inode_item.st_gid;
1162 bii->st_mode = fcb->inode_item.st_mode;
1163 bii->st_rdev = fcb->inode_item.st_rdev;
1164 bii->flags = fcb->inode_item.flags;
1165
1166 bii->inline_length = 0;
1167 bii->disk_size[0] = 0;
1168 bii->disk_size[1] = 0;
1169 bii->disk_size[2] = 0;
1170
1171 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1172 LIST_ENTRY* le;
1173
1174 le = fcb->extents.Flink;
1175 while (le != &fcb->extents) {
1176 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1177
1178 if (!ext->ignore) {
1179 if (ext->data->type == EXTENT_TYPE_INLINE) {
1180 bii->inline_length += ext->data->decoded_size;
1181 } else {
1182 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
1183
1184 // FIXME - compressed extents with a hole in them are counted more than once
1185 if (ed2->size != 0) {
1186 if (ext->data->compression == BTRFS_COMPRESSION_NONE) {
1187 bii->disk_size[0] += ed2->num_bytes;
1188 } else if (ext->data->compression == BTRFS_COMPRESSION_ZLIB) {
1189 bii->disk_size[1] += ed2->size;
1190 } else if (ext->data->compression == BTRFS_COMPRESSION_LZO) {
1191 bii->disk_size[2] += ed2->size;
1192 }
1193 }
1194 }
1195 }
1196
1197 le = le->Flink;
1198 }
1199 }
1200
1201 ExReleaseResourceLite(fcb->Header.Resource);
1202
1203 return STATUS_SUCCESS;
1204 }
1205
1206 static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
1207 btrfs_set_inode_info* bsii = data;
1208 NTSTATUS Status;
1209 fcb* fcb;
1210 ccb* ccb;
1211
1212 if (length < sizeof(btrfs_set_inode_info))
1213 return STATUS_BUFFER_OVERFLOW;
1214
1215 if (!FileObject)
1216 return STATUS_INVALID_PARAMETER;
1217
1218 fcb = FileObject->FsContext;
1219
1220 if (!fcb)
1221 return STATUS_INVALID_PARAMETER;
1222
1223 ccb = FileObject->FsContext2;
1224
1225 if (!ccb)
1226 return STATUS_INVALID_PARAMETER;
1227
1228 if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1229 WARN("insufficient privileges\n");
1230 return STATUS_ACCESS_DENIED;
1231 }
1232
1233 if (bsii->mode_changed && !(ccb->access & WRITE_DAC)) {
1234 WARN("insufficient privileges\n");
1235 return STATUS_ACCESS_DENIED;
1236 }
1237
1238 if ((bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_OWNER)) {
1239 WARN("insufficient privileges\n");
1240 return STATUS_ACCESS_DENIED;
1241 }
1242
1243 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1244
1245 if (bsii->flags_changed) {
1246 if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1247 (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
1248 WARN("trying to change nocow flag on non-empty file\n");
1249 Status = STATUS_INVALID_PARAMETER;
1250 goto end;
1251 }
1252
1253 fcb->inode_item.flags = bsii->flags;
1254
1255 if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
1256 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
1257 else
1258 fcb->inode_item.flags &= ~(UINT64)BTRFS_INODE_NODATASUM;
1259 }
1260
1261 if (bsii->mode_changed) {
1262 UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
1263
1264 fcb->inode_item.st_mode &= ~allowed;
1265 fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1266 }
1267
1268 if (bsii->uid_changed) {
1269 PSID sid;
1270 SECURITY_INFORMATION secinfo;
1271 SECURITY_DESCRIPTOR sd;
1272 void* oldsd;
1273
1274 fcb->inode_item.st_uid = bsii->st_uid;
1275
1276 uid_to_sid(bsii->st_uid, &sid);
1277
1278 Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
1279 if (!NT_SUCCESS(Status)) {
1280 ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
1281 goto end;
1282 }
1283
1284 Status = RtlSetOwnerSecurityDescriptor(&sd, sid, FALSE);
1285 if (!NT_SUCCESS(Status)) {
1286 ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
1287 goto end;
1288 }
1289
1290 oldsd = fcb->sd;
1291
1292 secinfo = OWNER_SECURITY_INFORMATION;
1293 Status = SeSetSecurityDescriptorInfoEx(NULL, &secinfo, &sd, (void**)&fcb->sd, SEF_AVOID_PRIVILEGE_CHECK, PagedPool, IoGetFileObjectGenericMapping());
1294
1295 if (!NT_SUCCESS(Status)) {
1296 ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
1297 goto end;
1298 }
1299
1300 ExFreePool(oldsd);
1301
1302 fcb->sd_dirty = TRUE;
1303
1304 send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED);
1305 }
1306
1307 if (bsii->gid_changed)
1308 fcb->inode_item.st_gid = bsii->st_gid;
1309
1310 if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) {
1311 fcb->inode_item_changed = TRUE;
1312 mark_fcb_dirty(fcb);
1313 }
1314
1315 Status = STATUS_SUCCESS;
1316
1317 end:
1318 ExReleaseResourceLite(fcb->Header.Resource);
1319
1320 return Status;
1321 }
1322
1323 static NTSTATUS get_devices(device_extension* Vcb, void* data, ULONG length) {
1324 btrfs_device* dev = NULL;
1325 NTSTATUS Status;
1326 LIST_ENTRY* le;
1327
1328 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1329
1330 le = Vcb->devices.Flink;
1331 while (le != &Vcb->devices) {
1332 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1333 ULONG structlen;
1334
1335 if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1336 Status = STATUS_BUFFER_OVERFLOW;
1337 goto end;
1338 }
1339
1340 if (!dev)
1341 dev = data;
1342 else {
1343 dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1344 dev = (btrfs_device*)((UINT8*)dev + dev->next_entry);
1345 }
1346
1347 structlen = length - offsetof(btrfs_device, namelen);
1348
1349 Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, TRUE, NULL);
1350 if (!NT_SUCCESS(Status))
1351 goto end;
1352
1353 dev->next_entry = 0;
1354 dev->dev_id = dev2->devitem.dev_id;
1355 dev->size = dev2->length;
1356 dev->readonly = (Vcb->readonly || dev2->readonly) ? TRUE : FALSE;
1357 dev->device_number = dev2->disk_num;
1358 dev->partition_number = dev2->part_num;
1359
1360 length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1361
1362 le = le->Flink;
1363 }
1364
1365 end:
1366 ExReleaseResourceLite(&Vcb->tree_lock);
1367
1368 return Status;
1369 }
1370
1371 static NTSTATUS get_usage(device_extension* Vcb, void* data, ULONG length) {
1372 btrfs_usage* usage = (btrfs_usage*)data;
1373 btrfs_usage* lastbue = NULL;
1374 NTSTATUS Status;
1375 LIST_ENTRY* le;
1376
1377 if (length < sizeof(btrfs_usage))
1378 return STATUS_BUFFER_OVERFLOW;
1379
1380 length -= offsetof(btrfs_usage, devices);
1381
1382 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
1383
1384 le = Vcb->chunks.Flink;
1385 while (le != &Vcb->chunks) {
1386 BOOL addnew = FALSE;
1387
1388 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1389
1390 if (!lastbue) // first entry
1391 addnew = TRUE;
1392 else {
1393 btrfs_usage* bue = usage;
1394
1395 addnew = TRUE;
1396
1397 while (TRUE) {
1398 if (bue->type == c->chunk_item->type) {
1399 addnew = FALSE;
1400 break;
1401 }
1402
1403 if (bue->next_entry == 0)
1404 break;
1405 else
1406 bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
1407 }
1408 }
1409
1410 if (addnew) {
1411 btrfs_usage* bue;
1412 LIST_ENTRY* le2;
1413 UINT64 factor;
1414
1415 if (!lastbue) {
1416 bue = usage;
1417 } else {
1418 if (length < offsetof(btrfs_usage, devices)) {
1419 Status = STATUS_BUFFER_OVERFLOW;
1420 goto end;
1421 }
1422
1423 length -= offsetof(btrfs_usage, devices);
1424
1425 lastbue->next_entry = offsetof(btrfs_usage, devices) + (lastbue->num_devices * sizeof(btrfs_usage_device));
1426
1427 bue = (btrfs_usage*)((UINT8*)lastbue + lastbue->next_entry);
1428 }
1429
1430 bue->next_entry = 0;
1431 bue->type = c->chunk_item->type;
1432 bue->size = 0;
1433 bue->used = 0;
1434 bue->num_devices = 0;
1435
1436 if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1437 factor = c->chunk_item->num_stripes;
1438 else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1439 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1440 else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1441 factor = c->chunk_item->num_stripes - 1;
1442 else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1443 factor = c->chunk_item->num_stripes - 2;
1444 else
1445 factor = 1;
1446
1447 le2 = le;
1448 while (le2 != &Vcb->chunks) {
1449 chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1450
1451 if (c2->chunk_item->type == c->chunk_item->type) {
1452 UINT16 i;
1453 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c2->chunk_item[1];
1454 UINT64 stripesize;
1455
1456 bue->size += c2->chunk_item->size;
1457 bue->used += c2->used;
1458
1459 stripesize = c2->chunk_item->size / factor;
1460
1461 for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1462 UINT64 j;
1463 BOOL found = FALSE;
1464
1465 for (j = 0; j < bue->num_devices; j++) {
1466 if (bue->devices[j].dev_id == cis[i].dev_id) {
1467 bue->devices[j].alloc += stripesize;
1468 found = TRUE;
1469 break;
1470 }
1471 }
1472
1473 if (!found) {
1474 if (length < sizeof(btrfs_usage_device)) {
1475 Status = STATUS_BUFFER_OVERFLOW;
1476 goto end;
1477 }
1478
1479 length -= sizeof(btrfs_usage_device);
1480
1481 bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1482 bue->devices[bue->num_devices].alloc = stripesize;
1483 bue->num_devices++;
1484 }
1485 }
1486 }
1487
1488 le2 = le2->Flink;
1489 }
1490
1491 lastbue = bue;
1492 }
1493
1494 le = le->Flink;
1495 }
1496
1497 Status = STATUS_SUCCESS;
1498
1499 end:
1500 ExReleaseResourceLite(&Vcb->chunk_lock);
1501
1502 return Status;
1503 }
1504
1505 static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) {
1506 NTSTATUS Status;
1507 ULONG cc;
1508 IO_STATUS_BLOCK iosb;
1509 BOOL verify = FALSE;
1510 LIST_ENTRY* le;
1511
1512 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1513
1514 le = Vcb->devices.Flink;
1515 while (le != &Vcb->devices) {
1516 device* dev = CONTAINING_RECORD(le, device, list_entry);
1517
1518 if (dev->devobj && dev->removable) {
1519 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), FALSE, &iosb);
1520
1521 if (iosb.Information != sizeof(ULONG))
1522 cc = 0;
1523
1524 if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1525 dev->devobj->Flags |= DO_VERIFY_VOLUME;
1526 verify = TRUE;
1527 }
1528
1529 if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1530 dev->change_count = cc;
1531
1532 if (!NT_SUCCESS(Status) || verify) {
1533 IoSetHardErrorOrVerifyDevice(Irp, dev->devobj);
1534 ExReleaseResourceLite(&Vcb->tree_lock);
1535
1536 return verify ? STATUS_VERIFY_REQUIRED : Status;
1537 }
1538 }
1539
1540 le = le->Flink;
1541 }
1542
1543 ExReleaseResourceLite(&Vcb->tree_lock);
1544
1545 return STATUS_SUCCESS;
1546 }
1547
1548 static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1549 FILESYSTEM_STATISTICS* fss;
1550
1551 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1552
1553 // This is hideously wrong, but at least it stops SMB from breaking
1554
1555 if (buflen < sizeof(FILESYSTEM_STATISTICS))
1556 return STATUS_BUFFER_TOO_SMALL;
1557
1558 fss = buffer;
1559 RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1560
1561 fss->Version = 1;
1562 fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
1563 fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS);
1564
1565 *retlen = sizeof(FILESYSTEM_STATISTICS);
1566
1567 return STATUS_SUCCESS;
1568 }
1569
1570 static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1571 FILE_SET_SPARSE_BUFFER* fssb = data;
1572 NTSTATUS Status;
1573 BOOL set;
1574 fcb* fcb;
1575 ccb* ccb = FileObject->FsContext2;
1576 file_ref* fileref = ccb ? ccb->fileref : NULL;
1577
1578 if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1579 return STATUS_INVALID_PARAMETER;
1580
1581 if (!FileObject) {
1582 ERR("FileObject was NULL\n");
1583 return STATUS_INVALID_PARAMETER;
1584 }
1585
1586 fcb = FileObject->FsContext;
1587
1588 if (!fcb) {
1589 ERR("FCB was NULL\n");
1590 return STATUS_INVALID_PARAMETER;
1591 }
1592
1593 if (!ccb) {
1594 ERR("CCB was NULL\n");
1595 return STATUS_INVALID_PARAMETER;
1596 }
1597
1598 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1599 WARN("insufficient privileges\n");
1600 return STATUS_ACCESS_DENIED;
1601 }
1602
1603 if (!fileref) {
1604 ERR("no fileref\n");
1605 return STATUS_INVALID_PARAMETER;
1606 }
1607
1608 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1609 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1610
1611 if (fcb->type != BTRFS_TYPE_FILE) {
1612 WARN("FileObject did not point to a file\n");
1613 Status = STATUS_INVALID_PARAMETER;
1614 goto end;
1615 }
1616
1617 if (fssb)
1618 set = fssb->SetSparse;
1619 else
1620 set = TRUE;
1621
1622 if (set) {
1623 fcb->atts |= FILE_ATTRIBUTE_SPARSE_FILE;
1624 fcb->atts_changed = TRUE;
1625 } else {
1626 ULONG defda;
1627
1628 fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE;
1629 fcb->atts_changed = TRUE;
1630
1631 defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type,
1632 fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
1633
1634 fcb->atts_deleted = defda == fcb->atts;
1635 }
1636
1637 mark_fcb_dirty(fcb);
1638 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED);
1639
1640 Status = STATUS_SUCCESS;
1641
1642 end:
1643 ExReleaseResourceLite(fcb->Header.Resource);
1644 ExReleaseResourceLite(&Vcb->tree_lock);
1645
1646 return Status;
1647 }
1648
1649 static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
1650 NTSTATUS Status;
1651 BOOL compress = write_fcb_compressed(fcb);
1652 UINT64 start_data, end_data;
1653 UINT8* data;
1654
1655 if (compress) {
1656 start_data = start & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
1657 end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE),
1658 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size));
1659 } else {
1660 start_data = start & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
1661 end_data = sector_align(start + length, fcb->Vcb->superblock.sector_size);
1662 }
1663
1664 data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
1665 if (!data) {
1666 ERR("out of memory\n");
1667 return STATUS_INSUFFICIENT_RESOURCES;
1668 }
1669
1670 RtlZeroMemory(data, end_data - start_data);
1671
1672 if (start > start_data || start + length < end_data) {
1673 Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp, TRUE);
1674
1675 if (!NT_SUCCESS(Status)) {
1676 ERR("read_file returned %08x\n", Status);
1677 ExFreePool(data);
1678 return Status;
1679 }
1680 }
1681
1682 RtlZeroMemory(data + start - start_data, length);
1683
1684 if (compress) {
1685 Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback);
1686
1687 ExFreePool(data);
1688
1689 if (!NT_SUCCESS(Status)) {
1690 ERR("write_compressed returned %08x\n", Status);
1691 return Status;
1692 }
1693 } else {
1694 Status = do_write_file(fcb, start_data, end_data, data, Irp, rollback);
1695
1696 ExFreePool(data);
1697
1698 if (!NT_SUCCESS(Status)) {
1699 ERR("do_write_file returned %08x\n", Status);
1700 return Status;
1701 }
1702 }
1703
1704 return STATUS_SUCCESS;
1705 }
1706
1707 static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1708 FILE_ZERO_DATA_INFORMATION* fzdi = data;
1709 NTSTATUS Status;
1710 fcb* fcb;
1711 ccb* ccb;
1712 file_ref* fileref;
1713 LIST_ENTRY rollback, *le;
1714 LARGE_INTEGER time;
1715 BTRFS_TIME now;
1716 UINT64 start, end;
1717 extent* ext;
1718 IO_STATUS_BLOCK iosb;
1719
1720 if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1721 return STATUS_INVALID_PARAMETER;
1722
1723 if (!FileObject) {
1724 ERR("FileObject was NULL\n");
1725 return STATUS_INVALID_PARAMETER;
1726 }
1727
1728 if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1729 WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1730 return STATUS_INVALID_PARAMETER;
1731 }
1732
1733 fcb = FileObject->FsContext;
1734
1735 if (!fcb) {
1736 ERR("FCB was NULL\n");
1737 return STATUS_INVALID_PARAMETER;
1738 }
1739
1740 ccb = FileObject->FsContext2;
1741
1742 if (!ccb) {
1743 ERR("ccb was NULL\n");
1744 return STATUS_INVALID_PARAMETER;
1745 }
1746
1747 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1748 WARN("insufficient privileges\n");
1749 return STATUS_ACCESS_DENIED;
1750 }
1751
1752 fileref = ccb->fileref;
1753
1754 if (!fileref) {
1755 ERR("fileref was NULL\n");
1756 return STATUS_INVALID_PARAMETER;
1757 }
1758
1759 InitializeListHead(&rollback);
1760
1761 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1762 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1763
1764 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
1765
1766 if (fcb->type != BTRFS_TYPE_FILE) {
1767 WARN("FileObject did not point to a file\n");
1768 Status = STATUS_INVALID_PARAMETER;
1769 goto end;
1770 }
1771
1772 if (fcb->ads) {
1773 ERR("FileObject is stream\n");
1774 Status = STATUS_INVALID_PARAMETER;
1775 goto end;
1776 }
1777
1778 if (fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1779 Status = STATUS_SUCCESS;
1780 goto end;
1781 }
1782
1783 ext = NULL;
1784 le = fcb->extents.Flink;
1785 while (le != &fcb->extents) {
1786 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
1787
1788 if (!ext2->ignore) {
1789 ext = ext2;
1790 break;
1791 }
1792
1793 le = le->Flink;
1794 }
1795
1796 if (!ext) {
1797 Status = STATUS_SUCCESS;
1798 goto end;
1799 }
1800
1801 if (ext->datalen >= sizeof(EXTENT_DATA) && ext->data->type == EXTENT_TYPE_INLINE) {
1802 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1803 if (!NT_SUCCESS(Status)) {
1804 ERR("zero_data returned %08x\n", Status);
1805 goto end;
1806 }
1807 } else {
1808 start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
1809
1810 if (fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
1811 end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
1812 else
1813 end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
1814
1815 if (end <= start) {
1816 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1817 if (!NT_SUCCESS(Status)) {
1818 ERR("zero_data returned %08x\n", Status);
1819 goto end;
1820 }
1821 } else {
1822 if (start > fzdi->FileOffset.QuadPart) {
1823 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
1824 if (!NT_SUCCESS(Status)) {
1825 ERR("zero_data returned %08x\n", Status);
1826 goto end;
1827 }
1828 }
1829
1830 if (end < fzdi->BeyondFinalZero.QuadPart) {
1831 Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
1832 if (!NT_SUCCESS(Status)) {
1833 ERR("zero_data returned %08x\n", Status);
1834 goto end;
1835 }
1836 }
1837
1838 if (end > start) {
1839 Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback);
1840 if (!NT_SUCCESS(Status)) {
1841 ERR("excise_extents returned %08x\n", Status);
1842 goto end;
1843 }
1844 }
1845 }
1846 }
1847
1848 CcPurgeCacheSection(&fcb->nonpaged->segment_object, &fzdi->FileOffset, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, FALSE);
1849
1850 KeQuerySystemTime(&time);
1851 win_time_to_unix(time, &now);
1852
1853 fcb->inode_item.transid = Vcb->superblock.generation;
1854 fcb->inode_item.sequence++;
1855
1856 if (!ccb->user_set_change_time)
1857 fcb->inode_item.st_ctime = now;
1858
1859 if (!ccb->user_set_write_time)
1860 fcb->inode_item.st_mtime = now;
1861
1862 fcb->extents_changed = TRUE;
1863 fcb->inode_item_changed = TRUE;
1864 mark_fcb_dirty(fcb);
1865
1866 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
1867
1868 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1869 fcb->subvol->root_item.ctime = now;
1870
1871 Status = STATUS_SUCCESS;
1872
1873 end:
1874 if (!NT_SUCCESS(Status))
1875 do_rollback(Vcb, &rollback);
1876 else
1877 clear_rollback(Vcb, &rollback);
1878
1879 ExReleaseResourceLite(fcb->Header.Resource);
1880 ExReleaseResourceLite(&Vcb->tree_lock);
1881
1882 return Status;
1883 }
1884
1885 static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
1886 NTSTATUS Status;
1887 fcb* fcb;
1888 LIST_ENTRY* le;
1889 FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
1890 ULONG i = 0;
1891 UINT64 last_start, last_end;
1892
1893 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
1894
1895 if (!FileObject) {
1896 ERR("FileObject was NULL\n");
1897 return STATUS_INVALID_PARAMETER;
1898 }
1899
1900 if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
1901 return STATUS_INVALID_PARAMETER;
1902
1903 fcb = FileObject->FsContext;
1904
1905 if (!fcb) {
1906 ERR("FCB was NULL\n");
1907 return STATUS_INVALID_PARAMETER;
1908 }
1909
1910 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
1911
1912 // If file is not marked as sparse, claim the whole thing as an allocated range
1913
1914 if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
1915 if (fcb->inode_item.st_size == 0)
1916 Status = STATUS_SUCCESS;
1917 else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
1918 Status = STATUS_BUFFER_TOO_SMALL;
1919 else {
1920 ranges[i].FileOffset.QuadPart = 0;
1921 ranges[i].Length.QuadPart = fcb->inode_item.st_size;
1922 i++;
1923 Status = STATUS_SUCCESS;
1924 }
1925
1926 goto end;
1927
1928 }
1929
1930 le = fcb->extents.Flink;
1931
1932 last_start = 0;
1933 last_end = 0;
1934
1935 while (le != &fcb->extents) {
1936 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1937
1938 if (!ext->ignore) {
1939 EXTENT_DATA2* ed2 = (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->data->data : NULL;
1940 UINT64 len = ed2 ? ed2->num_bytes : ext->data->decoded_size;
1941
1942 if (ext->offset > last_end) { // first extent after a hole
1943 if (last_end > last_start) {
1944 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
1945 ranges[i].FileOffset.QuadPart = last_start;
1946 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
1947 i++;
1948 } else {
1949 Status = STATUS_BUFFER_TOO_SMALL;
1950 goto end;
1951 }
1952 }
1953
1954 last_start = ext->offset;
1955 }
1956
1957 last_end = ext->offset + len;
1958 }
1959
1960 le = le->Flink;
1961 }
1962
1963 if (last_end > last_start) {
1964 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
1965 ranges[i].FileOffset.QuadPart = last_start;
1966 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
1967 i++;
1968 } else {
1969 Status = STATUS_BUFFER_TOO_SMALL;
1970 goto end;
1971 }
1972 }
1973
1974 Status = STATUS_SUCCESS;
1975
1976 end:
1977 *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
1978
1979 ExReleaseResourceLite(fcb->Header.Resource);
1980
1981 return Status;
1982 }
1983
1984 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
1985 fcb* fcb;
1986
1987 TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen);
1988
1989 if (!FileObject) {
1990 ERR("FileObject was NULL\n");
1991 return STATUS_INVALID_PARAMETER;
1992 }
1993
1994 if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
1995 return STATUS_INVALID_PARAMETER;
1996
1997 fcb = FileObject->FsContext;
1998
1999 if (!fcb) {
2000 ERR("FCB was NULL\n");
2001 return STATUS_INVALID_PARAMETER;
2002 }
2003
2004 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
2005
2006 RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(UINT64));
2007 RtlCopyMemory(&buf->ObjectId[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
2008
2009 ExReleaseResourceLite(fcb->Header.Resource);
2010
2011 RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2012
2013 *retlen = sizeof(FILE_OBJECTID_BUFFER);
2014
2015 return STATUS_SUCCESS;
2016 }
2017
2018 static void flush_fcb_caches(device_extension* Vcb) {
2019 LIST_ENTRY* le;
2020
2021 le = Vcb->all_fcbs.Flink;
2022 while (le != &Vcb->all_fcbs) {
2023 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2024 IO_STATUS_BLOCK iosb;
2025
2026 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2027 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2028
2029 le = le->Flink;
2030 }
2031 }
2032
2033 static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
2034 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2035 NTSTATUS Status;
2036 LIST_ENTRY rollback;
2037 KIRQL irql;
2038 BOOL lock_paused_balance = FALSE;
2039
2040 TRACE("FSCTL_LOCK_VOLUME\n");
2041
2042 TRACE("locking volume\n");
2043
2044 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK);
2045
2046 if (Vcb->locked)
2047 return STATUS_SUCCESS;
2048
2049 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
2050
2051 if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2052 Status = STATUS_ACCESS_DENIED;
2053 ExReleaseResourceLite(&Vcb->fcb_lock);
2054 goto end;
2055 }
2056
2057 ExReleaseResourceLite(&Vcb->fcb_lock);
2058
2059 InitializeListHead(&rollback);
2060
2061 if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2062 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2063 KeClearEvent(&Vcb->balance.event);
2064 ExReleaseResourceLite(&Vcb->tree_lock);
2065
2066 lock_paused_balance = TRUE;
2067 }
2068
2069 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2070
2071 flush_fcb_caches(Vcb);
2072
2073 if (Vcb->need_write && !Vcb->readonly)
2074 do_write(Vcb, Irp, &rollback);
2075
2076 free_trees(Vcb);
2077
2078 clear_rollback(Vcb, &rollback);
2079
2080 ExReleaseResourceLite(&Vcb->tree_lock);
2081
2082 IoAcquireVpbSpinLock(&irql);
2083
2084 if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2085 Vcb->Vpb->Flags |= VPB_LOCKED;
2086 Vcb->locked = TRUE;
2087 Vcb->locked_fileobj = IrpSp->FileObject;
2088 Vcb->lock_paused_balance = lock_paused_balance;
2089 } else {
2090 Status = STATUS_ACCESS_DENIED;
2091 IoReleaseVpbSpinLock(irql);
2092
2093 if (lock_paused_balance)
2094 KeSetEvent(&Vcb->balance.event, 0, FALSE);
2095
2096 goto end;
2097 }
2098
2099 IoReleaseVpbSpinLock(irql);
2100
2101 Status = STATUS_SUCCESS;
2102
2103 end:
2104 if (!NT_SUCCESS(Status))
2105 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED);
2106
2107 return Status;
2108 }
2109
2110 void do_unlock_volume(device_extension* Vcb) {
2111 KIRQL irql;
2112
2113 IoAcquireVpbSpinLock(&irql);
2114
2115 Vcb->locked = FALSE;
2116 Vcb->Vpb->Flags &= ~VPB_LOCKED;
2117 Vcb->locked_fileobj = NULL;
2118
2119 IoReleaseVpbSpinLock(irql);
2120
2121 if (Vcb->lock_paused_balance)
2122 KeSetEvent(&Vcb->balance.event, 0, FALSE);
2123 }
2124
2125 static NTSTATUS unlock_volume(device_extension* Vcb, PIRP Irp) {
2126 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2127
2128 TRACE("FSCTL_UNLOCK_VOLUME\n");
2129
2130 if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2131 return STATUS_NOT_LOCKED;
2132
2133 TRACE("unlocking volume\n");
2134
2135 do_unlock_volume(Vcb);
2136
2137 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK);
2138
2139 return STATUS_SUCCESS;
2140 }
2141
2142 static NTSTATUS invalidate_volumes(PIRP Irp) {
2143 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2144 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2145 NTSTATUS Status;
2146 HANDLE h;
2147 PFILE_OBJECT fileobj;
2148 PDEVICE_OBJECT devobj;
2149 LIST_ENTRY* le;
2150
2151 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2152
2153 if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2154 return STATUS_PRIVILEGE_NOT_HELD;
2155
2156 #if defined(_WIN64)
2157 if (IoIs32bitProcess(Irp)) {
2158 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2159 return STATUS_INVALID_PARAMETER;
2160
2161 h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2162 } else {
2163 #endif
2164 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2165 return STATUS_INVALID_PARAMETER;
2166
2167 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2168 #if defined(_WIN64)
2169 }
2170 #endif
2171
2172 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, KernelMode, (void**)&fileobj, NULL);
2173
2174 if (!NT_SUCCESS(Status)) {
2175 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2176 return Status;
2177 }
2178
2179 devobj = fileobj->DeviceObject;
2180 ObDereferenceObject(fileobj);
2181
2182 ExAcquireResourceSharedLite(&global_loading_lock, TRUE);
2183
2184 le = VcbList.Flink;
2185
2186 while (le != &VcbList) {
2187 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2188
2189 if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2190 if (Vcb->Vpb == devobj->Vpb) {
2191 KIRQL irql;
2192 PVPB newvpb;
2193 BOOL free_newvpb = FALSE;
2194 LIST_ENTRY rollback;
2195
2196 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2197 if (!newvpb) {
2198 ERR("out of memory\n");
2199 Status = STATUS_INSUFFICIENT_RESOURCES;
2200 goto end;
2201 }
2202
2203 RtlZeroMemory(newvpb, sizeof(VPB));
2204
2205 IoAcquireVpbSpinLock(&irql);
2206 devobj->Vpb->Flags &= ~VPB_MOUNTED;
2207 IoReleaseVpbSpinLock(irql);
2208
2209 InitializeListHead(&rollback);
2210
2211 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2212
2213 Vcb->removing = TRUE;
2214
2215 ExReleaseResourceLite(&Vcb->tree_lock);
2216
2217 CcWaitForCurrentLazyWriterActivity();
2218
2219 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2220
2221 flush_fcb_caches(Vcb);
2222
2223 if (Vcb->need_write && !Vcb->readonly)
2224 do_write(Vcb, Irp, &rollback);
2225
2226 free_trees(Vcb);
2227
2228 clear_rollback(Vcb, &rollback);
2229
2230 flush_fcb_caches(Vcb);
2231
2232 ExReleaseResourceLite(&Vcb->tree_lock);
2233
2234 IoAcquireVpbSpinLock(&irql);
2235
2236 if (devobj->Vpb->Flags & VPB_MOUNTED) {
2237 newvpb->Type = IO_TYPE_VPB;
2238 newvpb->Size = sizeof(VPB);
2239 newvpb->RealDevice = devobj;
2240 newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2241
2242 devobj->Vpb = newvpb;
2243 } else
2244 free_newvpb = TRUE;
2245
2246 IoReleaseVpbSpinLock(irql);
2247
2248 if (free_newvpb)
2249 ExFreePool(newvpb);
2250
2251 uninit(Vcb, FALSE);
2252 }
2253
2254 break;
2255 }
2256
2257 le = le->Flink;
2258 }
2259
2260 Status = STATUS_SUCCESS;
2261
2262 end:
2263 ExReleaseResourceLite(&global_loading_lock);
2264
2265 return Status;
2266 }
2267
2268 static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) {
2269 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2270 ULONG* volstate;
2271
2272 if (Irp->AssociatedIrp.SystemBuffer) {
2273 volstate = Irp->AssociatedIrp.SystemBuffer;
2274 } else if (Irp->MdlAddress != NULL) {
2275 volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2276
2277 if (!volstate)
2278 return STATUS_INSUFFICIENT_RESOURCES;
2279 } else
2280 return STATUS_INVALID_USER_BUFFER;
2281
2282 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2283 return STATUS_INVALID_PARAMETER;
2284
2285 *volstate = 0;
2286
2287 if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2288 return STATUS_INVALID_PARAMETER;
2289
2290 Irp->IoStatus.Information = sizeof(ULONG);
2291
2292 return STATUS_SUCCESS;
2293 }
2294
2295 static NTSTATUS get_compression(device_extension* Vcb, PIRP Irp) {
2296 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2297 USHORT* compression;
2298
2299 TRACE("FSCTL_GET_COMPRESSION\n");
2300
2301 if (Irp->AssociatedIrp.SystemBuffer) {
2302 compression = Irp->AssociatedIrp.SystemBuffer;
2303 } else if (Irp->MdlAddress != NULL) {
2304 compression = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2305
2306 if (!compression)
2307 return STATUS_INSUFFICIENT_RESOURCES;
2308 } else
2309 return STATUS_INVALID_USER_BUFFER;
2310
2311 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2312 return STATUS_INVALID_PARAMETER;
2313
2314 *compression = COMPRESSION_FORMAT_NONE;
2315
2316 Irp->IoStatus.Information = sizeof(USHORT);
2317
2318 return STATUS_SUCCESS;
2319 }
2320
2321 static void update_volumes(device_extension* Vcb) {
2322 LIST_ENTRY* le;
2323
2324 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2325 ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
2326
2327 le = volumes.Flink;
2328
2329 while (le != &volumes) {
2330 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2331
2332 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2333 LIST_ENTRY* le;
2334
2335 le = Vcb->devices.Flink;
2336 while (le != &Vcb->devices) {
2337 device* dev = CONTAINING_RECORD(le, device, list_entry);
2338
2339 if (RtlCompareMemory(&dev->devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2340 v->gen1 = v->gen2 = Vcb->superblock.generation - 1;
2341 break;
2342 }
2343
2344 le = le->Flink;
2345 }
2346 }
2347
2348 le = le->Flink;
2349 }
2350
2351 ExReleaseResourceLite(&volumes_lock);
2352 ExReleaseResourceLite(&Vcb->tree_lock);
2353 }
2354
2355 static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
2356 NTSTATUS Status;
2357 KIRQL irql;
2358 LIST_ENTRY rollback;
2359
2360 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2361
2362 if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2363 return STATUS_SUCCESS;
2364
2365 if (Vcb->disallow_dismount) {
2366 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2367 return STATUS_ACCESS_DENIED;
2368 }
2369
2370 InitializeListHead(&rollback);
2371
2372 Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
2373 if (!NT_SUCCESS(Status)) {
2374 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
2375 }
2376
2377 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2378
2379 flush_fcb_caches(Vcb);
2380
2381 if (Vcb->need_write && !Vcb->readonly)
2382 do_write(Vcb, Irp, &rollback);
2383
2384 free_trees(Vcb);
2385
2386 clear_rollback(Vcb, &rollback);
2387
2388 Vcb->removing = TRUE;
2389 update_volumes(Vcb);
2390
2391 ExReleaseResourceLite(&Vcb->tree_lock);
2392
2393 IoAcquireVpbSpinLock(&irql);
2394 Vcb->Vpb->Flags &= ~VPB_MOUNTED;
2395 Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
2396 IoReleaseVpbSpinLock(irql);
2397
2398 return STATUS_SUCCESS;
2399 }
2400
2401 static NTSTATUS is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj) {
2402 NTSTATUS Status;
2403 ULONG to_read;
2404 superblock* sb;
2405 UINT32 crc32;
2406 BTRFS_UUID fsuuid, devuuid;
2407 LIST_ENTRY* le;
2408
2409 to_read = devobj->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), devobj->SectorSize);
2410
2411 sb = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
2412 if (!sb) {
2413 ERR("out of memory\n");
2414 return STATUS_INSUFFICIENT_RESOURCES;
2415 }
2416
2417 Status = sync_read_phys(devobj, superblock_addrs[0], to_read, (UINT8*)sb, TRUE);
2418 if (!NT_SUCCESS(Status)) {
2419 ERR("sync_read_phys returned %08x\n", Status);
2420 ExFreePool(sb);
2421 return Status;
2422 }
2423
2424 if (sb->magic != BTRFS_MAGIC) {
2425 TRACE("device is not Btrfs\n");
2426 ExFreePool(sb);
2427 return STATUS_SUCCESS;
2428 }
2429
2430 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2431
2432 if (crc32 != *((UINT32*)sb->checksum)) {
2433 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2434 ExFreePool(sb);
2435 return STATUS_SUCCESS;
2436 }
2437
2438 fsuuid = sb->uuid;
2439 devuuid = sb->dev_item.device_uuid;
2440
2441 ExFreePool(sb);
2442
2443 ExAcquireResourceSharedLite(&global_loading_lock, TRUE);
2444
2445 le = VcbList.Flink;
2446
2447 while (le != &VcbList) {
2448 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2449
2450 if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2451 LIST_ENTRY* le2;
2452
2453 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2454
2455 if (Vcb->superblock.num_devices > 1) {
2456 le2 = Vcb->devices.Flink;
2457 while (le2 != &Vcb->devices) {
2458 device* dev = CONTAINING_RECORD(le2, device, list_entry);
2459
2460 if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2461 ExReleaseResourceLite(&Vcb->tree_lock);
2462 ExReleaseResourceLite(&global_loading_lock);
2463 return STATUS_DEVICE_NOT_READY;
2464 }
2465
2466 le2 = le2->Flink;
2467 }
2468 }
2469
2470 ExReleaseResourceLite(&Vcb->tree_lock);
2471 ExReleaseResourceLite(&global_loading_lock);
2472 return STATUS_SUCCESS;
2473 }
2474
2475 le = le->Flink;
2476 }
2477
2478 ExReleaseResourceLite(&global_loading_lock);
2479
2480 return STATUS_SUCCESS;
2481 }
2482
2483 static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
2484 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2485 NTSTATUS Status;
2486 PFILE_OBJECT fileobj, mountmgrfo;
2487 HANDLE h;
2488 LIST_ENTRY rollback, *le;
2489 GET_LENGTH_INFORMATION gli;
2490 device* dev;
2491 DEV_ITEM* di;
2492 UINT64 dev_id;
2493 UINT8* mb;
2494 UINT64* stats;
2495 MOUNTDEV_NAME mdn1, *mdn2;
2496 UNICODE_STRING volname, mmdevpath;
2497 volume* v;
2498 PDEVICE_OBJECT mountmgr;
2499 KEY searchkey;
2500 traverse_ptr tp;
2501 STORAGE_DEVICE_NUMBER sdn;
2502
2503 volname.Buffer = NULL;
2504
2505 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2506 return STATUS_PRIVILEGE_NOT_HELD;
2507
2508 if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2509 return STATUS_MEDIA_WRITE_PROTECTED;
2510
2511 #if defined(_WIN64)
2512 if (IoIs32bitProcess(Irp)) {
2513 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2514 return STATUS_INVALID_PARAMETER;
2515
2516 h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2517 } else {
2518 #endif
2519 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2520 return STATUS_INVALID_PARAMETER;
2521
2522 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2523 #if defined(_WIN64)
2524 }
2525 #endif
2526
2527 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2528
2529 if (!NT_SUCCESS(Status)) {
2530 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2531 return Status;
2532 }
2533
2534 Status = is_device_part_of_mounted_btrfs_raid(fileobj->DeviceObject);
2535 if (!NT_SUCCESS(Status)) {
2536 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
2537 ObDereferenceObject(fileobj);
2538 return Status;
2539 }
2540
2541 Status = dev_ioctl(fileobj->DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, TRUE, NULL);
2542 if (!NT_SUCCESS(Status)) {
2543 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
2544 ObDereferenceObject(fileobj);
2545 return Status;
2546 }
2547
2548 Status = dev_ioctl(fileobj->DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2549 &gli, sizeof(gli), TRUE, NULL);
2550 if (!NT_SUCCESS(Status)) {
2551 ERR("error reading length information: %08x\n", Status);
2552 ObDereferenceObject(fileobj);
2553 return Status;
2554 }
2555
2556 if (gli.Length.QuadPart < 0x100000) {
2557 ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", gli.Length.QuadPart);
2558 ObDereferenceObject(fileobj);
2559 return STATUS_INTERNAL_ERROR;
2560 }
2561
2562 Status = dev_ioctl(fileobj->DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0,
2563 &mdn1, sizeof(MOUNTDEV_NAME), TRUE, NULL);
2564 if (Status == STATUS_BUFFER_OVERFLOW) {
2565 mdn2 = ExAllocatePoolWithTag(PagedPool, offsetof(MOUNTDEV_NAME, Name[0]) + mdn1.NameLength, ALLOC_TAG);
2566 if (!mdn2) {
2567 ERR("out of memory\n");
2568 ObDereferenceObject(fileobj);
2569 return STATUS_INTERNAL_ERROR;
2570 }
2571
2572 Status = dev_ioctl(fileobj->DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0,
2573 mdn2, offsetof(MOUNTDEV_NAME, Name[0]) + mdn1.NameLength, TRUE, NULL);
2574
2575 if (!NT_SUCCESS(Status)) {
2576 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
2577 ObDereferenceObject(fileobj);
2578 return Status;
2579 }
2580 } else if (NT_SUCCESS(Status)) {
2581 mdn2 = ExAllocatePoolWithTag(PagedPool, sizeof(MOUNTDEV_NAME), ALLOC_TAG);
2582 if (!mdn2) {
2583 ERR("out of memory\n");
2584 ObDereferenceObject(fileobj);
2585 return STATUS_INTERNAL_ERROR;
2586 }
2587
2588 RtlCopyMemory(mdn2, &mdn1, sizeof(MOUNTDEV_NAME));
2589 } else {
2590 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
2591 ObDereferenceObject(fileobj);
2592 return Status;
2593 }
2594
2595 if (mdn2->NameLength == 0) {
2596 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned zero-length name\n");
2597 ObDereferenceObject(fileobj);
2598 ExFreePool(mdn2);
2599 return STATUS_INTERNAL_ERROR;
2600 }
2601
2602 volname.Length = volname.MaximumLength = mdn2->NameLength;
2603 volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG);
2604 if (!volname.Buffer) {
2605 ERR("out of memory\n");
2606 ObDereferenceObject(fileobj);
2607 ExFreePool(mdn2);
2608 return STATUS_INSUFFICIENT_RESOURCES;
2609 }
2610
2611 RtlCopyMemory(volname.Buffer, mdn2->Name, volname.Length);
2612 ExFreePool(mdn2);
2613
2614 InitializeListHead(&rollback);
2615
2616 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2617
2618 if (Vcb->need_write) {
2619 Status = do_write(Vcb, Irp, &rollback);
2620 if (!NT_SUCCESS(Status)) {
2621 ERR("do_write returned %08x\n", Status);
2622 do_rollback(Vcb, &rollback);
2623 goto end;
2624 }
2625 }
2626
2627 free_trees(Vcb);
2628
2629 clear_rollback(Vcb, &rollback);
2630
2631 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
2632 if (!dev) {
2633 ERR("out of memory\n");
2634 Status = STATUS_INSUFFICIENT_RESOURCES;
2635 goto end;
2636 }
2637
2638 RtlZeroMemory(dev, sizeof(device));
2639
2640 dev->devobj = fileobj->DeviceObject;
2641 dev->seeding = FALSE;
2642 dev->length = gli.Length.QuadPart;
2643 init_device(Vcb, dev, FALSE, TRUE);
2644
2645 InitializeListHead(&dev->space);
2646
2647 if (gli.Length.QuadPart > 0x100000) { // add disk hole - the first MB is marked as used
2648 Status = add_space_entry(&dev->space, NULL, 0x100000, gli.Length.QuadPart - 0x100000);
2649 if (!NT_SUCCESS(Status)) {
2650 ERR("add_space_entry returned %08x\n", Status);
2651 Status = STATUS_INTERNAL_ERROR;
2652 goto end;
2653 }
2654 }
2655
2656 dev_id = 0;
2657
2658 le = Vcb->devices.Flink;
2659 while (le != &Vcb->devices) {
2660 device* dev = CONTAINING_RECORD(le, device, list_entry);
2661
2662 if (dev->devitem.dev_id > dev_id)
2663 dev_id = dev->devitem.dev_id;
2664
2665 le = le->Flink;
2666 }
2667
2668 dev_id++;
2669
2670 dev->devitem.dev_id = dev_id;
2671 dev->devitem.num_bytes = gli.Length.QuadPart;
2672 dev->devitem.bytes_used = 0;
2673 dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2674 dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2675 dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2676 dev->devitem.type = 0;
2677 dev->devitem.generation = 0;
2678 dev->devitem.start_offset = 0;
2679 dev->devitem.dev_group = 0;
2680 dev->devitem.seek_speed = 0;
2681 dev->devitem.bandwidth = 0;
2682 get_uuid(&dev->devitem.device_uuid);
2683 dev->devitem.fs_uuid = Vcb->superblock.uuid;
2684
2685 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
2686 if (!di) {
2687 ERR("out of memory\n");
2688 goto end;
2689 }
2690
2691 RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2692
2693 if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp, &rollback)) {
2694 ERR("insert_tree_item failed\n");
2695 ExFreePool(di);
2696 Status = STATUS_INTERNAL_ERROR;
2697 goto end;
2698 }
2699
2700 // add stats entry to dev tree
2701 stats = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * 5, ALLOC_TAG);
2702 if (!stats) {
2703 ERR("out of memory\n");
2704 Status = STATUS_INTERNAL_ERROR;
2705 goto end;
2706 }
2707
2708 RtlZeroMemory(stats, sizeof(UINT64) * 5);
2709
2710 searchkey.obj_id = 0;
2711 searchkey.obj_type = TYPE_DEV_STATS;
2712 searchkey.offset = di->dev_id;
2713
2714 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2715 if (!NT_SUCCESS(Status)) {
2716 ERR("error - find_item returned %08x\n", Status);
2717 goto end;
2718 }
2719
2720 if (!keycmp(tp.item->key, searchkey))
2721 delete_tree_item(Vcb, &tp, &rollback);
2722
2723 if (!insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(UINT64) * 5, NULL, Irp, &rollback)) {
2724 ERR("insert_tree_item failed\n");
2725 ExFreePool(stats);
2726 Status = STATUS_INTERNAL_ERROR;
2727 goto end;
2728 }
2729
2730 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2731 mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
2732 if (!mb) {
2733 ERR("out of memory\n");
2734 Status = STATUS_INSUFFICIENT_RESOURCES;
2735 goto end;
2736 }
2737
2738 RtlZeroMemory(mb, 0x100000);
2739
2740 Status = write_data_phys(fileobj->DeviceObject, 0, mb, 0x100000);
2741 if (!NT_SUCCESS(Status)) {
2742 ERR("write_data_phys returned %08x\n", Status);
2743 goto end;
2744 }
2745
2746 ExFreePool(mb);
2747
2748 v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
2749 if (!v) {
2750 ERR("out of memory\n");
2751 Status = STATUS_INSUFFICIENT_RESOURCES;
2752 goto end;
2753 }
2754
2755 v->fsuuid = Vcb->superblock.uuid;
2756 v->devuuid = dev->devitem.device_uuid;
2757 v->devnum = dev_id;
2758 v->devpath = volname;
2759 v->length = gli.Length.QuadPart;
2760 v->gen1 = v->gen2 = Vcb->superblock.generation;
2761 v->seeding = FALSE;
2762 v->processed = TRUE;
2763
2764 ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
2765 InsertTailList(&volumes, &v->list_entry);
2766 ExReleaseResourceLite(&volumes_lock);
2767
2768 volname.Buffer = NULL;
2769
2770 Status = dev_ioctl(fileobj->DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
2771 &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
2772 if (!NT_SUCCESS(Status)) {
2773 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
2774 v->disk_num = 0;
2775 v->part_num = 0;
2776 } else {
2777 v->disk_num = sdn.DeviceNumber;
2778 v->part_num = sdn.PartitionNumber;
2779 }
2780
2781 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
2782 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
2783 if (!NT_SUCCESS(Status))
2784 ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
2785 else {
2786 remove_drive_letter(mountmgr, v);
2787
2788 ObDereferenceObject(mountmgrfo);
2789 }
2790
2791 Vcb->superblock.num_devices++;
2792 Vcb->superblock.total_bytes += gli.Length.QuadPart;
2793 Vcb->devices_loaded++;
2794 InsertTailList(&Vcb->devices, &dev->list_entry);
2795
2796 ObReferenceObject(fileobj->DeviceObject);
2797
2798 do_write(Vcb, Irp, &rollback);
2799
2800 free_trees(Vcb);
2801
2802 clear_rollback(Vcb, &rollback);
2803
2804 Status = STATUS_SUCCESS;
2805
2806 end:
2807 ExReleaseResourceLite(&Vcb->tree_lock);
2808 ObDereferenceObject(fileobj);
2809
2810 if (volname.Buffer)
2811 ExFreePool(volname.Buffer);
2812
2813 return Status;
2814 }
2815
2816 static NTSTATUS allow_extended_dasd_io(device_extension* Vcb, PFILE_OBJECT FileObject) {
2817 fcb* fcb;
2818 ccb* ccb;
2819
2820 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
2821
2822 if (!FileObject)
2823 return STATUS_INVALID_PARAMETER;
2824
2825 fcb = FileObject->FsContext;
2826 ccb = FileObject->FsContext2;
2827
2828 if (!fcb)
2829 return STATUS_INVALID_PARAMETER;
2830
2831 if (fcb != Vcb->volume_fcb)
2832 return STATUS_INVALID_PARAMETER;
2833
2834 if (!ccb)
2835 return STATUS_INVALID_PARAMETER;
2836
2837 ccb->allow_extended_dasd_io = TRUE;
2838
2839 return STATUS_SUCCESS;
2840 }
2841
2842 static NTSTATUS query_uuid(device_extension* Vcb, void* data, ULONG length) {
2843 if (length < sizeof(BTRFS_UUID))
2844 return STATUS_BUFFER_OVERFLOW;
2845
2846 RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
2847
2848 return STATUS_SUCCESS;
2849 }
2850
2851 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
2852 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2853 NTSTATUS Status;
2854
2855 switch (type) {
2856 case FSCTL_REQUEST_OPLOCK_LEVEL_1:
2857 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
2858 Status = STATUS_INVALID_DEVICE_REQUEST;
2859 break;
2860
2861 case FSCTL_REQUEST_OPLOCK_LEVEL_2:
2862 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
2863 Status = STATUS_INVALID_DEVICE_REQUEST;
2864 break;
2865
2866 case FSCTL_REQUEST_BATCH_OPLOCK:
2867 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
2868 Status = STATUS_INVALID_DEVICE_REQUEST;
2869 break;
2870
2871 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
2872 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
2873 Status = STATUS_INVALID_DEVICE_REQUEST;
2874 break;
2875
2876 case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
2877 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
2878 Status = STATUS_INVALID_DEVICE_REQUEST;
2879 break;
2880
2881 case FSCTL_OPLOCK_BREAK_NOTIFY:
2882 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
2883 Status = STATUS_INVALID_DEVICE_REQUEST;
2884 break;
2885
2886 case FSCTL_LOCK_VOLUME:
2887 Status = lock_volume(DeviceObject->DeviceExtension, Irp);
2888 break;
2889
2890 case FSCTL_UNLOCK_VOLUME:
2891 Status = unlock_volume(DeviceObject->DeviceExtension, Irp);
2892 break;
2893
2894 case FSCTL_DISMOUNT_VOLUME:
2895 Status = dismount_volume(DeviceObject->DeviceExtension, Irp);
2896 break;
2897
2898 case FSCTL_IS_VOLUME_MOUNTED:
2899 Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp);
2900 break;
2901
2902 case FSCTL_IS_PATHNAME_VALID:
2903 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
2904 Status = STATUS_INVALID_DEVICE_REQUEST;
2905 break;
2906
2907 case FSCTL_MARK_VOLUME_DIRTY:
2908 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
2909 Status = STATUS_INVALID_DEVICE_REQUEST;
2910 break;
2911
2912 case FSCTL_QUERY_RETRIEVAL_POINTERS:
2913 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
2914 Status = STATUS_INVALID_DEVICE_REQUEST;
2915 break;
2916
2917 case FSCTL_GET_COMPRESSION:
2918 Status = get_compression(DeviceObject->DeviceExtension, Irp);
2919 break;
2920
2921 case FSCTL_SET_COMPRESSION:
2922 WARN("STUB: FSCTL_SET_COMPRESSION\n");
2923 Status = STATUS_INVALID_DEVICE_REQUEST;
2924 break;
2925
2926 case FSCTL_SET_BOOTLOADER_ACCESSED:
2927 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
2928 Status = STATUS_INVALID_DEVICE_REQUEST;
2929 break;
2930
2931 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
2932 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
2933 Status = STATUS_INVALID_DEVICE_REQUEST;
2934 break;
2935
2936 case FSCTL_INVALIDATE_VOLUMES:
2937 Status = invalidate_volumes(Irp);
2938 break;
2939
2940 case FSCTL_QUERY_FAT_BPB:
2941 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
2942 Status = STATUS_INVALID_DEVICE_REQUEST;
2943 break;
2944
2945 case FSCTL_REQUEST_FILTER_OPLOCK:
2946 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
2947 Status = STATUS_INVALID_DEVICE_REQUEST;
2948 break;
2949
2950 case FSCTL_FILESYSTEM_GET_STATISTICS:
2951 Status = fs_get_statistics(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
2952 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
2953 break;
2954
2955 case FSCTL_GET_NTFS_VOLUME_DATA:
2956 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
2957 Status = STATUS_INVALID_DEVICE_REQUEST;
2958 break;
2959
2960 case FSCTL_GET_NTFS_FILE_RECORD:
2961 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
2962 Status = STATUS_INVALID_DEVICE_REQUEST;
2963 break;
2964
2965 case FSCTL_GET_VOLUME_BITMAP:
2966 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
2967 Status = STATUS_INVALID_DEVICE_REQUEST;
2968 break;
2969
2970 case FSCTL_GET_RETRIEVAL_POINTERS:
2971 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
2972 Status = STATUS_INVALID_DEVICE_REQUEST;
2973 break;
2974
2975 case FSCTL_MOVE_FILE:
2976 WARN("STUB: FSCTL_MOVE_FILE\n");
2977 Status = STATUS_INVALID_DEVICE_REQUEST;
2978 break;
2979
2980 case FSCTL_IS_VOLUME_DIRTY:
2981 Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp);
2982 break;
2983
2984 case FSCTL_ALLOW_EXTENDED_DASD_IO:
2985 Status = allow_extended_dasd_io(DeviceObject->DeviceExtension, IrpSp->FileObject);
2986 break;
2987
2988 case FSCTL_FIND_FILES_BY_SID:
2989 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
2990 Status = STATUS_INVALID_DEVICE_REQUEST;
2991 break;
2992
2993 case FSCTL_SET_OBJECT_ID:
2994 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
2995 Status = STATUS_INVALID_DEVICE_REQUEST;
2996 break;
2997
2998 case FSCTL_GET_OBJECT_ID:
2999 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
3000 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
3001 break;
3002
3003 case FSCTL_DELETE_OBJECT_ID:
3004 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
3005 Status = STATUS_INVALID_DEVICE_REQUEST;
3006 break;
3007
3008 case FSCTL_SET_REPARSE_POINT:
3009 Status = set_reparse_point(DeviceObject, Irp);
3010 break;
3011
3012 case FSCTL_GET_REPARSE_POINT:
3013 Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
3014 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
3015 break;
3016
3017 case FSCTL_DELETE_REPARSE_POINT:
3018 Status = delete_reparse_point(DeviceObject, Irp);
3019 break;
3020
3021 case FSCTL_ENUM_USN_DATA:
3022 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
3023 Status = STATUS_INVALID_DEVICE_REQUEST;
3024 break;
3025
3026 case FSCTL_SECURITY_ID_CHECK:
3027 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
3028 Status = STATUS_INVALID_DEVICE_REQUEST;
3029 break;
3030
3031 case FSCTL_READ_USN_JOURNAL:
3032 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
3033 Status = STATUS_INVALID_DEVICE_REQUEST;
3034 break;
3035
3036 case FSCTL_SET_OBJECT_ID_EXTENDED:
3037 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
3038 Status = STATUS_INVALID_DEVICE_REQUEST;
3039 break;
3040
3041 case FSCTL_CREATE_OR_GET_OBJECT_ID:
3042 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
3043 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
3044 break;
3045
3046 case FSCTL_SET_SPARSE:
3047 Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
3048 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
3049 break;
3050
3051 case FSCTL_SET_ZERO_DATA:
3052 Status = set_zero_data(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
3053 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
3054 break;
3055
3056 case FSCTL_QUERY_ALLOCATED_RANGES:
3057 Status = query_ranges(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
3058 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer,
3059 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
3060 break;
3061
3062 case FSCTL_ENABLE_UPGRADE:
3063 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
3064 Status = STATUS_INVALID_DEVICE_REQUEST;
3065 break;
3066
3067 case FSCTL_SET_ENCRYPTION:
3068 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
3069 Status = STATUS_INVALID_DEVICE_REQUEST;
3070 break;
3071
3072 case FSCTL_ENCRYPTION_FSCTL_IO:
3073 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
3074 Status = STATUS_INVALID_DEVICE_REQUEST;
3075 break;
3076
3077 case FSCTL_WRITE_RAW_ENCRYPTED:
3078 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
3079 Status = STATUS_INVALID_DEVICE_REQUEST;
3080 break;
3081
3082 case FSCTL_READ_RAW_ENCRYPTED:
3083 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
3084 Status = STATUS_INVALID_DEVICE_REQUEST;
3085 break;
3086
3087 case FSCTL_CREATE_USN_JOURNAL:
3088 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
3089 Status = STATUS_INVALID_DEVICE_REQUEST;
3090 break;
3091
3092 case FSCTL_READ_FILE_USN_DATA:
3093 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
3094 Status = STATUS_INVALID_DEVICE_REQUEST;
3095 break;
3096
3097 case FSCTL_WRITE_USN_CLOSE_RECORD:
3098 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
3099 Status = STATUS_INVALID_DEVICE_REQUEST;
3100 break;
3101
3102 case FSCTL_EXTEND_VOLUME:
3103 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
3104 Status = STATUS_INVALID_DEVICE_REQUEST;
3105 break;
3106
3107 case FSCTL_QUERY_USN_JOURNAL:
3108 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
3109 Status = STATUS_INVALID_DEVICE_REQUEST;
3110 break;
3111
3112 case FSCTL_DELETE_USN_JOURNAL:
3113 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
3114 Status = STATUS_INVALID_DEVICE_REQUEST;
3115 break;
3116
3117 case FSCTL_MARK_HANDLE:
3118 WARN("STUB: FSCTL_MARK_HANDLE\n");
3119 Status = STATUS_INVALID_DEVICE_REQUEST;
3120 break;
3121
3122 case FSCTL_SIS_COPYFILE:
3123 WARN("STUB: FSCTL_SIS_COPYFILE\n");
3124 Status = STATUS_INVALID_DEVICE_REQUEST;
3125 break;
3126
3127 case FSCTL_SIS_LINK_FILES:
3128 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
3129 Status = STATUS_INVALID_DEVICE_REQUEST;
3130 break;
3131
3132 case FSCTL_RECALL_FILE:
3133 WARN("STUB: FSCTL_RECALL_FILE\n");
3134 Status = STATUS_INVALID_DEVICE_REQUEST;
3135 break;
3136
3137 case FSCTL_READ_FROM_PLEX:
3138 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
3139 Status = STATUS_INVALID_DEVICE_REQUEST;
3140 break;
3141
3142 case FSCTL_FILE_PREFETCH:
3143 WARN("STUB: FSCTL_FILE_PREFETCH\n");
3144 Status = STATUS_INVALID_DEVICE_REQUEST;
3145 break;
3146
3147 #if WIN32_WINNT >= 0x0600
3148 case FSCTL_MAKE_MEDIA_COMPATIBLE:
3149 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
3150 Status = STATUS_INVALID_DEVICE_REQUEST;
3151 break;
3152
3153 case FSCTL_SET_DEFECT_MANAGEMENT:
3154 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
3155 Status = STATUS_INVALID_DEVICE_REQUEST;
3156 break;
3157
3158 case FSCTL_QUERY_SPARING_INFO:
3159 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
3160 Status = STATUS_INVALID_DEVICE_REQUEST;
3161 break;
3162
3163 case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
3164 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
3165 Status = STATUS_INVALID_DEVICE_REQUEST;
3166 break;
3167
3168 case FSCTL_SET_VOLUME_COMPRESSION_STATE:
3169 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
3170 Status = STATUS_INVALID_DEVICE_REQUEST;
3171 break;
3172
3173 case FSCTL_TXFS_MODIFY_RM:
3174 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
3175 Status = STATUS_INVALID_DEVICE_REQUEST;
3176 break;
3177
3178 case FSCTL_TXFS_QUERY_RM_INFORMATION:
3179 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
3180 Status = STATUS_INVALID_DEVICE_REQUEST;
3181 break;
3182
3183 case FSCTL_TXFS_ROLLFORWARD_REDO:
3184 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
3185 Status = STATUS_INVALID_DEVICE_REQUEST;
3186 break;
3187
3188 case FSCTL_TXFS_ROLLFORWARD_UNDO:
3189 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
3190 Status = STATUS_INVALID_DEVICE_REQUEST;
3191 break;
3192
3193 case FSCTL_TXFS_START_RM:
3194 WARN("STUB: FSCTL_TXFS_START_RM\n");
3195 Status = STATUS_INVALID_DEVICE_REQUEST;
3196 break;
3197
3198 case FSCTL_TXFS_SHUTDOWN_RM:
3199 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
3200 Status = STATUS_INVALID_DEVICE_REQUEST;
3201 break;
3202
3203 case FSCTL_TXFS_READ_BACKUP_INFORMATION:
3204 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
3205 Status = STATUS_INVALID_DEVICE_REQUEST;
3206 break;
3207
3208 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
3209 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
3210 Status = STATUS_INVALID_DEVICE_REQUEST;
3211 break;
3212
3213 case FSCTL_TXFS_CREATE_SECONDARY_RM:
3214 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
3215 Status = STATUS_INVALID_DEVICE_REQUEST;
3216 break;
3217
3218 case FSCTL_TXFS_GET_METADATA_INFO:
3219 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
3220 Status = STATUS_INVALID_DEVICE_REQUEST;
3221 break;
3222
3223 case FSCTL_TXFS_GET_TRANSACTED_VERSION:
3224 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
3225 Status = STATUS_INVALID_DEVICE_REQUEST;
3226 break;
3227
3228 case FSCTL_TXFS_SAVEPOINT_INFORMATION:
3229 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
3230 Status = STATUS_INVALID_DEVICE_REQUEST;
3231 break;
3232
3233 case FSCTL_TXFS_CREATE_MINIVERSION:
3234 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
3235 Status = STATUS_INVALID_DEVICE_REQUEST;
3236 break;
3237
3238 case FSCTL_TXFS_TRANSACTION_ACTIVE:
3239 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
3240 Status = STATUS_INVALID_DEVICE_REQUEST;
3241 break;
3242
3243 case FSCTL_SET_ZERO_ON_DEALLOCATION:
3244 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
3245 Status = STATUS_INVALID_DEVICE_REQUEST;
3246 break;
3247
3248 case FSCTL_SET_REPAIR:
3249 WARN("STUB: FSCTL_SET_REPAIR\n");
3250 Status = STATUS_INVALID_DEVICE_REQUEST;
3251 break;
3252
3253 case FSCTL_GET_REPAIR:
3254 WARN("STUB: FSCTL_GET_REPAIR\n");
3255 Status = STATUS_INVALID_DEVICE_REQUEST;
3256 break;
3257
3258 case FSCTL_WAIT_FOR_REPAIR:
3259 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
3260 Status = STATUS_INVALID_DEVICE_REQUEST;
3261 break;
3262
3263 case FSCTL_INITIATE_REPAIR:
3264 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
3265 Status = STATUS_INVALID_DEVICE_REQUEST;
3266 break;
3267
3268 case FSCTL_CSC_INTERNAL:
3269 WARN("STUB: FSCTL_CSC_INTERNAL\n");
3270 Status = STATUS_INVALID_DEVICE_REQUEST;
3271 break;
3272
3273 case FSCTL_SHRINK_VOLUME:
3274 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
3275 Status = STATUS_INVALID_DEVICE_REQUEST;
3276 break;
3277
3278 case FSCTL_SET_SHORT_NAME_BEHAVIOR:
3279 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
3280 Status = STATUS_INVALID_DEVICE_REQUEST;
3281 break;
3282
3283 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
3284 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
3285 Status = STATUS_INVALID_DEVICE_REQUEST;
3286 break;
3287
3288 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
3289 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
3290 Status = STATUS_INVALID_DEVICE_REQUEST;
3291 break;
3292
3293 case FSCTL_TXFS_LIST_TRANSACTIONS:
3294 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
3295 Status = STATUS_INVALID_DEVICE_REQUEST;
3296 break;
3297
3298 case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
3299 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
3300 Status = STATUS_INVALID_DEVICE_REQUEST;
3301 break;
3302
3303 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
3304 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
3305 Status = STATUS_INVALID_DEVICE_REQUEST;
3306 break;
3307
3308 case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
3309 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
3310 Status = STATUS_INVALID_DEVICE_REQUEST;
3311 break;
3312
3313 case FSCTL_CSV_CONTROL:
3314 WARN("STUB: FSCTL_CSV_CONTROL\n");
3315 Status = STATUS_INVALID_DEVICE_REQUEST;
3316 break;
3317 #endif
3318 case FSCTL_BTRFS_GET_FILE_IDS:
3319 Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3320 break;
3321
3322 case FSCTL_BTRFS_CREATE_SUBVOL:
3323 Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
3324 break;
3325
3326 case FSCTL_BTRFS_CREATE_SNAPSHOT:
3327 Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
3328 break;
3329
3330 case FSCTL_BTRFS_GET_INODE_INFO:
3331 Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3332 break;
3333
3334 case FSCTL_BTRFS_SET_INODE_INFO:
3335 Status = set_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3336 break;
3337
3338 case FSCTL_BTRFS_GET_DEVICES:
3339 Status = get_devices(DeviceObject->DeviceExtension, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3340 break;
3341
3342 case FSCTL_BTRFS_GET_USAGE:
3343 Status = get_usage(DeviceObject->DeviceExtension, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3344 break;
3345
3346 case FSCTL_BTRFS_START_BALANCE:
3347 Status = start_balance(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
3348 break;
3349
3350 case FSCTL_BTRFS_QUERY_BALANCE:
3351 Status = query_balance(DeviceObject->DeviceExtension, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3352 break;
3353
3354 case FSCTL_BTRFS_PAUSE_BALANCE:
3355 Status = pause_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
3356 break;
3357
3358 case FSCTL_BTRFS_RESUME_BALANCE:
3359 Status = resume_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
3360 break;
3361
3362 case FSCTL_BTRFS_STOP_BALANCE:
3363 Status = stop_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
3364 break;
3365
3366 case FSCTL_BTRFS_ADD_DEVICE:
3367 Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
3368 break;
3369
3370 case FSCTL_BTRFS_REMOVE_DEVICE:
3371 Status = remove_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
3372 break;
3373
3374 case FSCTL_BTRFS_GET_UUID:
3375 Status = query_uuid(DeviceObject->DeviceExtension, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
3376 break;
3377
3378 default:
3379 TRACE("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
3380 IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
3381 (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
3382 IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
3383 Status = STATUS_INVALID_DEVICE_REQUEST;
3384 break;
3385 }
3386
3387 return Status;
3388 }