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