[BTRFS] Upgrade to 1.2.1
[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(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->fcb->nonpaged->dir_children_lock, TRUE);
452 InsertTailList(&fileref->children, &fr->list_entry);
453 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_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(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(fr2);
645 Status = STATUS_OBJECT_NAME_COLLISION;
646 goto end3;
647 } else
648 free_fileref(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(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(fr2);
870 Status = STATUS_OBJECT_NAME_COLLISION;
871 goto end;
872 } else
873 free_fileref(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 r->fcbs_version++;
1009 release_fcb_lock(Vcb);
1010
1011 rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1012 rootfcb->Header.AllocationSize.QuadPart = 0;
1013 rootfcb->Header.FileSize.QuadPart = 0;
1014 rootfcb->Header.ValidDataLength.QuadPart = 0;
1015
1016 rootfcb->created = TRUE;
1017
1018 if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1019 rootfcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
1020
1021 rootfcb->prop_compression = fileref->fcb->prop_compression;
1022 rootfcb->prop_compression_changed = rootfcb->prop_compression != PropCompression_None;
1023
1024 r->lastinode = rootfcb->inode;
1025
1026 // add INODE_REF
1027
1028 irsize = (UINT16)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1029 ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1030 if (!ir) {
1031 ERR("out of memory\n");
1032 Status = STATUS_INSUFFICIENT_RESOURCES;
1033 goto end;
1034 }
1035
1036 ir->index = 0;
1037 ir->n = sizeof(DOTDOT) - 1;
1038 RtlCopyMemory(ir->name, DOTDOT, ir->n);
1039
1040 Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1041 if (!NT_SUCCESS(Status)) {
1042 ERR("insert_tree_item returned %08x\n", Status);
1043 ExFreePool(ir);
1044 goto end;
1045 }
1046
1047 // create fileref for entry in other subvolume
1048
1049 fr = create_fileref(Vcb);
1050 if (!fr) {
1051 ERR("out of memory\n");
1052
1053 reap_fcb(rootfcb);
1054
1055 Status = STATUS_INSUFFICIENT_RESOURCES;
1056 goto end;
1057 }
1058
1059 fr->fcb = rootfcb;
1060
1061 mark_fcb_dirty(rootfcb);
1062
1063 fr->parent = fileref;
1064
1065 Status = add_dir_child(fileref->fcb, r->id, TRUE, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1066 if (!NT_SUCCESS(Status))
1067 WARN("add_dir_child returned %08x\n", Status);
1068
1069 fr->dc = dc;
1070 dc->fileref = fr;
1071
1072 fr->fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1073 if (!fr->fcb->hash_ptrs) {
1074 ERR("out of memory\n");
1075 free_fileref(fr);
1076 Status = STATUS_INSUFFICIENT_RESOURCES;
1077 goto end;
1078 }
1079
1080 RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1081
1082 fr->fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
1083 if (!fr->fcb->hash_ptrs_uc) {
1084 ERR("out of memory\n");
1085 free_fileref(fr);
1086 Status = STATUS_INSUFFICIENT_RESOURCES;
1087 goto end;
1088 }
1089
1090 RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1091
1092 ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
1093 InsertTailList(&fileref->children, &fr->list_entry);
1094 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1095
1096 increase_fileref_refcount(fileref);
1097
1098 if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1099 fr->fcb->fileref = fr;
1100
1101 fr->created = TRUE;
1102 mark_fileref_dirty(fr);
1103
1104 // change fcb->subvol's ROOT_ITEM
1105
1106 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1107 fcb->subvol->root_item.ctime = now;
1108
1109 // change fcb's INODE_ITEM
1110
1111 fcb->inode_item.transid = Vcb->superblock.generation;
1112 fcb->inode_item.st_size += utf8.Length * 2;
1113 fcb->inode_item.sequence++;
1114
1115 if (!ccb->user_set_change_time)
1116 fcb->inode_item.st_ctime = now;
1117
1118 if (!ccb->user_set_write_time)
1119 fcb->inode_item.st_mtime = now;
1120
1121 fcb->inode_item_changed = TRUE;
1122 mark_fcb_dirty(fcb);
1123
1124 fr->fcb->subvol->parent = fcb->subvol->id;
1125
1126 Status = STATUS_SUCCESS;
1127
1128 end:
1129 if (!NT_SUCCESS(Status)) {
1130 if (fr) {
1131 fr->deleted = TRUE;
1132 mark_fileref_dirty(fr);
1133 } else if (rootfcb) {
1134 rootfcb->deleted = TRUE;
1135 mark_fcb_dirty(rootfcb);
1136 }
1137
1138 if (r) {
1139 RemoveEntryList(&r->list_entry);
1140 InsertTailList(&Vcb->drop_roots, &r->list_entry);
1141 }
1142 }
1143
1144 ExReleaseResourceLite(&Vcb->tree_lock);
1145
1146 if (NT_SUCCESS(Status)) {
1147 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL);
1148 send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1149 }
1150
1151 end2:
1152 if (fr)
1153 free_fileref(fr);
1154
1155 return Status;
1156 }
1157
1158 static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
1159 btrfs_inode_info2* bii = data;
1160 fcb* fcb;
1161 ccb* ccb;
1162 BOOL old_style;
1163
1164 if (length < sizeof(btrfs_inode_info))
1165 return STATUS_BUFFER_OVERFLOW;
1166
1167 if (!FileObject)
1168 return STATUS_INVALID_PARAMETER;
1169
1170 fcb = FileObject->FsContext;
1171
1172 if (!fcb)
1173 return STATUS_INVALID_PARAMETER;
1174
1175 ccb = FileObject->FsContext2;
1176
1177 if (!ccb)
1178 return STATUS_INVALID_PARAMETER;
1179
1180 if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1181 WARN("insufficient privileges\n");
1182 return STATUS_ACCESS_DENIED;
1183 }
1184
1185 if (fcb->ads)
1186 fcb = ccb->fileref->parent->fcb;
1187
1188 old_style = length < sizeof(btrfs_inode_info2);
1189
1190 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
1191
1192 bii->subvol = fcb->subvol->id;
1193 bii->inode = fcb->inode;
1194 bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
1195 bii->type = fcb->type;
1196 bii->st_uid = fcb->inode_item.st_uid;
1197 bii->st_gid = fcb->inode_item.st_gid;
1198 bii->st_mode = fcb->inode_item.st_mode;
1199
1200 if (fcb->inode_item.st_rdev == 0)
1201 bii->st_rdev = 0;
1202 else
1203 bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1204
1205 bii->flags = fcb->inode_item.flags;
1206
1207 bii->inline_length = 0;
1208 bii->disk_size_uncompressed = 0;
1209 bii->disk_size_zlib = 0;
1210 bii->disk_size_lzo = 0;
1211
1212 if (!old_style) {
1213 bii->disk_size_zstd = 0;
1214 bii->sparse_size = 0;
1215 }
1216
1217 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1218 UINT64 last_end = 0;
1219 LIST_ENTRY* le;
1220 BOOL extents_inline = FALSE;
1221
1222 le = fcb->extents.Flink;
1223 while (le != &fcb->extents) {
1224 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1225
1226 if (!ext->ignore) {
1227 if (!old_style && ext->offset > last_end)
1228 bii->sparse_size += ext->offset - last_end;
1229
1230 if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1231 bii->inline_length += ext->datalen - (UINT16)offsetof(EXTENT_DATA, data[0]);
1232 last_end = ext->offset + ext->extent_data.decoded_size;
1233 extents_inline = TRUE;
1234 } else {
1235 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1236
1237 // FIXME - compressed extents with a hole in them are counted more than once
1238 if (ed2->size != 0) {
1239 switch (ext->extent_data.compression) {
1240 case BTRFS_COMPRESSION_NONE:
1241 bii->disk_size_uncompressed += ed2->num_bytes;
1242 break;
1243
1244 case BTRFS_COMPRESSION_ZLIB:
1245 bii->disk_size_zlib += ed2->size;
1246 break;
1247
1248 case BTRFS_COMPRESSION_LZO:
1249 bii->disk_size_lzo += ed2->size;
1250 break;
1251
1252 case BTRFS_COMPRESSION_ZSTD:
1253 if (!old_style)
1254 bii->disk_size_zstd += ed2->size;
1255 break;
1256 }
1257 }
1258
1259 last_end = ext->offset + ed2->num_bytes;
1260 }
1261 }
1262
1263 le = le->Flink;
1264 }
1265
1266 if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1267 bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1268 }
1269
1270 switch (fcb->prop_compression) {
1271 case PropCompression_Zlib:
1272 bii->compression_type = BTRFS_COMPRESSION_ZLIB;
1273 break;
1274
1275 case PropCompression_LZO:
1276 bii->compression_type = BTRFS_COMPRESSION_LZO;
1277 break;
1278
1279 case PropCompression_ZSTD:
1280 bii->compression_type = BTRFS_COMPRESSION_ZSTD;
1281 break;
1282
1283 default:
1284 bii->compression_type = BTRFS_COMPRESSION_ANY;
1285 break;
1286 }
1287
1288 ExReleaseResourceLite(fcb->Header.Resource);
1289
1290 return STATUS_SUCCESS;
1291 }
1292
1293 static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1294 btrfs_set_inode_info* bsii = data;
1295 NTSTATUS Status;
1296 fcb* fcb;
1297 ccb* ccb;
1298
1299 if (length < sizeof(btrfs_set_inode_info))
1300 return STATUS_INVALID_PARAMETER;
1301
1302 if (!FileObject)
1303 return STATUS_INVALID_PARAMETER;
1304
1305 fcb = FileObject->FsContext;
1306
1307 if (!fcb)
1308 return STATUS_INVALID_PARAMETER;
1309
1310 ccb = FileObject->FsContext2;
1311
1312 if (!ccb)
1313 return STATUS_INVALID_PARAMETER;
1314
1315 if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1316 WARN("insufficient privileges\n");
1317 return STATUS_ACCESS_DENIED;
1318 }
1319
1320 if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1321 WARN("insufficient privileges\n");
1322 return STATUS_ACCESS_DENIED;
1323 }
1324
1325 if (bsii->compression_type_changed && bsii->compression_type > BTRFS_COMPRESSION_ZSTD)
1326 return STATUS_INVALID_PARAMETER;
1327
1328 if (fcb->ads)
1329 fcb = ccb->fileref->parent->fcb;
1330
1331 if (is_subvol_readonly(fcb->subvol, Irp)) {
1332 WARN("trying to change inode on readonly subvolume\n");
1333 return STATUS_ACCESS_DENIED;
1334 }
1335
1336 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1337
1338 if (bsii->flags_changed) {
1339 if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1340 (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
1341 WARN("trying to change nocow flag on non-empty file\n");
1342 Status = STATUS_INVALID_PARAMETER;
1343 goto end;
1344 }
1345
1346 fcb->inode_item.flags = bsii->flags;
1347
1348 if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
1349 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
1350 else
1351 fcb->inode_item.flags &= ~(UINT64)BTRFS_INODE_NODATASUM;
1352 }
1353
1354 if (bsii->mode_changed) {
1355 UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH |
1356 S_ISGID | S_ISVTX;
1357
1358 if (ccb->access & WRITE_OWNER)
1359 allowed |= S_ISUID;
1360
1361 fcb->inode_item.st_mode &= ~allowed;
1362 fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1363 }
1364
1365 if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1366 fcb->inode_item.st_uid = bsii->st_uid;
1367
1368 fcb->sd_dirty = TRUE;
1369 fcb->sd_deleted = FALSE;
1370 }
1371
1372 if (bsii->gid_changed)
1373 fcb->inode_item.st_gid = bsii->st_gid;
1374
1375 if (bsii->compression_type_changed) {
1376 switch (bsii->compression_type) {
1377 case BTRFS_COMPRESSION_ANY:
1378 fcb->prop_compression = PropCompression_None;
1379 break;
1380
1381 case BTRFS_COMPRESSION_ZLIB:
1382 fcb->prop_compression = PropCompression_Zlib;
1383 break;
1384
1385 case BTRFS_COMPRESSION_LZO:
1386 fcb->prop_compression = PropCompression_LZO;
1387 break;
1388
1389 case BTRFS_COMPRESSION_ZSTD:
1390 fcb->prop_compression = PropCompression_ZSTD;
1391 break;
1392 }
1393
1394 fcb->prop_compression_changed = TRUE;
1395 }
1396
1397 if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1398 fcb->inode_item_changed = TRUE;
1399 mark_fcb_dirty(fcb);
1400 }
1401
1402 Status = STATUS_SUCCESS;
1403
1404 end:
1405 ExReleaseResourceLite(fcb->Header.Resource);
1406
1407 return Status;
1408 }
1409
1410 static NTSTATUS get_devices(device_extension* Vcb, void* data, ULONG length) {
1411 btrfs_device* dev = NULL;
1412 NTSTATUS Status;
1413 LIST_ENTRY* le;
1414
1415 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1416
1417 le = Vcb->devices.Flink;
1418 while (le != &Vcb->devices) {
1419 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1420 ULONG structlen;
1421
1422 if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1423 Status = STATUS_BUFFER_OVERFLOW;
1424 goto end;
1425 }
1426
1427 if (!dev)
1428 dev = data;
1429 else {
1430 dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1431 dev = (btrfs_device*)((UINT8*)dev + dev->next_entry);
1432 }
1433
1434 structlen = length - offsetof(btrfs_device, namelen);
1435
1436 if (dev2->devobj) {
1437 Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, TRUE, NULL);
1438 if (!NT_SUCCESS(Status))
1439 goto end;
1440
1441 dev->missing = FALSE;
1442 } else {
1443 dev->namelen = 0;
1444 dev->missing = TRUE;
1445 }
1446
1447 dev->next_entry = 0;
1448 dev->dev_id = dev2->devitem.dev_id;
1449 dev->readonly = (Vcb->readonly || dev2->readonly) ? TRUE : FALSE;
1450 dev->device_number = dev2->disk_num;
1451 dev->partition_number = dev2->part_num;
1452 dev->size = dev2->devitem.num_bytes;
1453
1454 if (dev2->devobj) {
1455 GET_LENGTH_INFORMATION gli;
1456
1457 Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL);
1458 if (!NT_SUCCESS(Status))
1459 goto end;
1460
1461 dev->max_size = gli.Length.QuadPart;
1462 } else
1463 dev->max_size = dev->size;
1464
1465 RtlCopyMemory(dev->stats, dev2->stats, sizeof(UINT64) * 5);
1466
1467 length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1468
1469 le = le->Flink;
1470 }
1471
1472 end:
1473 ExReleaseResourceLite(&Vcb->tree_lock);
1474
1475 return Status;
1476 }
1477
1478 static NTSTATUS get_usage(device_extension* Vcb, void* data, ULONG length, PIRP Irp) {
1479 btrfs_usage* usage = (btrfs_usage*)data;
1480 btrfs_usage* lastbue = NULL;
1481 NTSTATUS Status;
1482 LIST_ENTRY* le;
1483
1484 if (length < sizeof(btrfs_usage))
1485 return STATUS_BUFFER_OVERFLOW;
1486
1487 if (!Vcb->chunk_usage_found) {
1488 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1489
1490 if (!Vcb->chunk_usage_found)
1491 Status = find_chunk_usage(Vcb, Irp);
1492 else
1493 Status = STATUS_SUCCESS;
1494
1495 ExReleaseResourceLite(&Vcb->tree_lock);
1496
1497 if (!NT_SUCCESS(Status)) {
1498 ERR("find_chunk_usage returned %08x\n", Status);
1499 return Status;
1500 }
1501 }
1502
1503 length -= offsetof(btrfs_usage, devices);
1504
1505 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
1506
1507 le = Vcb->chunks.Flink;
1508 while (le != &Vcb->chunks) {
1509 BOOL addnew = FALSE;
1510
1511 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1512
1513 if (!lastbue) // first entry
1514 addnew = TRUE;
1515 else {
1516 btrfs_usage* bue = usage;
1517
1518 addnew = TRUE;
1519
1520 while (TRUE) {
1521 if (bue->type == c->chunk_item->type) {
1522 addnew = FALSE;
1523 break;
1524 }
1525
1526 if (bue->next_entry == 0)
1527 break;
1528 else
1529 bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
1530 }
1531 }
1532
1533 if (addnew) {
1534 btrfs_usage* bue;
1535 LIST_ENTRY* le2;
1536 UINT64 factor;
1537
1538 if (!lastbue) {
1539 bue = usage;
1540 } else {
1541 if (length < offsetof(btrfs_usage, devices)) {
1542 Status = STATUS_BUFFER_OVERFLOW;
1543 goto end;
1544 }
1545
1546 length -= offsetof(btrfs_usage, devices);
1547
1548 lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1549
1550 bue = (btrfs_usage*)((UINT8*)lastbue + lastbue->next_entry);
1551 }
1552
1553 bue->next_entry = 0;
1554 bue->type = c->chunk_item->type;
1555 bue->size = 0;
1556 bue->used = 0;
1557 bue->num_devices = 0;
1558
1559 if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1560 factor = c->chunk_item->num_stripes;
1561 else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1562 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1563 else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1564 factor = c->chunk_item->num_stripes - 1;
1565 else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1566 factor = c->chunk_item->num_stripes - 2;
1567 else
1568 factor = 1;
1569
1570 le2 = le;
1571 while (le2 != &Vcb->chunks) {
1572 chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1573
1574 if (c2->chunk_item->type == c->chunk_item->type) {
1575 UINT16 i;
1576 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c2->chunk_item[1];
1577 UINT64 stripesize;
1578
1579 bue->size += c2->chunk_item->size;
1580 bue->used += c2->used;
1581
1582 stripesize = c2->chunk_item->size / factor;
1583
1584 for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1585 UINT64 j;
1586 BOOL found = FALSE;
1587
1588 for (j = 0; j < bue->num_devices; j++) {
1589 if (bue->devices[j].dev_id == cis[i].dev_id) {
1590 bue->devices[j].alloc += stripesize;
1591 found = TRUE;
1592 break;
1593 }
1594 }
1595
1596 if (!found) {
1597 if (length < sizeof(btrfs_usage_device)) {
1598 Status = STATUS_BUFFER_OVERFLOW;
1599 goto end;
1600 }
1601
1602 length -= sizeof(btrfs_usage_device);
1603
1604 bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1605 bue->devices[bue->num_devices].alloc = stripesize;
1606 bue->num_devices++;
1607 }
1608 }
1609 }
1610
1611 le2 = le2->Flink;
1612 }
1613
1614 lastbue = bue;
1615 }
1616
1617 le = le->Flink;
1618 }
1619
1620 Status = STATUS_SUCCESS;
1621
1622 end:
1623 ExReleaseResourceLite(&Vcb->chunk_lock);
1624
1625 return Status;
1626 }
1627
1628 static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) {
1629 NTSTATUS Status;
1630 ULONG cc;
1631 IO_STATUS_BLOCK iosb;
1632 BOOL verify = FALSE;
1633 LIST_ENTRY* le;
1634
1635 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1636
1637 le = Vcb->devices.Flink;
1638 while (le != &Vcb->devices) {
1639 device* dev = CONTAINING_RECORD(le, device, list_entry);
1640
1641 if (dev->devobj && dev->removable) {
1642 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), FALSE, &iosb);
1643
1644 if (iosb.Information != sizeof(ULONG))
1645 cc = 0;
1646
1647 if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1648 dev->devobj->Flags |= DO_VERIFY_VOLUME;
1649 verify = TRUE;
1650 }
1651
1652 if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1653 dev->change_count = cc;
1654
1655 if (!NT_SUCCESS(Status) || verify) {
1656 IoSetHardErrorOrVerifyDevice(Irp, dev->devobj);
1657 ExReleaseResourceLite(&Vcb->tree_lock);
1658
1659 return verify ? STATUS_VERIFY_REQUIRED : Status;
1660 }
1661 }
1662
1663 le = le->Flink;
1664 }
1665
1666 ExReleaseResourceLite(&Vcb->tree_lock);
1667
1668 return STATUS_SUCCESS;
1669 }
1670
1671 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1672 FILESYSTEM_STATISTICS* fss;
1673
1674 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1675
1676 // This is hideously wrong, but at least it stops SMB from breaking
1677
1678 if (buflen < sizeof(FILESYSTEM_STATISTICS))
1679 return STATUS_BUFFER_TOO_SMALL;
1680
1681 fss = buffer;
1682 RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1683
1684 fss->Version = 1;
1685 fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
1686 fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS);
1687
1688 *retlen = sizeof(FILESYSTEM_STATISTICS);
1689
1690 return STATUS_SUCCESS;
1691 }
1692
1693 static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1694 FILE_SET_SPARSE_BUFFER* fssb = data;
1695 NTSTATUS Status;
1696 BOOL set;
1697 fcb* fcb;
1698 ccb* ccb = FileObject->FsContext2;
1699 file_ref* fileref = ccb ? ccb->fileref : NULL;
1700
1701 if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1702 return STATUS_INVALID_PARAMETER;
1703
1704 if (!FileObject) {
1705 ERR("FileObject was NULL\n");
1706 return STATUS_INVALID_PARAMETER;
1707 }
1708
1709 fcb = FileObject->FsContext;
1710
1711 if (!fcb) {
1712 ERR("FCB was NULL\n");
1713 return STATUS_INVALID_PARAMETER;
1714 }
1715
1716 if (!ccb) {
1717 ERR("CCB was NULL\n");
1718 return STATUS_INVALID_PARAMETER;
1719 }
1720
1721 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1722 WARN("insufficient privileges\n");
1723 return STATUS_ACCESS_DENIED;
1724 }
1725
1726 if (!fileref) {
1727 ERR("no fileref\n");
1728 return STATUS_INVALID_PARAMETER;
1729 }
1730
1731 if (fcb->ads) {
1732 fileref = fileref->parent;
1733 fcb = fileref->fcb;
1734 }
1735
1736 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1737 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1738
1739 if (fcb->type != BTRFS_TYPE_FILE) {
1740 WARN("FileObject did not point to a file\n");
1741 Status = STATUS_INVALID_PARAMETER;
1742 goto end;
1743 }
1744
1745 if (fssb)
1746 set = fssb->SetSparse;
1747 else
1748 set = TRUE;
1749
1750 if (set) {
1751 fcb->atts |= FILE_ATTRIBUTE_SPARSE_FILE;
1752 fcb->atts_changed = TRUE;
1753 } else {
1754 ULONG defda;
1755
1756 fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE;
1757 fcb->atts_changed = TRUE;
1758
1759 defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type,
1760 fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
1761
1762 fcb->atts_deleted = defda == fcb->atts;
1763 }
1764
1765 mark_fcb_dirty(fcb);
1766 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
1767
1768 Status = STATUS_SUCCESS;
1769
1770 end:
1771 ExReleaseResourceLite(fcb->Header.Resource);
1772 ExReleaseResourceLite(&Vcb->tree_lock);
1773
1774 return Status;
1775 }
1776
1777 static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
1778 NTSTATUS Status;
1779 BOOL make_inline, compress;
1780 UINT64 start_data, end_data;
1781 ULONG buf_head;
1782 UINT8* data;
1783
1784 make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1785
1786 if (!make_inline)
1787 compress = write_fcb_compressed(fcb);
1788
1789 if (make_inline) {
1790 start_data = 0;
1791 end_data = fcb->inode_item.st_size;
1792 buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1793 } else if (compress) {
1794 start_data = start & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
1795 end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE),
1796 sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1797 buf_head = 0;
1798 } else {
1799 start_data = start & ~(UINT64)(Vcb->superblock.sector_size - 1);
1800 end_data = sector_align(start + length, Vcb->superblock.sector_size);
1801 buf_head = 0;
1802 }
1803
1804 data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1805 if (!data) {
1806 ERR("out of memory\n");
1807 return STATUS_INSUFFICIENT_RESOURCES;
1808 }
1809
1810 RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1811
1812 if (start > start_data || start + length < end_data) {
1813 Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1814
1815 if (!NT_SUCCESS(Status)) {
1816 ERR("read_file returned %08x\n", Status);
1817 ExFreePool(data);
1818 return Status;
1819 }
1820 }
1821
1822 RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1823
1824 if (make_inline) {
1825 UINT16 edsize;
1826 EXTENT_DATA* ed = (EXTENT_DATA*)data;
1827
1828 Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1829 if (!NT_SUCCESS(Status)) {
1830 ERR("excise_extents returned %08x\n", Status);
1831 ExFreePool(data);
1832 return Status;
1833 }
1834
1835 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end_data);
1836
1837 ed->generation = Vcb->superblock.generation;
1838 ed->decoded_size = end_data;
1839 ed->compression = BTRFS_COMPRESSION_NONE;
1840 ed->encryption = BTRFS_ENCRYPTION_NONE;
1841 ed->encoding = BTRFS_ENCODING_NONE;
1842 ed->type = EXTENT_TYPE_INLINE;
1843
1844 Status = add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, NULL, rollback);
1845 if (!NT_SUCCESS(Status)) {
1846 ERR("add_extent_to_fcb returned %08x\n", Status);
1847 ExFreePool(data);
1848 return Status;
1849 }
1850
1851 ExFreePool(data);
1852
1853 fcb->inode_item.st_blocks += end_data;
1854 } else if (compress) {
1855 Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback);
1856
1857 ExFreePool(data);
1858
1859 if (!NT_SUCCESS(Status)) {
1860 ERR("write_compressed returned %08x\n", Status);
1861 return Status;
1862 }
1863 } else {
1864 Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
1865
1866 ExFreePool(data);
1867
1868 if (!NT_SUCCESS(Status)) {
1869 ERR("do_write_file returned %08x\n", Status);
1870 return Status;
1871 }
1872 }
1873
1874 return STATUS_SUCCESS;
1875 }
1876
1877 static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
1878 FILE_ZERO_DATA_INFORMATION* fzdi = data;
1879 NTSTATUS Status;
1880 fcb* fcb;
1881 ccb* ccb;
1882 file_ref* fileref;
1883 LIST_ENTRY rollback, *le;
1884 LARGE_INTEGER time;
1885 BTRFS_TIME now;
1886 UINT64 start, end;
1887 extent* ext;
1888 IO_STATUS_BLOCK iosb;
1889
1890 if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1891 return STATUS_INVALID_PARAMETER;
1892
1893 if (!FileObject) {
1894 ERR("FileObject was NULL\n");
1895 return STATUS_INVALID_PARAMETER;
1896 }
1897
1898 if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1899 WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1900 return STATUS_INVALID_PARAMETER;
1901 }
1902
1903 fcb = FileObject->FsContext;
1904
1905 if (!fcb) {
1906 ERR("FCB was NULL\n");
1907 return STATUS_INVALID_PARAMETER;
1908 }
1909
1910 ccb = FileObject->FsContext2;
1911
1912 if (!ccb) {
1913 ERR("ccb was NULL\n");
1914 return STATUS_INVALID_PARAMETER;
1915 }
1916
1917 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1918 WARN("insufficient privileges\n");
1919 return STATUS_ACCESS_DENIED;
1920 }
1921
1922 fileref = ccb->fileref;
1923
1924 if (!fileref) {
1925 ERR("fileref was NULL\n");
1926 return STATUS_INVALID_PARAMETER;
1927 }
1928
1929 InitializeListHead(&rollback);
1930
1931 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1932 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1933
1934 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
1935
1936 if (fcb->type != BTRFS_TYPE_FILE) {
1937 WARN("FileObject did not point to a file\n");
1938 Status = STATUS_INVALID_PARAMETER;
1939 goto end;
1940 }
1941
1942 if (fcb->ads) {
1943 ERR("FileObject is stream\n");
1944 Status = STATUS_INVALID_PARAMETER;
1945 goto end;
1946 }
1947
1948 if ((UINT64)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1949 Status = STATUS_SUCCESS;
1950 goto end;
1951 }
1952
1953 ext = NULL;
1954 le = fcb->extents.Flink;
1955 while (le != &fcb->extents) {
1956 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
1957
1958 if (!ext2->ignore) {
1959 ext = ext2;
1960 break;
1961 }
1962
1963 le = le->Flink;
1964 }
1965
1966 if (!ext) {
1967 Status = STATUS_SUCCESS;
1968 goto end;
1969 }
1970
1971 if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1972 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1973 if (!NT_SUCCESS(Status)) {
1974 ERR("zero_data returned %08x\n", Status);
1975 goto end;
1976 }
1977 } else {
1978 start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
1979
1980 if ((UINT64)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
1981 end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
1982 else
1983 end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
1984
1985 if (end <= start) {
1986 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1987 if (!NT_SUCCESS(Status)) {
1988 ERR("zero_data returned %08x\n", Status);
1989 goto end;
1990 }
1991 } else {
1992 if (start > (UINT64)fzdi->FileOffset.QuadPart) {
1993 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
1994 if (!NT_SUCCESS(Status)) {
1995 ERR("zero_data returned %08x\n", Status);
1996 goto end;
1997 }
1998 }
1999
2000 if (end < (UINT64)fzdi->BeyondFinalZero.QuadPart) {
2001 Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2002 if (!NT_SUCCESS(Status)) {
2003 ERR("zero_data returned %08x\n", Status);
2004 goto end;
2005 }
2006 }
2007
2008 if (end > start) {
2009 Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback);
2010 if (!NT_SUCCESS(Status)) {
2011 ERR("excise_extents returned %08x\n", Status);
2012 goto end;
2013 }
2014 }
2015 }
2016 }
2017
2018 CcPurgeCacheSection(&fcb->nonpaged->segment_object, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), FALSE);
2019
2020 KeQuerySystemTime(&time);
2021 win_time_to_unix(time, &now);
2022
2023 fcb->inode_item.transid = Vcb->superblock.generation;
2024 fcb->inode_item.sequence++;
2025
2026 if (!ccb->user_set_change_time)
2027 fcb->inode_item.st_ctime = now;
2028
2029 if (!ccb->user_set_write_time)
2030 fcb->inode_item.st_mtime = now;
2031
2032 fcb->extents_changed = TRUE;
2033 fcb->inode_item_changed = TRUE;
2034 mark_fcb_dirty(fcb);
2035
2036 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2037
2038 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2039 fcb->subvol->root_item.ctime = now;
2040
2041 Status = STATUS_SUCCESS;
2042
2043 end:
2044 if (!NT_SUCCESS(Status))
2045 do_rollback(Vcb, &rollback);
2046 else
2047 clear_rollback(&rollback);
2048
2049 ExReleaseResourceLite(fcb->Header.Resource);
2050 ExReleaseResourceLite(&Vcb->tree_lock);
2051
2052 return Status;
2053 }
2054
2055 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2056 NTSTATUS Status;
2057 fcb* fcb;
2058 LIST_ENTRY* le;
2059 FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2060 ULONG i = 0;
2061 UINT64 last_start, last_end;
2062
2063 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2064
2065 if (!FileObject) {
2066 ERR("FileObject was NULL\n");
2067 return STATUS_INVALID_PARAMETER;
2068 }
2069
2070 if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2071 return STATUS_INVALID_PARAMETER;
2072
2073 fcb = FileObject->FsContext;
2074
2075 if (!fcb) {
2076 ERR("FCB was NULL\n");
2077 return STATUS_INVALID_PARAMETER;
2078 }
2079
2080 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
2081
2082 // If file is not marked as sparse, claim the whole thing as an allocated range
2083
2084 if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2085 if (fcb->inode_item.st_size == 0)
2086 Status = STATUS_SUCCESS;
2087 else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2088 Status = STATUS_BUFFER_TOO_SMALL;
2089 else {
2090 ranges[i].FileOffset.QuadPart = 0;
2091 ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2092 i++;
2093 Status = STATUS_SUCCESS;
2094 }
2095
2096 goto end;
2097
2098 }
2099
2100 le = fcb->extents.Flink;
2101
2102 last_start = 0;
2103 last_end = 0;
2104
2105 while (le != &fcb->extents) {
2106 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2107
2108 if (!ext->ignore) {
2109 EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2110 UINT64 len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2111
2112 if (ext->offset > last_end) { // first extent after a hole
2113 if (last_end > last_start) {
2114 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2115 ranges[i].FileOffset.QuadPart = last_start;
2116 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2117 i++;
2118 } else {
2119 Status = STATUS_BUFFER_TOO_SMALL;
2120 goto end;
2121 }
2122 }
2123
2124 last_start = ext->offset;
2125 }
2126
2127 last_end = ext->offset + len;
2128 }
2129
2130 le = le->Flink;
2131 }
2132
2133 if (last_end > last_start) {
2134 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2135 ranges[i].FileOffset.QuadPart = last_start;
2136 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2137 i++;
2138 } else {
2139 Status = STATUS_BUFFER_TOO_SMALL;
2140 goto end;
2141 }
2142 }
2143
2144 Status = STATUS_SUCCESS;
2145
2146 end:
2147 *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2148
2149 ExReleaseResourceLite(fcb->Header.Resource);
2150
2151 return Status;
2152 }
2153
2154 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2155 fcb* fcb;
2156
2157 TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen);
2158
2159 if (!FileObject) {
2160 ERR("FileObject was NULL\n");
2161 return STATUS_INVALID_PARAMETER;
2162 }
2163
2164 if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2165 return STATUS_INVALID_PARAMETER;
2166
2167 fcb = FileObject->FsContext;
2168
2169 if (!fcb) {
2170 ERR("FCB was NULL\n");
2171 return STATUS_INVALID_PARAMETER;
2172 }
2173
2174 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
2175
2176 RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(UINT64));
2177 RtlCopyMemory(&buf->ObjectId[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
2178
2179 ExReleaseResourceLite(fcb->Header.Resource);
2180
2181 RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2182
2183 *retlen = sizeof(FILE_OBJECTID_BUFFER);
2184
2185 return STATUS_SUCCESS;
2186 }
2187
2188 static void flush_fcb_caches(device_extension* Vcb) {
2189 LIST_ENTRY* le;
2190
2191 le = Vcb->all_fcbs.Flink;
2192 while (le != &Vcb->all_fcbs) {
2193 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2194 IO_STATUS_BLOCK iosb;
2195
2196 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2197 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2198
2199 le = le->Flink;
2200 }
2201 }
2202
2203 static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
2204 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2205 NTSTATUS Status;
2206 KIRQL irql;
2207 BOOL lock_paused_balance = FALSE;
2208
2209 TRACE("FSCTL_LOCK_VOLUME\n");
2210
2211 if (Vcb->scrub.thread) {
2212 WARN("cannot lock while scrub running\n");
2213 return STATUS_DEVICE_NOT_READY;
2214 }
2215
2216 if (Vcb->balance.thread) {
2217 WARN("cannot lock while balance running\n");
2218 return STATUS_DEVICE_NOT_READY;
2219 }
2220
2221 TRACE("locking volume\n");
2222
2223 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK);
2224
2225 if (Vcb->locked)
2226 return STATUS_SUCCESS;
2227
2228 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
2229
2230 if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2231 Status = STATUS_ACCESS_DENIED;
2232 ExReleaseResourceLite(&Vcb->fileref_lock);
2233 goto end;
2234 }
2235
2236 ExReleaseResourceLite(&Vcb->fileref_lock);
2237
2238 if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2239 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2240 KeClearEvent(&Vcb->balance.event);
2241 ExReleaseResourceLite(&Vcb->tree_lock);
2242
2243 lock_paused_balance = TRUE;
2244 }
2245
2246 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2247
2248 flush_fcb_caches(Vcb);
2249
2250 if (Vcb->need_write && !Vcb->readonly)
2251 Status = do_write(Vcb, Irp);
2252 else
2253 Status = STATUS_SUCCESS;
2254
2255 free_trees(Vcb);
2256
2257 ExReleaseResourceLite(&Vcb->tree_lock);
2258
2259 if (!NT_SUCCESS(Status)) {
2260 ERR("do_write returned %08x\n", Status);
2261 goto end;
2262 }
2263
2264 IoAcquireVpbSpinLock(&irql);
2265
2266 if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2267 Vcb->Vpb->Flags |= VPB_LOCKED;
2268 Vcb->locked = TRUE;
2269 Vcb->locked_fileobj = IrpSp->FileObject;
2270 Vcb->lock_paused_balance = lock_paused_balance;
2271 } else {
2272 Status = STATUS_ACCESS_DENIED;
2273 IoReleaseVpbSpinLock(irql);
2274
2275 if (lock_paused_balance)
2276 KeSetEvent(&Vcb->balance.event, 0, FALSE);
2277
2278 goto end;
2279 }
2280
2281 IoReleaseVpbSpinLock(irql);
2282
2283 Status = STATUS_SUCCESS;
2284
2285 end:
2286 if (!NT_SUCCESS(Status))
2287 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED);
2288
2289 return Status;
2290 }
2291
2292 void do_unlock_volume(device_extension* Vcb) {
2293 KIRQL irql;
2294
2295 IoAcquireVpbSpinLock(&irql);
2296
2297 Vcb->locked = FALSE;
2298 Vcb->Vpb->Flags &= ~VPB_LOCKED;
2299 Vcb->locked_fileobj = NULL;
2300
2301 IoReleaseVpbSpinLock(irql);
2302
2303 if (Vcb->lock_paused_balance)
2304 KeSetEvent(&Vcb->balance.event, 0, FALSE);
2305 }
2306
2307 static NTSTATUS unlock_volume(device_extension* Vcb, PIRP Irp) {
2308 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2309
2310 TRACE("FSCTL_UNLOCK_VOLUME\n");
2311
2312 if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2313 return STATUS_NOT_LOCKED;
2314
2315 TRACE("unlocking volume\n");
2316
2317 do_unlock_volume(Vcb);
2318
2319 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK);
2320
2321 return STATUS_SUCCESS;
2322 }
2323
2324 static NTSTATUS invalidate_volumes(PIRP Irp) {
2325 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2326 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2327 NTSTATUS Status;
2328 HANDLE h;
2329 PFILE_OBJECT fileobj;
2330 PDEVICE_OBJECT devobj;
2331 LIST_ENTRY* le;
2332
2333 TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2334
2335 if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2336 return STATUS_PRIVILEGE_NOT_HELD;
2337
2338 #if defined(_WIN64)
2339 if (IoIs32bitProcess(Irp)) {
2340 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2341 return STATUS_INVALID_PARAMETER;
2342
2343 h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2344 } else {
2345 #endif
2346 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2347 return STATUS_INVALID_PARAMETER;
2348
2349 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2350 #if defined(_WIN64)
2351 }
2352 #endif
2353
2354 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2355
2356 if (!NT_SUCCESS(Status)) {
2357 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2358 return Status;
2359 }
2360
2361 devobj = fileobj->DeviceObject;
2362
2363 ExAcquireResourceSharedLite(&global_loading_lock, TRUE);
2364
2365 le = VcbList.Flink;
2366
2367 while (le != &VcbList) {
2368 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2369
2370 if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2371 if (Vcb->Vpb == devobj->Vpb) {
2372 KIRQL irql;
2373 PVPB newvpb;
2374 BOOL free_newvpb = FALSE;
2375
2376 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2377 if (!newvpb) {
2378 ERR("out of memory\n");
2379 Status = STATUS_INSUFFICIENT_RESOURCES;
2380 goto end;
2381 }
2382
2383 RtlZeroMemory(newvpb, sizeof(VPB));
2384
2385 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2386
2387 Vcb->removing = TRUE;
2388
2389 ExReleaseResourceLite(&Vcb->tree_lock);
2390
2391 CcWaitForCurrentLazyWriterActivity();
2392
2393 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2394
2395 flush_fcb_caches(Vcb);
2396
2397 if (Vcb->need_write && !Vcb->readonly)
2398 Status = do_write(Vcb, Irp);
2399 else
2400 Status = STATUS_SUCCESS;
2401
2402 free_trees(Vcb);
2403
2404 if (!NT_SUCCESS(Status)) {
2405 ERR("do_write returned %08x\n", Status);
2406 ExReleaseResourceLite(&Vcb->tree_lock);
2407 ExFreePool(newvpb);
2408 goto end;
2409 }
2410
2411 flush_fcb_caches(Vcb);
2412
2413 ExReleaseResourceLite(&Vcb->tree_lock);
2414
2415 IoAcquireVpbSpinLock(&irql);
2416
2417 if (devobj->Vpb->Flags & VPB_MOUNTED) {
2418 newvpb->Type = IO_TYPE_VPB;
2419 newvpb->Size = sizeof(VPB);
2420 newvpb->RealDevice = devobj;
2421 newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2422
2423 devobj->Vpb = newvpb;
2424 } else
2425 free_newvpb = TRUE;
2426
2427 IoReleaseVpbSpinLock(irql);
2428
2429 if (free_newvpb)
2430 ExFreePool(newvpb);
2431
2432 if (Vcb->open_files == 0)
2433 uninit(Vcb);
2434 }
2435
2436 break;
2437 }
2438
2439 le = le->Flink;
2440 }
2441
2442 Status = STATUS_SUCCESS;
2443
2444 end:
2445 ExReleaseResourceLite(&global_loading_lock);
2446
2447 ObDereferenceObject(fileobj);
2448
2449 return Status;
2450 }
2451
2452 static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) {
2453 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2454 ULONG* volstate;
2455
2456 if (Irp->AssociatedIrp.SystemBuffer) {
2457 volstate = Irp->AssociatedIrp.SystemBuffer;
2458 } else if (Irp->MdlAddress != NULL) {
2459 volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2460
2461 if (!volstate)
2462 return STATUS_INSUFFICIENT_RESOURCES;
2463 } else
2464 return STATUS_INVALID_USER_BUFFER;
2465
2466 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2467 return STATUS_INVALID_PARAMETER;
2468
2469 *volstate = 0;
2470
2471 if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2472 return STATUS_INVALID_PARAMETER;
2473
2474 Irp->IoStatus.Information = sizeof(ULONG);
2475
2476 return STATUS_SUCCESS;
2477 }
2478
2479 static NTSTATUS get_compression(PIRP Irp) {
2480 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2481 USHORT* compression;
2482
2483 TRACE("FSCTL_GET_COMPRESSION\n");
2484
2485 if (Irp->AssociatedIrp.SystemBuffer) {
2486 compression = Irp->AssociatedIrp.SystemBuffer;
2487 } else if (Irp->MdlAddress != NULL) {
2488 compression = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2489
2490 if (!compression)
2491 return STATUS_INSUFFICIENT_RESOURCES;
2492 } else
2493 return STATUS_INVALID_USER_BUFFER;
2494
2495 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2496 return STATUS_INVALID_PARAMETER;
2497
2498 *compression = COMPRESSION_FORMAT_NONE;
2499
2500 Irp->IoStatus.Information = sizeof(USHORT);
2501
2502 return STATUS_SUCCESS;
2503 }
2504
2505 static NTSTATUS set_compression(PIRP Irp) {
2506 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2507 USHORT* compression;
2508
2509 TRACE("FSCTL_SET_COMPRESSION\n");
2510
2511 if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2512 return STATUS_INVALID_PARAMETER;
2513
2514 compression = Irp->AssociatedIrp.SystemBuffer;
2515
2516 if (*compression != COMPRESSION_FORMAT_NONE)
2517 return STATUS_INVALID_PARAMETER;
2518
2519 return STATUS_SUCCESS;
2520 }
2521
2522 static void update_volumes(device_extension* Vcb) {
2523 LIST_ENTRY* le;
2524 volume_device_extension* vde = Vcb->vde;
2525 pdo_device_extension* pdode = vde->pdode;
2526
2527 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2528
2529 ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
2530
2531 le = pdode->children.Flink;
2532 while (le != &pdode->children) {
2533 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
2534
2535 vc->generation = Vcb->superblock.generation - 1;
2536
2537 le = le->Flink;
2538 }
2539
2540 ExReleaseResourceLite(&pdode->child_lock);
2541
2542 ExReleaseResourceLite(&Vcb->tree_lock);
2543 }
2544
2545 static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
2546 NTSTATUS Status;
2547
2548 TRACE("FSCTL_DISMOUNT_VOLUME\n");
2549
2550 if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2551 return STATUS_SUCCESS;
2552
2553 if (Vcb->disallow_dismount) {
2554 WARN("attempting to dismount boot volume or one containing a pagefile\n");
2555 return STATUS_ACCESS_DENIED;
2556 }
2557
2558 Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
2559 if (!NT_SUCCESS(Status)) {
2560 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
2561 }
2562
2563 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2564
2565 if (!Vcb->locked) {
2566 flush_fcb_caches(Vcb);
2567
2568 if (Vcb->need_write && !Vcb->readonly) {
2569 Status = do_write(Vcb, Irp);
2570
2571 if (!NT_SUCCESS(Status))
2572 ERR("do_write returned %08x\n", Status);
2573 }
2574 }
2575
2576 free_trees(Vcb);
2577
2578 Vcb->removing = TRUE;
2579
2580 if (Vcb->vde) {
2581 update_volumes(Vcb);
2582 Vcb->vde->mounted_device = NULL;
2583 }
2584
2585 ExReleaseResourceLite(&Vcb->tree_lock);
2586
2587 return STATUS_SUCCESS;
2588 }
2589
2590 static NTSTATUS is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj) {
2591 NTSTATUS Status;
2592 ULONG to_read;
2593 superblock* sb;
2594 UINT32 crc32;
2595 BTRFS_UUID fsuuid, devuuid;
2596 LIST_ENTRY* le;
2597
2598 to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2599
2600 sb = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
2601 if (!sb) {
2602 ERR("out of memory\n");
2603 return STATUS_INSUFFICIENT_RESOURCES;
2604 }
2605
2606 Status = sync_read_phys(devobj, superblock_addrs[0], to_read, (UINT8*)sb, TRUE);
2607 if (!NT_SUCCESS(Status)) {
2608 ERR("sync_read_phys returned %08x\n", Status);
2609 ExFreePool(sb);
2610 return Status;
2611 }
2612
2613 if (sb->magic != BTRFS_MAGIC) {
2614 TRACE("device is not Btrfs\n");
2615 ExFreePool(sb);
2616 return STATUS_SUCCESS;
2617 }
2618
2619 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2620
2621 if (crc32 != *((UINT32*)sb->checksum)) {
2622 TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2623 ExFreePool(sb);
2624 return STATUS_SUCCESS;
2625 }
2626
2627 fsuuid = sb->uuid;
2628 devuuid = sb->dev_item.device_uuid;
2629
2630 ExFreePool(sb);
2631
2632 ExAcquireResourceSharedLite(&global_loading_lock, TRUE);
2633
2634 le = VcbList.Flink;
2635
2636 while (le != &VcbList) {
2637 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
2638
2639 if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2640 LIST_ENTRY* le2;
2641
2642 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2643
2644 if (Vcb->superblock.num_devices > 1) {
2645 le2 = Vcb->devices.Flink;
2646 while (le2 != &Vcb->devices) {
2647 device* dev = CONTAINING_RECORD(le2, device, list_entry);
2648
2649 if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2650 ExReleaseResourceLite(&Vcb->tree_lock);
2651 ExReleaseResourceLite(&global_loading_lock);
2652 return STATUS_DEVICE_NOT_READY;
2653 }
2654
2655 le2 = le2->Flink;
2656 }
2657 }
2658
2659 ExReleaseResourceLite(&Vcb->tree_lock);
2660 ExReleaseResourceLite(&global_loading_lock);
2661 return STATUS_SUCCESS;
2662 }
2663
2664 le = le->Flink;
2665 }
2666
2667 ExReleaseResourceLite(&global_loading_lock);
2668
2669 return STATUS_SUCCESS;
2670 }
2671
2672 void trim_whole_device(device* dev) {
2673 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa;
2674 NTSTATUS Status;
2675
2676 // FIXME - avoid "bootloader area"??
2677
2678 dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2679 dmdsa.Action = DeviceDsmAction_Trim;
2680 dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
2681 dmdsa.ParameterBlockOffset = 0;
2682 dmdsa.ParameterBlockLength = 0;
2683 dmdsa.DataSetRangesOffset = 0;
2684 dmdsa.DataSetRangesLength = 0;
2685
2686 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0, TRUE, NULL);
2687 if (!NT_SUCCESS(Status))
2688 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
2689 }
2690
2691 static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, KPROCESSOR_MODE processor_mode) {
2692 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2693 NTSTATUS Status;
2694 PFILE_OBJECT fileobj, mountmgrfo;
2695 PDEVICE_OBJECT DeviceObject;
2696 HANDLE h;
2697 LIST_ENTRY* le;
2698 device* dev;
2699 DEV_ITEM* di;
2700 UINT64 dev_id, size;
2701 UINT8* mb;
2702 UINT64* stats;
2703 UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2704 volume_child* vc;
2705 PDEVICE_OBJECT mountmgr;
2706 KEY searchkey;
2707 traverse_ptr tp;
2708 STORAGE_DEVICE_NUMBER sdn;
2709 volume_device_extension* vde;
2710 pdo_device_extension* pdode;
2711 const GUID* pnp_guid;
2712 GET_LENGTH_INFORMATION gli;
2713
2714 pnp_name.Buffer = NULL;
2715
2716 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2717 return STATUS_PRIVILEGE_NOT_HELD;
2718
2719 if (!Vcb->vde) {
2720 WARN("not allowing second device to be added to non-PNP device\n");
2721 return STATUS_NOT_SUPPORTED;
2722 }
2723
2724 if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2725 return STATUS_MEDIA_WRITE_PROTECTED;
2726
2727 #if defined(_WIN64)
2728 if (IoIs32bitProcess(Irp)) {
2729 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2730 return STATUS_INVALID_PARAMETER;
2731
2732 h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2733 } else {
2734 #endif
2735 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2736 return STATUS_INVALID_PARAMETER;
2737
2738 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2739 #if defined(_WIN64)
2740 }
2741 #endif
2742
2743 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2744
2745 if (!NT_SUCCESS(Status)) {
2746 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2747 return Status;
2748 }
2749
2750 DeviceObject = fileobj->DeviceObject;
2751
2752 Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid);
2753 if (!NT_SUCCESS(Status)) {
2754 ERR("get_device_pnp_name returned %08x\n", Status);
2755 ObDereferenceObject(fileobj);
2756 return Status;
2757 }
2758
2759 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2760 if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2761 DeviceObject = DeviceObject->AttachedDevice;
2762
2763 Status = dev_ioctl(DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, TRUE, NULL);
2764 if (!NT_SUCCESS(Status)) {
2765 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
2766 ObDereferenceObject(fileobj);
2767 return Status;
2768 }
2769
2770 Status = is_device_part_of_mounted_btrfs_raid(DeviceObject);
2771 if (!NT_SUCCESS(Status)) {
2772 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
2773 ObDereferenceObject(fileobj);
2774 return Status;
2775 }
2776
2777 // if disk, check it has no partitions
2778 if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2779 ULONG dlisize;
2780 DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
2781
2782 dlisize = 0;
2783
2784 do {
2785 dlisize += 1024;
2786
2787 if (dli)
2788 ExFreePool(dli);
2789
2790 dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2791 if (!dli) {
2792 ERR("out of memory\n");
2793 Status = STATUS_INSUFFICIENT_RESOURCES;
2794 goto end2;
2795 }
2796
2797 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, dli, dlisize, TRUE, NULL);
2798 } while (Status == STATUS_BUFFER_TOO_SMALL);
2799
2800 if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2801 ExFreePool(dli);
2802 ERR("not adding disk which has partitions\n");
2803 Status = STATUS_DEVICE_NOT_READY;
2804 goto end2;
2805 }
2806
2807 ExFreePool(dli);
2808 }
2809
2810 Status = dev_ioctl(DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
2811 &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
2812 if (NT_SUCCESS(Status)) {
2813 if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2814 WARN("device was not disk\n");
2815 ObDereferenceObject(fileobj);
2816 return STATUS_INVALID_PARAMETER;
2817 }
2818 } else {
2819 sdn.DeviceNumber = 0xffffffff;
2820 sdn.PartitionNumber = 0xffffffff;
2821 }
2822
2823 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2824 &gli, sizeof(gli), TRUE, NULL);
2825 if (!NT_SUCCESS(Status)) {
2826 ERR("error reading length information: %08x\n", Status);
2827 ObDereferenceObject(fileobj);
2828 return Status;
2829 }
2830
2831 size = gli.Length.QuadPart;
2832
2833 if (size < 0x100000) {
2834 ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size);
2835 ObDereferenceObject(fileobj);
2836 return STATUS_INTERNAL_ERROR;
2837 }
2838
2839 volume_removal(drvobj, &pnp_name);
2840
2841 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2842
2843 if (Vcb->need_write)
2844 Status = do_write(Vcb, Irp);
2845 else
2846 Status = STATUS_SUCCESS;
2847
2848 free_trees(Vcb);
2849
2850 if (!NT_SUCCESS(Status)) {
2851 ERR("do_write returned %08x\n", Status);
2852 goto end;
2853 }
2854
2855 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
2856 if (!dev) {
2857 ERR("out of memory\n");
2858 Status = STATUS_INSUFFICIENT_RESOURCES;
2859 goto end;
2860 }
2861
2862 RtlZeroMemory(dev, sizeof(device));
2863
2864 dev->devobj = DeviceObject;
2865 dev->seeding = FALSE;
2866 init_device(Vcb, dev, TRUE);
2867
2868 InitializeListHead(&dev->space);
2869
2870 if (size > 0x100000) { // add disk hole - the first MB is marked as used
2871 Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2872 if (!NT_SUCCESS(Status)) {
2873 ERR("add_space_entry returned %08x\n", Status);
2874 goto end;
2875 }
2876 }
2877
2878 dev_id = 0;
2879
2880 le = Vcb->devices.Flink;
2881 while (le != &Vcb->devices) {
2882 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2883
2884 if (dev2->devitem.dev_id > dev_id)
2885 dev_id = dev2->devitem.dev_id;
2886
2887 le = le->Flink;
2888 }
2889
2890 dev_id++;
2891
2892 dev->devitem.dev_id = dev_id;
2893 dev->devitem.num_bytes = size;
2894 dev->devitem.bytes_used = 0;
2895 dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2896 dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2897 dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2898 dev->devitem.type = 0;
2899 dev->devitem.generation = 0;
2900 dev->devitem.start_offset = 0;
2901 dev->devitem.dev_group = 0;
2902 dev->devitem.seek_speed = 0;
2903 dev->devitem.bandwidth = 0;
2904 get_uuid(&dev->devitem.device_uuid);
2905 dev->devitem.fs_uuid = Vcb->superblock.uuid;
2906
2907 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
2908 if (!di) {
2909 ERR("out of memory\n");
2910 goto end;
2911 }
2912
2913 RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2914
2915 Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2916 if (!NT_SUCCESS(Status)) {
2917 ERR("insert_tree_item returned %08x\n", Status);
2918 ExFreePool(di);
2919 goto end;
2920 }
2921
2922 // add stats entry to dev tree
2923 stats = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * 5, ALLOC_TAG);
2924 if (!stats) {
2925 ERR("out of memory\n");
2926 Status = STATUS_INSUFFICIENT_RESOURCES;
2927 goto end;
2928 }
2929
2930 RtlZeroMemory(stats, sizeof(UINT64) * 5);
2931
2932 searchkey.obj_id = 0;
2933 searchkey.obj_type = TYPE_DEV_STATS;
2934 searchkey.offset = di->dev_id;
2935
2936 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2937 if (!NT_SUCCESS(Status)) {
2938 ERR("error - find_item returned %08x\n", Status);
2939 ExFreePool(stats);
2940 goto end;
2941 }
2942
2943 if (!keycmp(tp.item->key, searchkey)) {
2944 Status = delete_tree_item(Vcb, &tp);
2945 if (!NT_SUCCESS(Status)) {
2946 ERR("delete_tree_item returned %08x\n", Status);
2947 ExFreePool(stats);
2948 goto end;
2949 }
2950 }
2951
2952 Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(UINT64) * 5, NULL, Irp);
2953 if (!NT_SUCCESS(Status)) {
2954 ERR("insert_tree_item returned %08x\n", Status);
2955 ExFreePool(stats);
2956 goto end;
2957 }
2958
2959 if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
2960 trim_whole_device(dev);
2961
2962 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2963 mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
2964 if (!mb) {
2965 ERR("out of memory\n");
2966 Status = STATUS_INSUFFICIENT_RESOURCES;
2967 goto end;
2968 }
2969
2970 RtlZeroMemory(mb, 0x100000);
2971
2972 Status = write_data_phys(DeviceObject, 0, mb, 0x100000);
2973 if (!NT_SUCCESS(Status)) {
2974 ERR("write_data_phys returned %08x\n", Status);
2975 ExFreePool(mb);
2976 goto end;
2977 }
2978
2979 ExFreePool(mb);
2980
2981 vde = Vcb->vde;
2982 pdode = vde->pdode;
2983
2984 vc = ExAllocatePoolWithTag(NonPagedPool, sizeof(volume_child), ALLOC_TAG);
2985 if (!vc) {
2986 ERR("out of memory\n");
2987 Status = STATUS_INSUFFICIENT_RESOURCES;
2988 goto end;
2989 }
2990
2991 vc->uuid = dev->devitem.device_uuid;
2992 vc->devid = dev_id;
2993 vc->generation = Vcb->superblock.generation;
2994 vc->devobj = DeviceObject;
2995 vc->fileobj = fileobj;
2996 vc->notification_entry = NULL;
2997
2998 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj,
2999 drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3000 if (!NT_SUCCESS(Status))
3001 WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
3002
3003 pnp_name2 = pnp_name;
3004
3005 if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3006 pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3007 pnp_name2.Buffer = &pnp_name2.Buffer[3];
3008 pnp_name2.Length -= 3 * sizeof(WCHAR);
3009 pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3010 }
3011
3012 vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3013
3014 if (pnp_name2.Length == 0)
3015 vc->pnp_name.Buffer = NULL;
3016 else {
3017 vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, pnp_name2.Length, ALLOC_TAG);
3018 if (!vc->pnp_name.Buffer) {
3019 ERR("out of memory\n");
3020 Status = STATUS_INSUFFICIENT_RESOURCES;
3021 goto end;
3022 }
3023
3024 RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3025 }
3026
3027 vc->size = size;
3028 vc->seeding = FALSE;
3029 vc->disk_num = sdn.DeviceNumber;
3030 vc->part_num = sdn.PartitionNumber;
3031 vc->had_drive_letter = FALSE;
3032
3033 ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
3034 InsertTailList(&pdode->children, &vc->list_entry);
3035 pdode->num_children++;
3036 pdode->children_loaded++;
3037 ExReleaseResourceLite(&pdode->child_lock);
3038
3039 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
3040 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3041 if (!NT_SUCCESS(Status))
3042 ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
3043 else {
3044 Status = remove_drive_letter(mountmgr, &pnp_name);
3045 if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
3046 WARN("remove_drive_letter returned %08x\n", Status);
3047
3048 vc->had_drive_letter = NT_SUCCESS(Status);
3049
3050 ObDereferenceObject(mountmgrfo);
3051 }
3052
3053 Vcb->superblock.num_devices++;
3054 Vcb->superblock.total_bytes += size;
3055 Vcb->devices_loaded++;
3056 InsertTailList(&Vcb->devices, &dev->list_entry);
3057
3058 // FIXME - send notification that volume size has increased
3059
3060 ObReferenceObject(DeviceObject); // for Vcb
3061
3062 Status = do_write(Vcb, Irp);
3063 if (!NT_SUCCESS(Status))
3064 ERR("do_write returned %08x\n", Status);
3065
3066 ObReferenceObject(fileobj);
3067
3068 end:
3069 free_trees(Vcb);
3070
3071 ExReleaseResourceLite(&Vcb->tree_lock);
3072
3073 end2:
3074 ObDereferenceObject(fileobj);
3075
3076 if (pnp_name.Buffer)
3077 ExFreePool(pnp_name.Buffer);
3078
3079 if (NT_SUCCESS(Status))
3080 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
3081
3082 return Status;
3083 }
3084
3085 static NTSTATUS allow_extended_dasd_io(device_extension* Vcb, PFILE_OBJECT FileObject) {
3086 fcb* fcb;
3087 ccb* ccb;
3088
3089 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3090
3091 if (!FileObject)
3092 return STATUS_INVALID_PARAMETER;
3093
3094 fcb = FileObject->FsContext;
3095 ccb = FileObject->FsContext2;
3096
3097 if (!fcb)
3098 return STATUS_INVALID_PARAMETER;
3099
3100 if (fcb != Vcb->volume_fcb)
3101 return STATUS_INVALID_PARAMETER;
3102
3103 if (!ccb)
3104 return STATUS_INVALID_PARAMETER;
3105
3106 ccb->allow_extended_dasd_io = TRUE;
3107
3108 return STATUS_SUCCESS;
3109 }
3110
3111 static NTSTATUS query_uuid(device_extension* Vcb, void* data, ULONG length) {
3112 if (length < sizeof(BTRFS_UUID))
3113 return STATUS_BUFFER_OVERFLOW;
3114
3115 RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3116
3117 return STATUS_SUCCESS;
3118 }
3119
3120 static NTSTATUS reset_stats(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
3121 UINT64 devid;
3122 NTSTATUS Status;
3123 LIST_ENTRY* le;
3124
3125 if (length < sizeof(UINT64))
3126 return STATUS_INVALID_PARAMETER;
3127
3128 if (Vcb->readonly)
3129 return STATUS_MEDIA_WRITE_PROTECTED;
3130
3131 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3132 return STATUS_PRIVILEGE_NOT_HELD;
3133
3134 devid = *((UINT64*)data);
3135
3136 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3137
3138 le = Vcb->devices.Flink;
3139
3140 while (le != &Vcb->devices) {
3141 device* dev = CONTAINING_RECORD(le, device, list_entry);
3142
3143 if (dev->devitem.dev_id == devid) {
3144 RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
3145 dev->stats_changed = TRUE;
3146 Vcb->stats_changed = TRUE;
3147 Vcb->need_write = TRUE;
3148 Status = STATUS_SUCCESS;
3149 goto end;
3150 }
3151
3152 le = le->Flink;
3153 }
3154
3155 WARN("device %llx not found\n", devid);
3156 Status = STATUS_INVALID_PARAMETER;
3157
3158 end:
3159 ExReleaseResourceLite(&Vcb->tree_lock);
3160
3161 return Status;
3162 }
3163
3164 static NTSTATUS get_integrity_information(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen) {
3165 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER* fgiib = (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*)data;
3166
3167 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3168
3169 // STUB
3170
3171 if (!FileObject)
3172 return STATUS_INVALID_PARAMETER;
3173
3174 if (!data || datalen < sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER))
3175 return STATUS_INVALID_PARAMETER;
3176
3177 fgiib->ChecksumAlgorithm = 0;
3178 fgiib->Reserved = 0;
3179 fgiib->Flags = 0;
3180 fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3181 fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3182
3183 return STATUS_SUCCESS;
3184 }
3185
3186 static NTSTATUS set_integrity_information(PFILE_OBJECT FileObject, void* data, ULONG datalen) {
3187 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3188
3189 // STUB
3190
3191 if (!FileObject)
3192 return STATUS_INVALID_PARAMETER;
3193
3194 if (!data || datalen < sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER))
3195 return STATUS_INVALID_PARAMETER;
3196
3197 return STATUS_SUCCESS;
3198 }
3199
3200 BOOL fcb_is_inline(fcb* fcb) {
3201 LIST_ENTRY* le;
3202
3203 le = fcb->extents.Flink;
3204 while (le != &fcb->extents) {
3205 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3206
3207 if (!ext->ignore)
3208 return ext->extent_data.type == EXTENT_TYPE_INLINE;
3209
3210 le = le->Flink;
3211 }
3212
3213 return FALSE;
3214 }
3215
3216 static NTSTATUS duplicate_extents(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
3217 DUPLICATE_EXTENTS_DATA* ded = (DUPLICATE_EXTENTS_DATA*)data;
3218 fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3219 ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3220 NTSTATUS Status;
3221 PFILE_OBJECT sourcefo;
3222 UINT64 sourcelen, nbytes = 0;
3223 LIST_ENTRY rollback, *le, newexts;
3224 LARGE_INTEGER time;
3225 BTRFS_TIME now;
3226 BOOL make_inline;
3227
3228 if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3229 return STATUS_BUFFER_TOO_SMALL;
3230
3231 if (Vcb->readonly)
3232 return STATUS_MEDIA_WRITE_PROTECTED;
3233
3234 if (ded->ByteCount.QuadPart == 0)
3235 return STATUS_SUCCESS;
3236
3237 if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3238 return STATUS_INVALID_PARAMETER;
3239
3240 if (is_subvol_readonly(fcb->subvol, Irp))
3241 return STATUS_ACCESS_DENIED;
3242
3243 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3244 WARN("insufficient privileges\n");
3245 return STATUS_ACCESS_DENIED;
3246 }
3247
3248 if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3249 return STATUS_INVALID_PARAMETER;
3250
3251 Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3252 if (!NT_SUCCESS(Status)) {
3253 ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3254 return Status;
3255 }
3256
3257 if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3258 WARN("source and destination are on different volumes\n");
3259 ObDereferenceObject(sourcefo);
3260 return STATUS_INVALID_PARAMETER;
3261 }
3262
3263 sourcefcb = sourcefo->FsContext;
3264 sourceccb = sourcefo->FsContext2;
3265
3266 if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3267 ObDereferenceObject(sourcefo);
3268 return STATUS_INVALID_PARAMETER;
3269 }
3270
3271 if (!sourcefcb->ads && !fcb->ads) {
3272 if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3273 ObDereferenceObject(sourcefo);
3274 return STATUS_INVALID_PARAMETER;
3275 }
3276
3277 if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3278 ObDereferenceObject(sourcefo);
3279 return STATUS_INVALID_PARAMETER;
3280 }
3281 }
3282
3283 if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3284 WARN("insufficient privileges\n");
3285 ObDereferenceObject(sourcefo);
3286 return STATUS_ACCESS_DENIED;
3287 }
3288
3289 if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3290 ObDereferenceObject(sourcefo);
3291 return STATUS_INVALID_PARAMETER;
3292 }
3293
3294 sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3295
3296 if (sector_align(sourcelen, Vcb->superblock.sector_size) < (UINT64)ded->SourceFileOffset.QuadPart + (UINT64)ded->ByteCount.QuadPart) {
3297 ObDereferenceObject(sourcefo);
3298 return STATUS_NOT_SUPPORTED;
3299 }
3300
3301 if (fcb == sourcefcb &&
3302 ((ded->SourceFileOffset.QuadPart >= ded->TargetFileOffset.QuadPart && ded->SourceFileOffset.QuadPart < ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart) ||
3303 (ded->TargetFileOffset.QuadPart >= ded->SourceFileOffset.QuadPart && ded->TargetFileOffset.QuadPart < ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart))) {
3304 WARN("source and destination are the same, and the ranges overlap\n");
3305 ObDereferenceObject(sourcefo);
3306 return STATUS_INVALID_PARAMETER;
3307 }
3308
3309 // fail if nocsum flag set on one file but not the other
3310 if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3311 ObDereferenceObject(sourcefo);
3312 return STATUS_INVALID_PARAMETER;
3313 }
3314
3315 InitializeListHead(&rollback);
3316 InitializeListHead(&newexts);
3317
3318 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3319
3320 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
3321
3322 if (fcb != sourcefcb)
3323 ExAcquireResourceSharedLite(sourcefcb->Header.Resource, TRUE);
3324
3325 if (!FsRtlFastCheckLockForWrite(&fcb->lock, &ded->TargetFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3326 Status = STATUS_FILE_LOCK_CONFLICT;
3327 goto end;
3328 }
3329
3330 if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3331 Status = STATUS_FILE_LOCK_CONFLICT;
3332 goto end;
3333 }
3334
3335 make_inline = fcb->ads ? FALSE : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3336
3337 if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3338 UINT8* data2;
3339 ULONG bytes_read, dataoff, datalen2;
3340
3341 if (make_inline) {
3342 dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3343 datalen2 = (ULONG)fcb->inode_item.st_size;
3344 } else if (fcb->ads) {
3345 dataoff = 0;
3346 datalen2 = (ULONG)ded->ByteCount.QuadPart;
3347 } else {
3348 dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3349 datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3350 }
3351
3352 data2 = ExAllocatePoolWithTag(PagedPool, datalen2, ALLOC_TAG);
3353 if (!data2) {
3354 ERR("out of memory\n");
3355 Status = STATUS_INSUFFICIENT_RESOURCES;
3356 goto end;
3357 }
3358
3359 if (dataoff > 0) {
3360 if (make_inline)
3361 Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3362 else
3363 Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3364
3365 if (!NT_SUCCESS(Status)) {
3366 ERR("read_file returned %08x\n", Status);
3367 ExFreePool(data2);
3368 goto end;
3369 }
3370 }
3371
3372 if (sourcefcb->ads) {
3373 Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3374 if (!NT_SUCCESS(Status)) {
3375 ERR("read_stream returned %08x\n", Status);
3376 ExFreePool(data2);
3377 goto end;
3378 }
3379 } else {
3380 Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3381 if (!NT_SUCCESS(Status)) {
3382 ERR("read_file returned %08x\n", Status);
3383 ExFreePool(data2);
3384 goto end;
3385 }
3386 }
3387
3388 if (dataoff + bytes_read < datalen2)
3389 RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3390
3391 if (fcb->ads)
3392 RtlCopyMemory(&fcb->adsdata.Buffer[ded->TargetFileOffset.QuadPart], data2, (USHORT)min(ded->ByteCount.QuadPart, fcb->adsdata.Length - ded->TargetFileOffset.QuadPart));
3393 else if (make_inline) {
3394 UINT16 edsize;
3395 EXTENT_DATA* ed;
3396
3397 Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3398 if (!NT_SUCCESS(Status)) {
3399 ERR("excise_extents returned %08x\n", Status);
3400 ExFreePool(data2);
3401 goto end;
3402 }
3403
3404 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3405
3406 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3407 if (!ed) {
3408 ERR("out of memory\n");
3409 ExFreePool(data2);
3410 Status = STATUS_INSUFFICIENT_RESOURCES;
3411 goto end;
3412 }
3413
3414 ed->generation = Vcb->superblock.generation;
3415 ed->decoded_size = fcb->inode_item.st_size;
3416 ed->compression = BTRFS_COMPRESSION_NONE;
3417 ed->encryption = BTRFS_ENCRYPTION_NONE;
3418 ed->encoding = BTRFS_ENCODING_NONE;
3419 ed->type = EXTENT_TYPE_INLINE;
3420
3421 RtlCopyMemory(ed->data, data2, datalen2);
3422
3423 Status = add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, NULL, &rollback);
3424 if (!NT_SUCCESS(Status)) {
3425 ERR("add_extent_to_fcb returned %08x\n", Status);
3426 ExFreePool(data2);
3427 goto end;
3428 }
3429
3430 fcb->inode_item.st_blocks += datalen2;
3431 } else {
3432 UINT64 start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3433
3434 Status = do_write_file(fcb, start, start + datalen2, data2, Irp, FALSE, 0, &rollback);
3435 if (!NT_SUCCESS(Status)) {
3436 ERR("do_write_file returned %08x\n", Status);
3437 ExFreePool(data2);
3438 goto end;
3439 }
3440 }
3441
3442 ExFreePool(data2);
3443 } else {
3444 LIST_ENTRY* lastextle;
3445
3446 le = sourcefcb->extents.Flink;
3447 while (le != &sourcefcb->extents) {
3448 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3449
3450 if (!ext->ignore) {
3451 if (ext->offset >= (UINT64)ded->SourceFileOffset.QuadPart + (UINT64)ded->ByteCount.QuadPart)
3452 break;
3453
3454 if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3455 ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3456 extent* ext2;
3457 EXTENT_DATA2 *ed2s, *ed2d;
3458 chunk* c;
3459
3460 ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3461
3462 if (ext->offset + ed2s->num_bytes <= (UINT64)ded->SourceFileOffset.QuadPart) {
3463 le = le->Flink;
3464 continue;
3465 }
3466
3467 ext2 = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
3468 if (!ext2) {
3469 ERR("out of memory\n");
3470 Status = STATUS_INSUFFICIENT_RESOURCES;
3471 goto end;
3472 }
3473
3474 if (ext->offset < (UINT64)ded->SourceFileOffset.QuadPart)
3475 ext2->offset = ded->TargetFileOffset.QuadPart;
3476 else
3477 ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3478
3479 ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3480 ext2->unique = FALSE;
3481 ext2->ignore = FALSE;
3482 ext2->inserted = TRUE;
3483
3484 ext2->extent_data.generation = Vcb->superblock.generation;
3485 ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3486 ext2->extent_data.compression = ext->extent_data.compression;
3487 ext2->extent_data.encryption = ext->extent_data.encryption;
3488 ext2->extent_data.encoding = ext->extent_data.encoding;
3489 ext2->extent_data.type = ext->extent_data.type;
3490
3491 ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3492
3493 ed2d->address = ed2s->address;
3494 ed2d->size = ed2s->size;
3495
3496 if (ext->offset < (UINT64)ded->SourceFileOffset.QuadPart) {
3497 ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3498 ed2d->num_bytes = min((UINT64)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3499 } else {
3500 ed2d->offset = ed2s->offset;
3501 ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3502 }
3503
3504 if (ext->csum) {
3505 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3506 ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
3507 if (!ext2->csum) {
3508 ERR("out of memory\n");
3509 Status = STATUS_INSUFFICIENT_RESOURCES;
3510 ExFreePool(ext2);
3511 goto end;
3512 }
3513
3514 RtlCopyMemory(ext2->csum, &ext->csum[(ed2d->offset - ed2s->offset) / Vcb->superblock.sector_size],
3515 (ULONG)(ed2d->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
3516 } else {
3517 ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
3518 if (!ext2->csum) {
3519 ERR("out of memory\n");
3520 Status = STATUS_INSUFFICIENT_RESOURCES;
3521 ExFreePool(ext2);
3522 goto end;
3523 }
3524
3525 RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * sizeof(UINT32) / Vcb->superblock.sector_size));
3526 }
3527 } else
3528 ext2->csum = NULL;
3529
3530 InsertTailList(&newexts, &ext2->list_entry);
3531
3532 c = get_chunk_from_address(Vcb, ed2s->address);
3533 if (!c) {
3534 ERR("get_chunk_from_address(%llx) failed\n", ed2s->address);
3535 Status = STATUS_INTERNAL_ERROR;
3536 goto end;
3537 }
3538
3539 Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3540 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3541 if (!NT_SUCCESS(Status)) {
3542 ERR("update_changed_extent_ref returned %08x\n", Status);
3543 goto end;
3544 }
3545
3546 nbytes += ed2d->num_bytes;
3547 }
3548 }
3549
3550 le = le->Flink;
3551 }
3552
3553 Status = excise_extents(Vcb, fcb, ded->TargetFileOffset.QuadPart, ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart, Irp, &rollback);
3554 if (!NT_SUCCESS(Status)) {
3555 ERR("excise_extents returned %08x\n", Status);
3556
3557 while (!IsListEmpty(&newexts)) {
3558 extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry);
3559 ExFreePool(ext);
3560 }
3561
3562 goto end;
3563 }
3564
3565 // clear unique flags in source fcb
3566 le = sourcefcb->extents.Flink;
3567 while (le != &sourcefcb->extents) {
3568 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3569
3570 if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3571 EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3572 LIST_ENTRY* le2;
3573
3574 le2 = newexts.Flink;
3575 while (le2 != &newexts) {
3576 extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
3577
3578 if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3579 EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3580
3581 if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3582 ext->unique = FALSE;
3583 break;
3584 }
3585 }
3586
3587 le2 = le2->Flink;
3588 }
3589 }
3590
3591 le = le->Flink;
3592 }
3593
3594 lastextle = &fcb->extents;
3595 while (!IsListEmpty(&newexts)) {
3596 extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry);
3597
3598 add_extent(fcb, lastextle, ext);
3599 lastextle = &ext->list_entry;
3600 }
3601 }
3602
3603 KeQuerySystemTime(&time);
3604 win_time_to_unix(time, &now);
3605
3606 if (fcb->ads) {
3607 ccb->fileref->parent->fcb->inode_item.sequence++;
3608
3609 if (!ccb->user_set_change_time)
3610 ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3611
3612 ccb->fileref->parent->fcb->inode_item_changed = TRUE;
3613 mark_fcb_dirty(ccb->fileref->parent->fcb);
3614 } else {
3615 fcb->inode_item.st_blocks += nbytes;
3616 fcb->inode_item.sequence++;
3617
3618 if (!ccb->user_set_change_time)
3619 fcb->inode_item.st_ctime = now;
3620
3621 if (!ccb->user_set_write_time) {
3622 fcb->inode_item.st_mtime = now;
3623 send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3624 }
3625
3626 fcb->inode_item_changed = TRUE;
3627 fcb->extents_changed = TRUE;
3628 }
3629
3630 mark_fcb_dirty(fcb);
3631
3632 if (fcb->nonpaged->segment_object.DataSectionObject)
3633 CcPurgeCacheSection(&fcb->nonpaged->segment_object, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, FALSE);
3634
3635 Status = STATUS_SUCCESS;
3636
3637 end:
3638 ObDereferenceObject(sourcefo);
3639
3640 if (NT_SUCCESS(Status))
3641 clear_rollback(&rollback);
3642 else
3643 do_rollback(Vcb, &rollback);
3644
3645 if (fcb != sourcefcb)
3646 ExReleaseResourceLite(sourcefcb->Header.Resource);
3647
3648 ExReleaseResourceLite(fcb->Header.Resource);
3649
3650 ExReleaseResourceLite(&Vcb->tree_lock);
3651
3652 return Status;
3653 }
3654
3655 static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
3656 NTSTATUS Status;
3657 btrfs_mknod* bmn;
3658 fcb *parfcb, *fcb;
3659 ccb* parccb;
3660 file_ref *parfileref, *fileref;
3661 UNICODE_STRING name;
3662 root* subvol;
3663 UINT64 inode;
3664 dir_child* dc;
3665 LARGE_INTEGER time;
3666 BTRFS_TIME now;
3667 LIST_ENTRY* lastle;
3668 ANSI_STRING utf8;
3669 ULONG len, i;
3670 SECURITY_SUBJECT_CONTEXT subjcont;
3671 PSID owner;
3672 BOOLEAN defaulted;
3673
3674 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
3675
3676 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3677 return STATUS_INVALID_PARAMETER;
3678
3679 if (Vcb->readonly)
3680 return STATUS_MEDIA_WRITE_PROTECTED;
3681
3682 parfcb = FileObject->FsContext;
3683
3684 if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3685 WARN("trying to create file in something other than a directory\n");
3686 return STATUS_INVALID_PARAMETER;
3687 }
3688
3689 if (is_subvol_readonly(parfcb->subvol, Irp))
3690 return STATUS_ACCESS_DENIED;
3691
3692 parccb = FileObject->FsContext2;
3693 parfileref = parccb->fileref;
3694
3695 if (!parfileref)
3696 return STATUS_INVALID_PARAMETER;
3697
3698 if (datalen < sizeof(btrfs_mknod))
3699 return STATUS_INVALID_PARAMETER;
3700
3701 bmn = (btrfs_mknod*)data;
3702
3703 if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3704 return STATUS_INVALID_PARAMETER;
3705
3706 if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3707 return STATUS_INVALID_PARAMETER;
3708
3709 if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3710 (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3711 WARN("insufficient privileges\n");
3712 return STATUS_ACCESS_DENIED;
3713 }
3714
3715 if (bmn->inode != 0) {
3716 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3717 return STATUS_PRIVILEGE_NOT_HELD;
3718 }
3719
3720 for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3721 if (bmn->name[i] == 0 || bmn->name[i] == '/')
3722 return STATUS_OBJECT_NAME_INVALID;
3723 }
3724
3725 // don't allow files called . or ..
3726 if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3727 return STATUS_OBJECT_NAME_INVALID;
3728
3729 Status = RtlUnicodeToUTF8N(NULL, 0, &len, bmn->name, bmn->namelen);
3730 if (!NT_SUCCESS(Status)) {
3731 ERR("RtlUnicodeToUTF8N return %08x\n", Status);
3732 return Status;
3733 }
3734
3735 if (len == 0) {
3736 ERR("RtlUnicodeToUTF8N returned a length of 0\n");
3737 return STATUS_INTERNAL_ERROR;
3738 }
3739
3740 if (len > 0xffff) {
3741 ERR("len was too long (%x)\n", len);
3742 return STATUS_INVALID_PARAMETER;
3743 }
3744
3745 utf8.MaximumLength = utf8.Length = (USHORT)len;
3746 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
3747
3748 if (!utf8.Buffer) {
3749 ERR("out of memory\n");
3750 return STATUS_INSUFFICIENT_RESOURCES;
3751 }
3752
3753 Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3754 if (!NT_SUCCESS(Status)) {
3755 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
3756 ExFreePool(utf8.Buffer);
3757 return Status;
3758 }
3759
3760 name.Length = name.MaximumLength = bmn->namelen;
3761 name.Buffer = bmn->name;
3762
3763 Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, TRUE);
3764 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
3765 ERR("find_file_in_dir returned %08x\n", Status);
3766 goto end;
3767 }
3768
3769 if (NT_SUCCESS(Status)) {
3770 WARN("filename already exists\n");
3771 Status = STATUS_OBJECT_NAME_COLLISION;
3772 goto end;
3773 }
3774
3775 KeQuerySystemTime(&time);
3776 win_time_to_unix(time, &now);
3777
3778 fcb = create_fcb(Vcb, PagedPool);
3779 if (!fcb) {
3780 ERR("out of memory\n");
3781 Status = STATUS_INSUFFICIENT_RESOURCES;
3782 goto end;
3783 }
3784
3785 fcb->Vcb = Vcb;
3786
3787 fcb->inode_item.generation = Vcb->superblock.generation;
3788 fcb->inode_item.transid = Vcb->superblock.generation;
3789 fcb->inode_item.st_size = 0;
3790 fcb->inode_item.st_blocks = 0;
3791 fcb->inode_item.block_group = 0;
3792 fcb->inode_item.st_nlink = 1;
3793 fcb->inode_item.st_uid = UID_NOBODY;
3794 fcb->inode_item.st_gid = GID_NOBODY;
3795 fcb->inode_item.st_mode = inherit_mode(parfcb, bmn->type == BTRFS_TYPE_DIRECTORY);
3796
3797 if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3798 fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3799 else
3800 fcb->inode_item.st_rdev = 0;
3801
3802 fcb->inode_item.flags = 0;
3803 fcb->inode_item.sequence = 1;
3804 fcb->inode_item.st_atime = now;
3805 fcb->inode_item.st_ctime = now;
3806 fcb->inode_item.st_mtime = now;
3807 fcb->inode_item.otime = now;
3808
3809 if (bmn->type == BTRFS_TYPE_DIRECTORY)
3810 fcb->inode_item.st_mode |= __S_IFDIR;
3811 else if (bmn->type == BTRFS_TYPE_CHARDEV)
3812 fcb->inode_item.st_mode |= __S_IFCHR;
3813 else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3814 fcb->inode_item.st_mode |= __S_IFBLK;
3815 else if (bmn->type == BTRFS_TYPE_FIFO)
3816 fcb->inode_item.st_mode |= __S_IFIFO;
3817 else if (bmn->type == BTRFS_TYPE_SOCKET)
3818 fcb->inode_item.st_mode |= __S_IFSOCK;
3819 else if (bmn->type == BTRFS_TYPE_SYMLINK)
3820 fcb->inode_item.st_mode |= __S_IFLNK;
3821 else
3822 fcb->inode_item.st_mode |= __S_IFREG;
3823
3824 if (bmn->type != BTRFS_TYPE_DIRECTORY)
3825 fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3826
3827 // inherit nodatacow flag from parent directory
3828 if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3829 fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
3830
3831 if (bmn->type != BTRFS_TYPE_DIRECTORY)
3832 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
3833 }
3834
3835 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3836 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
3837
3838 fcb->prop_compression = parfcb->prop_compression;
3839 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
3840
3841 fcb->inode_item_changed = TRUE;
3842
3843 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3844 fcb->Header.AllocationSize.QuadPart = 0;
3845 fcb->Header.FileSize.QuadPart = 0;
3846 fcb->Header.ValidDataLength.QuadPart = 0;
3847
3848 fcb->atts = 0;
3849
3850 if (bmn->name[0] == '.')
3851 fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
3852
3853 if (bmn->type == BTRFS_TYPE_DIRECTORY)
3854 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
3855
3856 fcb->atts_changed = FALSE;
3857
3858 InterlockedIncrement(&parfcb->refcount);
3859 fcb->subvol = parfcb->subvol;
3860
3861 SeCaptureSubjectContext(&subjcont);
3862
3863 Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3864 SEF_SACL_AUTO_INHERIT, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
3865
3866 if (!NT_SUCCESS(Status)) {
3867 ERR("SeAssignSecurityEx returned %08x\n", Status);
3868 reap_fcb(fcb);
3869 goto end;
3870 }
3871
3872 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3873 if (!NT_SUCCESS(Status)) {
3874 WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
3875 fcb->sd_dirty = TRUE;
3876 } else {
3877 fcb->inode_item.st_uid = sid_to_uid(owner);
3878 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
3879 }
3880
3881 find_gid(fcb, parfcb, &subjcont);
3882
3883 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
3884 acquire_fcb_lock_exclusive(Vcb);
3885
3886 if (bmn->inode == 0) {
3887 inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3888 lastle = parfcb->subvol->fcbs.Blink;
3889 } else {
3890 if (bmn->inode > (UINT64)parfcb->subvol->lastinode) {
3891 inode = parfcb->subvol->lastinode = bmn->inode;
3892 lastle = parfcb->subvol->fcbs.Blink;
3893 } else {
3894 LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3895
3896 lastle = parfcb->subvol->fcbs.Blink;;
3897 while (le != &parfcb->subvol->fcbs) {
3898 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3899
3900 if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3901 release_fcb_lock(Vcb);
3902 ExReleaseResourceLite(&Vcb->fileref_lock);
3903
3904 WARN("inode collision\n");
3905 Status = STATUS_INVALID_PARAMETER;
3906 goto end;
3907 } else if (fcb2->inode > bmn->inode) {
3908 lastle = fcb2->list_entry.Blink;
3909 break;
3910 }
3911
3912 le = le->Flink;
3913 }
3914
3915 inode = bmn->inode;
3916 }
3917 }
3918
3919 fcb->inode = inode;
3920 fcb->type = bmn->type;
3921
3922 fileref = create_fileref(Vcb);
3923 if (!fileref) {
3924 release_fcb_lock(Vcb);
3925 ExReleaseResourceLite(&Vcb->fileref_lock);
3926
3927 ERR("out of memory\n");
3928 reap_fcb(fcb);
3929 Status = STATUS_INSUFFICIENT_RESOURCES;
3930 goto end;
3931 }
3932
3933 fileref->fcb = fcb;
3934
3935 fcb->created = TRUE;
3936 fileref->created = TRUE;
3937
3938 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3939 fcb->subvol->root_item.ctime = now;
3940
3941 fileref->parent = parfileref;
3942
3943 mark_fcb_dirty(fcb);
3944 mark_fileref_dirty(fileref);
3945
3946 Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8, &name, fcb->type, &dc);
3947 if (!NT_SUCCESS(Status))
3948 WARN("add_dir_child returned %08x\n", Status);
3949
3950 fileref->dc = dc;
3951 dc->fileref = fileref;
3952
3953 ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, TRUE);
3954 InsertTailList(&parfileref->children, &fileref->list_entry);
3955 ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
3956
3957 increase_fileref_refcount(parfileref);
3958
3959 if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3960 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
3961 if (!fcb->hash_ptrs) {
3962 release_fcb_lock(Vcb);
3963 ExReleaseResourceLite(&Vcb->fileref_lock);
3964
3965 ERR("out of memory\n");
3966 free_fileref(fileref);
3967 Status = STATUS_INSUFFICIENT_RESOURCES;
3968 goto end;
3969 }
3970
3971 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
3972
3973 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
3974 if (!fcb->hash_ptrs_uc) {
3975 release_fcb_lock(Vcb);
3976 ExReleaseResourceLite(&Vcb->fileref_lock);
3977
3978 ERR("out of memory\n");
3979 free_fileref(fileref);
3980 Status = STATUS_INSUFFICIENT_RESOURCES;
3981 goto end;
3982 }
3983
3984 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
3985 }
3986
3987 InsertHeadList(lastle, &fcb->list_entry);
3988 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
3989
3990 if (bmn->type == BTRFS_TYPE_DIRECTORY)
3991 fileref->fcb->fileref = fileref;
3992
3993 ExAcquireResourceExclusiveLite(parfcb->Header.Resource, TRUE);
3994 parfcb->inode_item.st_size += utf8.Length * 2;
3995 parfcb->inode_item.transid = Vcb->superblock.generation;
3996 parfcb->inode_item.sequence++;
3997
3998 if (!parccb->user_set_change_time)
3999 parfcb->inode_item.st_ctime = now;
4000
4001 if (!parccb->user_set_write_time)
4002 parfcb->inode_item.st_mtime = now;
4003
4004 parfcb->subvol->fcbs_version++;
4005
4006 ExReleaseResourceLite(parfcb->Header.Resource);
4007 release_fcb_lock(Vcb);
4008 ExReleaseResourceLite(&Vcb->fileref_lock);
4009
4010 parfcb->inode_item_changed = TRUE;
4011 mark_fcb_dirty(parfcb);
4012
4013 send_notification_fileref(fileref, bmn->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
4014
4015 if (!parccb->user_set_write_time)
4016 send_notification_fcb(parfileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
4017
4018 Status = STATUS_SUCCESS;
4019
4020 end:
4021
4022 ExFreePool(utf8.Buffer);
4023
4024 return Status;
4025 }
4026
4027 static void mark_subvol_dirty(device_extension* Vcb, root* r) {
4028 if (!r->dirty) {
4029 r->dirty = TRUE;
4030
4031 ExAcquireResourceExclusiveLite(&Vcb->dirty_subvols_lock, TRUE);
4032 InsertTailList(&Vcb->dirty_subvols, &r->list_entry_dirty);
4033 ExReleaseResourceLite(&Vcb->dirty_subvols_lock);
4034 }
4035
4036 Vcb->need_write = TRUE;
4037 }
4038
4039 static NTSTATUS recvd_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) {
4040 btrfs_received_subvol* brs = (btrfs_received_subvol*)data;
4041 fcb* fcb;
4042 NTSTATUS Status;
4043 LARGE_INTEGER time;
4044 BTRFS_TIME now;
4045
4046 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
4047
4048 if (!data || datalen < sizeof(btrfs_received_subvol))
4049 return STATUS_INVALID_PARAMETER;
4050
4051 if (!FileObject || !FileObject->FsContext || FileObject->FsContext == Vcb->volume_fcb)
4052 return STATUS_INVALID_PARAMETER;
4053
4054 fcb = FileObject->FsContext;
4055
4056 if (!fcb->subvol)
4057 return STATUS_INVALID_PARAMETER;
4058
4059 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
4060 return STATUS_PRIVILEGE_NOT_HELD;
4061
4062 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
4063
4064 if (fcb->subvol->root_item.rtransid != 0) {
4065 WARN("subvol already has received information set\n");
4066 Status = STATUS_INVALID_PARAMETER;
4067 goto end;
4068 }
4069
4070 KeQuerySystemTime(&time);
4071 win_time_to_unix(time, &now);
4072
4073 RtlCopyMemory(&fcb->subvol->root_item.received_uuid, &brs->uuid, sizeof(BTRFS_UUID));
4074 fcb->subvol->root_item.stransid = brs->generation;
4075 fcb->subvol->root_item.rtransid = Vcb->superblock.generation;
4076 fcb->subvol->root_item.rtime = now;
4077
4078 fcb->subvol->received = TRUE;
4079 mark_subvol_dirty(Vcb, fcb->subvol);
4080
4081 Status = STATUS_SUCCESS;
4082
4083 end:
4084 ExReleaseResourceLite(&Vcb->tree_lock);
4085
4086 return Status;
4087 }
4088
4089 static NTSTATUS fsctl_get_xattrs(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) {
4090 LIST_ENTRY* le;
4091 btrfs_set_xattr* bsxa;
4092 ULONG reqlen = (ULONG)offsetof(btrfs_set_xattr, data[0]);
4093 fcb* fcb;
4094 ccb* ccb;
4095
4096 if (!data || datalen < reqlen)
4097 return STATUS_INVALID_PARAMETER;
4098
4099 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4100 return STATUS_INVALID_PARAMETER;
4101
4102 fcb = FileObject->FsContext;
4103 ccb = FileObject->FsContext2;
4104
4105 if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)) && processor_mode == UserMode) {
4106 WARN("insufficient privileges\n");
4107 return STATUS_ACCESS_DENIED;
4108 }
4109
4110 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
4111
4112 le = fcb->xattrs.Flink;
4113 while (le != &fcb->xattrs) {
4114 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
4115
4116 if (xa->valuelen > 0)
4117 reqlen += (ULONG)offsetof(btrfs_set_xattr, data[0]) + xa->namelen + xa->valuelen;
4118
4119 le = le->Flink;
4120 }
4121
4122 if (datalen < reqlen) {
4123 ExReleaseResourceLite(fcb->Header.Resource);
4124 return STATUS_BUFFER_OVERFLOW;
4125 }
4126
4127 bsxa = (btrfs_set_xattr*)data;
4128
4129 if (reqlen > 0) {
4130 le = fcb->xattrs.Flink;
4131 while (le != &fcb->xattrs) {
4132 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
4133
4134 if (xa->valuelen > 0) {
4135 bsxa->namelen = xa->namelen;
4136 bsxa->valuelen = xa->valuelen;
4137 memcpy(bsxa->data, xa->data, xa->namelen + xa->valuelen);
4138
4139 bsxa = (btrfs_set_xattr*)&bsxa->data[xa->namelen + xa->valuelen];
4140 }
4141
4142 le = le->Flink;
4143 }
4144 }
4145
4146 bsxa->namelen = 0;
4147 bsxa->valuelen = 0;
4148
4149 ExReleaseResourceLite(fcb->Header.Resource);
4150
4151 return STATUS_SUCCESS;
4152 }
4153
4154 static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) {
4155 NTSTATUS Status;
4156 btrfs_set_xattr* bsxa;
4157 xattr* xa;
4158 fcb* fcb;
4159 ccb* ccb;
4160 LIST_ENTRY* le;
4161
4162 static const char stream_pref[] = "user.";
4163
4164 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
4165
4166 if (!data || datalen < sizeof(btrfs_set_xattr))
4167 return STATUS_INVALID_PARAMETER;
4168
4169 bsxa = (btrfs_set_xattr*)data;
4170
4171 if (datalen < offsetof(btrfs_set_xattr, data[0]) + bsxa->namelen + bsxa->valuelen)
4172 return STATUS_INVALID_PARAMETER;
4173
4174 if (bsxa->namelen + bsxa->valuelen + sizeof(tree_header) + sizeof(leaf_node) + offsetof(DIR_ITEM, name[0]) > Vcb->superblock.node_size)
4175 return STATUS_INVALID_PARAMETER;
4176
4177 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4178 return STATUS_INVALID_PARAMETER;
4179
4180 if (Vcb->readonly)
4181 return STATUS_MEDIA_WRITE_PROTECTED;
4182
4183 fcb = FileObject->FsContext;
4184 ccb = FileObject->FsContext2;
4185
4186 if (is_subvol_readonly(fcb->subvol, Irp))
4187 return STATUS_ACCESS_DENIED;
4188
4189 if (!(ccb->access & FILE_WRITE_ATTRIBUTES) && Irp->RequestorMode == UserMode) {
4190 WARN("insufficient privileges\n");
4191 return STATUS_ACCESS_DENIED;
4192 }
4193
4194 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
4195
4196 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4197
4198 if (bsxa->namelen == sizeof(EA_NTACL) - 1 && RtlCompareMemory(bsxa->data, EA_NTACL, sizeof(EA_NTACL) - 1) == sizeof(EA_NTACL) - 1) {
4199 if ((!(ccb->access & WRITE_DAC) || !(ccb->access & WRITE_OWNER)) && Irp->RequestorMode == UserMode) {
4200 WARN("insufficient privileges\n");
4201 Status = STATUS_ACCESS_DENIED;
4202 goto end;
4203 }
4204
4205 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) {
4206 Status = STATUS_PRIVILEGE_NOT_HELD;
4207 goto end;
4208 }
4209
4210 if (fcb->sd)
4211 ExFreePool(fcb->sd);
4212
4213 if (bsxa->valuelen > 0 && RtlValidRelativeSecurityDescriptor(bsxa->data + bsxa->namelen, bsxa->valuelen, 0)) {
4214 fcb->sd = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4215 if (!fcb->sd) {
4216 ERR("out of memory\n");
4217 Status = STATUS_INSUFFICIENT_RESOURCES;
4218 goto end;
4219 }
4220
4221 RtlCopyMemory(fcb->sd, bsxa->data + bsxa->namelen, bsxa->valuelen);
4222 } else if (fcb->sd)
4223 fcb->sd = NULL;
4224
4225 fcb->sd_dirty = TRUE;
4226
4227 if (!fcb->sd) {
4228 fcb_get_sd(fcb, ccb->fileref->parent->fcb, FALSE, Irp);
4229 fcb->sd_deleted = TRUE;
4230 }
4231
4232 mark_fcb_dirty(fcb);
4233
4234 Status = STATUS_SUCCESS;
4235 goto end;
4236 } else if (bsxa->namelen == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(bsxa->data, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1) == sizeof(EA_DOSATTRIB) - 1) {
4237 ULONG atts;
4238
4239 if (bsxa->valuelen > 0 && get_file_attributes_from_xattr(bsxa->data + bsxa->namelen, bsxa->valuelen, &atts)) {
4240 fcb->atts = atts;
4241
4242 if (fcb->type == BTRFS_TYPE_DIRECTORY)
4243 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
4244 else if (fcb->type == BTRFS_TYPE_SYMLINK)
4245 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
4246
4247 if (fcb->inode == SUBVOL_ROOT_INODE) {
4248 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
4249 fcb->atts |= FILE_ATTRIBUTE_READONLY;
4250 else
4251 fcb->atts &= ~FILE_ATTRIBUTE_READONLY;
4252 }
4253
4254 fcb->atts_deleted = FALSE;
4255 } else {
4256 BOOL hidden = ccb->fileref && ccb->fileref->dc && ccb->fileref->dc->utf8.Buffer && ccb->fileref->dc->utf8.Buffer[0] == '.';
4257
4258 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, hidden, TRUE, Irp);
4259 fcb->atts_deleted = TRUE;
4260 }
4261
4262 fcb->atts_changed = TRUE;
4263 mark_fcb_dirty(fcb);
4264
4265 Status = STATUS_SUCCESS;
4266 goto end;
4267 } else if (bsxa->namelen == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(bsxa->data, EA_REPARSE, sizeof(EA_REPARSE) - 1) == sizeof(EA_REPARSE) - 1) {
4268 if (fcb->reparse_xattr.Buffer) {
4269 ExFreePool(fcb->reparse_xattr.Buffer);
4270 fcb->reparse_xattr.Buffer = NULL;
4271 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = 0;
4272 }
4273
4274 if (bsxa->valuelen > 0) {
4275 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4276 if (!fcb->reparse_xattr.Buffer) {
4277 ERR("out of memory\n");
4278 Status = STATUS_INSUFFICIENT_RESOURCES;
4279 goto end;
4280 }
4281
4282 RtlCopyMemory(fcb->reparse_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen);
4283 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = bsxa->valuelen;
4284 }
4285
4286 fcb->reparse_xattr_changed = TRUE;
4287 mark_fcb_dirty(fcb);
4288
4289 Status = STATUS_SUCCESS;
4290 goto end;
4291 } else if (bsxa->namelen == sizeof(EA_EA) - 1 && RtlCompareMemory(bsxa->data, EA_EA, sizeof(EA_EA) - 1) == sizeof(EA_EA) - 1) {
4292 if (!(ccb->access & FILE_WRITE_EA) && Irp->RequestorMode == UserMode) {
4293 WARN("insufficient privileges\n");
4294 Status = STATUS_ACCESS_DENIED;
4295 goto end;
4296 }
4297
4298 if (fcb->ea_xattr.Buffer) {
4299 ExFreePool(fcb->ea_xattr.Buffer);
4300 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4301 fcb->ea_xattr.Buffer = NULL;
4302 }
4303
4304 fcb->ealen = 0;
4305
4306 if (bsxa->valuelen > 0) {
4307 ULONG offset;
4308
4309 Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen), bsxa->valuelen, &offset);
4310
4311 if (!NT_SUCCESS(Status))
4312 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
4313 else {
4314 FILE_FULL_EA_INFORMATION* eainfo;
4315
4316 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG);
4317 if (!fcb->ea_xattr.Buffer) {
4318 ERR("out of memory\n");
4319 Status = STATUS_INSUFFICIENT_RESOURCES;
4320 goto end;
4321 }
4322
4323 RtlCopyMemory(fcb->ea_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen);
4324
4325 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = bsxa->valuelen;
4326
4327 fcb->ealen = 4;
4328
4329 // calculate ealen
4330 eainfo = (FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen);
4331 do {
4332 fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
4333
4334 if (eainfo->NextEntryOffset == 0)
4335 break;
4336
4337 eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
4338 } while (TRUE);
4339 }
4340 }
4341
4342 fcb->ea_changed = TRUE;
4343 mark_fcb_dirty(fcb);
4344
4345 Status = STATUS_SUCCESS;
4346 goto end;
4347 } else if (bsxa->namelen == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1) == sizeof(EA_PROP_COMPRESSION) - 1) {
4348 static const char lzo[] = "lzo";
4349 static const char zlib[] = "zlib";
4350 static const char zstd[] = "zstd";
4351
4352 if (bsxa->valuelen == sizeof(zstd) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zstd, bsxa->valuelen) == bsxa->valuelen)
4353 fcb->prop_compression = PropCompression_ZSTD;
4354 else if (bsxa->valuelen == sizeof(lzo) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, lzo, bsxa->valuelen) == bsxa->valuelen)
4355 fcb->prop_compression = PropCompression_LZO;
4356 else if (bsxa->valuelen == sizeof(zlib) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zlib, bsxa->valuelen) == bsxa->valuelen)
4357 fcb->prop_compression = PropCompression_Zlib;
4358 else
4359 fcb->prop_compression = PropCompression_None;
4360
4361 if (fcb->prop_compression != PropCompression_None) {
4362 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
4363 fcb->inode_item_changed = TRUE;
4364 }
4365
4366 fcb->prop_compression_changed = TRUE;
4367 mark_fcb_dirty(fcb);
4368
4369 Status = STATUS_SUCCESS;
4370 goto end;
4371 } else if (bsxa->namelen >= (sizeof(stream_pref) - 1) && RtlCompareMemory(bsxa->data, stream_pref, sizeof(stream_pref) - 1) == sizeof(stream_pref) - 1) {
4372 // don't allow xattrs beginning with user., as these appear as streams instead
4373 Status = STATUS_OBJECT_NAME_INVALID;
4374 goto end;
4375 }
4376
4377 xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + bsxa->namelen + bsxa->valuelen, ALLOC_TAG);
4378 if (!xa) {
4379 ERR("out of memory\n");
4380 Status = STATUS_INSUFFICIENT_RESOURCES;
4381 goto end;
4382 }
4383
4384 le = fcb->xattrs.Flink;
4385 while (le != &fcb->xattrs) {
4386 xattr* xa2 = CONTAINING_RECORD(le, xattr, list_entry);
4387
4388 if (xa2->namelen == bsxa->namelen && RtlCompareMemory(xa2->data, bsxa->data, xa2->namelen) == xa2->namelen) {
4389 RemoveEntryList(&xa2->list_entry);
4390 ExFreePool(xa2);
4391 break;
4392 }
4393
4394 le = le->Flink;
4395 }
4396
4397 xa->namelen = bsxa->namelen;
4398 xa->valuelen = bsxa->valuelen;
4399 xa->dirty = TRUE;
4400 RtlCopyMemory(xa->data, bsxa->data, bsxa->namelen + bsxa->valuelen);
4401
4402 InsertTailList(&fcb->xattrs, &xa->list_entry);
4403
4404 fcb->xattrs_changed = TRUE;
4405 mark_fcb_dirty(fcb);
4406
4407 Status = STATUS_SUCCESS;
4408
4409 end:
4410 ExReleaseResourceLite(fcb->Header.Resource);
4411
4412 ExReleaseResourceLite(&Vcb->tree_lock);
4413
4414 return Status;
4415 }
4416
4417 static NTSTATUS reserve_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
4418 fcb* fcb;
4419 ccb* ccb;
4420
4421 TRACE("(%p, %p)\n", Vcb, FileObject);
4422
4423 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed.
4424
4425 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4426 return STATUS_PRIVILEGE_NOT_HELD;
4427
4428 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
4429 return STATUS_INVALID_PARAMETER;
4430
4431 fcb = FileObject->FsContext;
4432 ccb = FileObject->FsContext2;
4433
4434 if (!(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY))
4435 return STATUS_INVALID_PARAMETER;
4436
4437 if (fcb->subvol->reserved)
4438 return STATUS_INVALID_PARAMETER;
4439
4440 fcb->subvol->reserved = PsGetCurrentProcess();
4441 ccb->reserving = TRUE;
4442
4443 return STATUS_SUCCESS;
4444 }
4445
4446 static NTSTATUS get_subvol_path(device_extension* Vcb, UINT64 id, WCHAR* out, ULONG outlen, PIRP Irp) {
4447 LIST_ENTRY* le;
4448 root* r = NULL;
4449 NTSTATUS Status;
4450 file_ref* fr;
4451 UNICODE_STRING us;
4452
4453 le = Vcb->roots.Flink;
4454 while (le != &Vcb->roots) {
4455 root* r2 = CONTAINING_RECORD(le, root, list_entry);
4456
4457 if (r2->id == id) {
4458 r = r2;
4459 break;
4460 }
4461
4462 le = le->Flink;
4463 }
4464
4465 if (!r) {
4466 ERR("couldn't find subvol %llx\n", id);
4467 return STATUS_INTERNAL_ERROR;
4468 }
4469
4470 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
4471
4472 Status = open_fileref_by_inode(Vcb, r, r->root_item.objid, &fr, Irp);
4473 if (!NT_SUCCESS(Status)) {
4474 ExReleaseResourceLite(&Vcb->fileref_lock);
4475 ERR("open_fileref_by_inode returned %08x\n", Status);
4476 return Status;
4477 }
4478
4479 us.Buffer = out;
4480 us.Length = 0;
4481 us.MaximumLength = (USHORT)min(0xffff, outlen) - sizeof(WCHAR);
4482
4483 Status = fileref_get_filename(fr, &us, NULL, NULL);
4484
4485 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
4486 out[us.Length / sizeof(WCHAR)] = 0;
4487 else
4488 ERR("fileref_get_filename returned %08x\n", Status);
4489
4490 free_fileref(fr);
4491
4492 ExReleaseResourceLite(&Vcb->fileref_lock);
4493
4494 return Status;
4495 }
4496
4497 static NTSTATUS find_subvol(device_extension* Vcb, void* in, ULONG inlen, void* out, ULONG outlen, PIRP Irp) {
4498 btrfs_find_subvol* bfs;
4499 NTSTATUS Status;
4500 traverse_ptr tp;
4501 KEY searchkey;
4502
4503 if (!in || inlen < sizeof(btrfs_find_subvol))
4504 return STATUS_INVALID_PARAMETER;
4505
4506 if (!out || outlen < sizeof(WCHAR))
4507 return STATUS_INVALID_PARAMETER;
4508
4509 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4510 return STATUS_PRIVILEGE_NOT_HELD;
4511
4512 bfs = (btrfs_find_subvol*)in;
4513
4514 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
4515
4516 if (!Vcb->uuid_root) {
4517 ERR("couldn't find uuid root\n");
4518 Status = STATUS_NOT_FOUND;
4519 goto end;
4520 }
4521
4522 RtlCopyMemory(&searchkey.obj_id, &bfs->uuid, sizeof(UINT64));
4523 searchkey.obj_type = TYPE_SUBVOL_UUID;
4524 RtlCopyMemory(&searchkey.offset, &bfs->uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
4525
4526 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
4527
4528 if (!NT_SUCCESS(Status)) {
4529 ERR("find_item returned %08x\n", Status);
4530 goto end;
4531 }
4532
4533 if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(UINT64)) {
4534 UINT64* id = (UINT64*)tp.item->data;
4535
4536 if (bfs->ctransid != 0) {
4537 KEY searchkey2;
4538 traverse_ptr tp2;
4539
4540 searchkey2.obj_id = *id;
4541 searchkey2.obj_type = TYPE_ROOT_ITEM;
4542 searchkey2.offset = 0xffffffffffffffff;
4543
4544 Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, FALSE, Irp);
4545 if (!NT_SUCCESS(Status)) {
4546 ERR("find_item returned %08x\n", Status);
4547 goto end;
4548 }
4549
4550 if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type &&
4551 tp2.item->size >= offsetof(ROOT_ITEM, otransid)) {
4552 ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data;
4553
4554 if (ri->ctransid == bfs->ctransid) {
4555 TRACE("found subvol %llx\n", *id);
4556 Status = get_subvol_path(Vcb, *id, out, outlen, Irp);
4557 goto end;
4558 }
4559 }
4560 } else {
4561 TRACE("found subvol %llx\n", *id);
4562 Status = get_subvol_path(Vcb, *id, out, outlen, Irp);
4563 goto end;
4564 }
4565 }
4566
4567 searchkey.obj_type = TYPE_SUBVOL_REC_UUID;
4568
4569 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
4570
4571 if (!NT_SUCCESS(Status)) {
4572 ERR("find_item returned %08x\n", Status);
4573 goto end;
4574 }
4575
4576 if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(UINT64)) {
4577 UINT64* ids = (UINT64*)tp.item->data;
4578 ULONG i;
4579
4580 for (i = 0; i < tp.item->size / sizeof(UINT64); i++) {
4581 if (bfs->ctransid != 0) {
4582 KEY searchkey2;
4583 traverse_ptr tp2;
4584
4585 searchkey2.obj_id = ids[i];
4586 searchkey2.obj_type = TYPE_ROOT_ITEM;
4587 searchkey2.offset = 0xffffffffffffffff;
4588
4589 Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, FALSE, Irp);
4590 if (!NT_SUCCESS(Status)) {
4591 ERR("find_item returned %08x\n", Status);
4592 goto end;
4593 }
4594
4595 if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type &&
4596 tp2.item->size >= offsetof(ROOT_ITEM, otransid)) {
4597 ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data;
4598
4599 if (ri->ctransid == bfs->ctransid) {
4600 TRACE("found subvol %llx\n", ids[i]);
4601 Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp);
4602 goto end;
4603 }
4604 }
4605 } else {
4606 TRACE("found subvol %llx\n", ids[i]);
4607 Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp);
4608 goto end;
4609 }
4610 }
4611 }
4612
4613 Status = STATUS_NOT_FOUND;
4614
4615 end:
4616 ExReleaseResourceLite(&Vcb->tree_lock);
4617
4618 return Status;
4619 }
4620
4621 static NTSTATUS resize_device(device_extension* Vcb, void* data, ULONG len, PIRP Irp) {
4622 btrfs_resize* br = (btrfs_resize*)data;
4623 NTSTATUS Status;
4624 LIST_ENTRY* le;
4625 device* dev = NULL;
4626
4627 TRACE("(%p, %p, %u)\n", Vcb, data, len);
4628
4629 if (!data || len < sizeof(btrfs_resize) || (br->size % Vcb->superblock.sector_size) != 0)
4630 return STATUS_INVALID_PARAMETER;
4631
4632 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
4633 return STATUS_PRIVILEGE_NOT_HELD;
4634
4635 if (Vcb->readonly)
4636 return STATUS_MEDIA_WRITE_PROTECTED;
4637
4638 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
4639
4640 le = Vcb->devices.Flink;
4641 while (le != &Vcb->devices) {
4642 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4643
4644 if (dev2->devitem.dev_id == br->device) {
4645 dev = dev2;
4646 break;
4647 }
4648
4649 le = le->Flink;
4650 }
4651
4652 if (!dev) {
4653 ERR("could not find device %llx\n", br->device);
4654 Status = STATUS_INVALID_PARAMETER;
4655 goto end;
4656 }
4657
4658 if (!dev->devobj) {
4659 ERR("trying to resize missing device\n");
4660 Status = STATUS_INVALID_PARAMETER;
4661 goto end;
4662 }
4663
4664 if (dev->readonly) {
4665 ERR("trying to resize readonly device\n");
4666 Status = STATUS_INVALID_PARAMETER;
4667 goto end;
4668 }
4669
4670 if (br->size > 0 && dev->devitem.num_bytes == br->size) {
4671 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4672 Status = STATUS_SUCCESS;
4673 goto end;
4674 }
4675
4676 if (br->size > 0 && dev->devitem.num_bytes > br->size) { // shrink device
4677 BOOL need_balance = TRUE;
4678 UINT64 old_size, delta;
4679
4680 le = dev->space.Flink;
4681 while (le != &dev->space) {
4682 space* s = CONTAINING_RECORD(le, space, list_entry);
4683
4684 if (s->address <= br->size && s->address + s->size >= dev->devitem.num_bytes) {
4685 need_balance = FALSE;
4686 break;
4687 }
4688
4689 le = le->Flink;
4690 }
4691
4692 delta = dev->devitem.num_bytes - br->size;
4693
4694 if (need_balance) {
4695 int i;
4696
4697 if (Vcb->balance.thread) {
4698 WARN("balance already running\n");
4699 Status = STATUS_DEVICE_NOT_READY;
4700 goto end;
4701 }
4702
4703 RtlZeroMemory(Vcb->balance.opts, sizeof(btrfs_balance_opts) * 3);
4704
4705 for (i = 0; i < 3; i++) {
4706 Vcb->balance.opts[i].flags = BTRFS_BALANCE_OPTS_ENABLED | BTRFS_BALANCE_OPTS_DEVID | BTRFS_BALANCE_OPTS_DRANGE;
4707 Vcb->balance.opts[i].devid = dev->devitem.dev_id;
4708 Vcb->balance.opts[i].drange_start = br->size;
4709 Vcb->balance.opts[i].drange_end = dev->devitem.num_bytes;
4710 }
4711
4712 Vcb->balance.paused = FALSE;
4713 Vcb->balance.shrinking = TRUE;
4714 Vcb->balance.status = STATUS_SUCCESS;
4715 KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused);
4716
4717 space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL);
4718
4719 Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb);
4720 if (!NT_SUCCESS(Status)) {
4721 ERR("PsCreateSystemThread returned %08x\n", Status);
4722 goto end;
4723 }
4724
4725 Status = STATUS_MORE_PROCESSING_REQUIRED;
4726
4727 goto end;
4728 }
4729
4730 old_size = dev->devitem.num_bytes;
4731 dev->devitem.num_bytes = br->size;
4732
4733 Status = update_dev_item(Vcb, dev, Irp);
4734 if (!NT_SUCCESS(Status)) {
4735 ERR("update_dev_item returned %08x\n", Status);
4736 dev->devitem.num_bytes = old_size;
4737 goto end;
4738 }
4739
4740 space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL);
4741
4742 Vcb->superblock.total_bytes -= delta;
4743 } else { // extend device
4744 GET_LENGTH_INFORMATION gli;
4745 UINT64 old_size, delta;
4746
4747 Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
4748 &gli, sizeof(gli), TRUE, NULL);
4749 if (!NT_SUCCESS(Status)) {
4750 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status);
4751 goto end;
4752 }
4753
4754 if (br->size == 0) {
4755 br->size = gli.Length.QuadPart;
4756
4757 if (dev->devitem.num_bytes == br->size) {
4758 TRACE("size unchanged, returning STATUS_SUCCESS\n");
4759 Status = STATUS_SUCCESS;
4760 goto end;
4761 }
4762
4763 if (br->size == 0) {
4764 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n");
4765 Status = STATUS_INTERNAL_ERROR;
4766 goto end;
4767 }
4768 } else if ((UINT64)gli.Length.QuadPart < br->size) {
4769 ERR("device was %llx bytes, trying to extend to %llx\n", gli.Length.QuadPart, br->size);
4770 Status = STATUS_INVALID_PARAMETER;
4771 goto end;
4772 }
4773
4774 delta = br->size - dev->devitem.num_bytes;
4775
4776 old_size = dev->devitem.num_bytes;
4777 dev->devitem.num_bytes = br->size;
4778
4779 Status = update_dev_item(Vcb, dev, Irp);
4780 if (!NT_SUCCESS(Status)) {
4781 ERR("update_dev_item returned %08x\n", Status);
4782 dev->devitem.num_bytes = old_size;
4783 goto end;
4784 }
4785
4786 space_list_add2(&dev->space, NULL, dev->devitem.num_bytes, delta, NULL, NULL);
4787
4788 Vcb->superblock.total_bytes += delta;
4789 }
4790
4791 Status = STATUS_SUCCESS;
4792 Vcb->need_write = TRUE;
4793
4794 end:
4795 ExReleaseResourceLite(&Vcb->tree_lock);
4796
4797 if (NT_SUCCESS(Status))
4798 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
4799
4800 return Status;
4801 }
4802
4803 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, UINT32 type) {
4804 PIRP Irp = *Pirp;
4805 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4806 NTSTATUS Status;
4807
4808 switch (type) {
4809 #if (NTDDI_VERSION >= NTDDI_WIN7)
4810 case FSCTL_REQUEST_OPLOCK:
4811 WARN("STUB: FSCTL_REQUEST_OPLOCK\n");
4812 Status = STATUS_INVALID_DEVICE_REQUEST;
4813 break;
4814 #endif
4815
4816 case FSCTL_REQUEST_OPLOCK_LEVEL_1:
4817 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
4818 Status = STATUS_INVALID_DEVICE_REQUEST;
4819 break;
4820
4821 case FSCTL_REQUEST_OPLOCK_LEVEL_2:
4822 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
4823 Status = STATUS_INVALID_DEVICE_REQUEST;
4824 break;
4825
4826 case FSCTL_REQUEST_BATCH_OPLOCK:
4827 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
4828 Status = STATUS_INVALID_DEVICE_REQUEST;
4829 break;
4830
4831 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
4832 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
4833 Status = STATUS_INVALID_DEVICE_REQUEST;
4834 break;
4835
4836 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
4837 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
4838 Status = STATUS_INVALID_DEVICE_REQUEST;
4839 break;
4840
4841 case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
4842 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
4843 Status = STATUS_INVALID_DEVICE_REQUEST;
4844 break;
4845
4846 case FSCTL_OPLOCK_BREAK_NOTIFY:
4847 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
4848 Status = STATUS_INVALID_DEVICE_REQUEST;
4849 break;
4850
4851 case FSCTL_REQUEST_FILTER_OPLOCK:
4852 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
4853 Status = STATUS_INVALID_DEVICE_REQUEST;
4854 break;
4855
4856 case FSCTL_LOCK_VOLUME:
4857 Status = lock_volume(DeviceObject->DeviceExtension, Irp);
4858 break;
4859
4860 case FSCTL_UNLOCK_VOLUME:
4861 Status = unlock_volume(DeviceObject->DeviceExtension, Irp);
4862 break;
4863
4864 case FSCTL_DISMOUNT_VOLUME:
4865 Status = dismount_volume(DeviceObject->DeviceExtension, Irp);
4866 break;
4867
4868 case FSCTL_IS_VOLUME_MOUNTED:
4869 Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp);
4870 break;
4871
4872 case FSCTL_IS_PATHNAME_VALID:
4873 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
4874 Status = STATUS_INVALID_DEVICE_REQUEST;
4875 break;
4876
4877 case FSCTL_MARK_VOLUME_DIRTY:
4878 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
4879 Status = STATUS_INVALID_DEVICE_REQUEST;
4880 break;
4881
4882 case FSCTL_QUERY_RETRIEVAL_POINTERS:
4883 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
4884 Status = STATUS_INVALID_DEVICE_REQUEST;
4885 break;
4886
4887 case FSCTL_GET_COMPRESSION:
4888 Status = get_compression(Irp);
4889 break;
4890
4891 case FSCTL_SET_COMPRESSION:
4892 Status = set_compression(Irp);
4893 break;
4894
4895 case FSCTL_SET_BOOTLOADER_ACCESSED:
4896 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
4897 Status = STATUS_INVALID_DEVICE_REQUEST;
4898 break;
4899
4900 case FSCTL_INVALIDATE_VOLUMES:
4901 Status = invalidate_volumes(Irp);
4902 break;
4903
4904 case FSCTL_QUERY_FAT_BPB:
4905 WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
4906 Status = STATUS_INVALID_DEVICE_REQUEST;
4907 break;
4908
4909 case FSCTL_FILESYSTEM_GET_STATISTICS:
4910 Status = fs_get_statistics(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
4911 break;
4912
4913 case FSCTL_GET_NTFS_VOLUME_DATA:
4914 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
4915 Status = STATUS_INVALID_DEVICE_REQUEST;
4916 break;
4917
4918 case FSCTL_GET_NTFS_FILE_RECORD:
4919 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
4920 Status = STATUS_INVALID_DEVICE_REQUEST;
4921 break;
4922
4923 case FSCTL_GET_VOLUME_BITMAP:
4924 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
4925 Status = STATUS_INVALID_DEVICE_REQUEST;
4926 break;
4927
4928 case FSCTL_GET_RETRIEVAL_POINTERS:
4929 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
4930 Status = STATUS_INVALID_DEVICE_REQUEST;
4931 break;
4932
4933 case FSCTL_MOVE_FILE:
4934 WARN("STUB: FSCTL_MOVE_FILE\n");
4935 Status = STATUS_INVALID_DEVICE_REQUEST;
4936 break;
4937
4938 case FSCTL_IS_VOLUME_DIRTY:
4939 Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp);
4940 break;
4941
4942 case FSCTL_ALLOW_EXTENDED_DASD_IO:
4943 Status = allow_extended_dasd_io(DeviceObject->DeviceExtension, IrpSp->FileObject);
4944 break;
4945
4946 case FSCTL_FIND_FILES_BY_SID:
4947 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
4948 Status = STATUS_INVALID_DEVICE_REQUEST;
4949 break;
4950
4951 case FSCTL_SET_OBJECT_ID:
4952 WARN("STUB: FSCTL_SET_OBJECT_ID\n");
4953 Status = STATUS_INVALID_DEVICE_REQUEST;
4954 break;
4955
4956 case FSCTL_GET_OBJECT_ID:
4957 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
4958 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
4959 break;
4960
4961 case FSCTL_DELETE_OBJECT_ID:
4962 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
4963 Status = STATUS_INVALID_DEVICE_REQUEST;
4964 break;
4965
4966 case FSCTL_SET_REPARSE_POINT:
4967 Status = set_reparse_point(DeviceObject, Irp);
4968 break;
4969
4970 case FSCTL_GET_REPARSE_POINT:
4971 Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
4972 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
4973 break;
4974
4975 case FSCTL_DELETE_REPARSE_POINT:
4976 Status = delete_reparse_point(DeviceObject, Irp);
4977 break;
4978
4979 case FSCTL_ENUM_USN_DATA:
4980 WARN("STUB: FSCTL_ENUM_USN_DATA\n");
4981 Status = STATUS_INVALID_DEVICE_REQUEST;
4982 break;
4983
4984 case FSCTL_SECURITY_ID_CHECK:
4985 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
4986 Status = STATUS_INVALID_DEVICE_REQUEST;
4987 break;
4988
4989 case FSCTL_READ_USN_JOURNAL:
4990 WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
4991 Status = STATUS_INVALID_DEVICE_REQUEST;
4992 break;
4993
4994 case FSCTL_SET_OBJECT_ID_EXTENDED:
4995 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
4996 Status = STATUS_INVALID_DEVICE_REQUEST;
4997 break;
4998
4999 case FSCTL_CREATE_OR_GET_OBJECT_ID:
5000 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer,
5001 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5002 break;
5003
5004 case FSCTL_SET_SPARSE:
5005 Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5006 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5007 break;
5008
5009 case FSCTL_SET_ZERO_DATA:
5010 Status = set_zero_data(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5011 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5012 break;
5013
5014 case FSCTL_QUERY_ALLOCATED_RANGES:
5015 Status = query_ranges(IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
5016 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer,
5017 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
5018 break;
5019
5020 case FSCTL_ENABLE_UPGRADE:
5021 WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
5022 Status = STATUS_INVALID_DEVICE_REQUEST;
5023 break;
5024
5025 case FSCTL_SET_ENCRYPTION:
5026 WARN("STUB: FSCTL_SET_ENCRYPTION\n");
5027 Status = STATUS_INVALID_DEVICE_REQUEST;
5028 break;
5029
5030 case FSCTL_ENCRYPTION_FSCTL_IO:
5031 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
5032 Status = STATUS_INVALID_DEVICE_REQUEST;
5033 break;
5034
5035 case FSCTL_WRITE_RAW_ENCRYPTED:
5036 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
5037 Status = STATUS_INVALID_DEVICE_REQUEST;
5038 break;
5039
5040 case FSCTL_READ_RAW_ENCRYPTED:
5041 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
5042 Status = STATUS_INVALID_DEVICE_REQUEST;
5043 break;
5044
5045 case FSCTL_CREATE_USN_JOURNAL:
5046 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
5047 Status = STATUS_INVALID_DEVICE_REQUEST;
5048 break;
5049
5050 case FSCTL_READ_FILE_USN_DATA:
5051 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
5052 Status = STATUS_INVALID_DEVICE_REQUEST;
5053 break;
5054
5055 case FSCTL_WRITE_USN_CLOSE_RECORD:
5056 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
5057 Status = STATUS_INVALID_DEVICE_REQUEST;
5058 break;
5059
5060 case FSCTL_EXTEND_VOLUME:
5061 WARN("STUB: FSCTL_EXTEND_VOLUME\n");
5062 Status = STATUS_INVALID_DEVICE_REQUEST;
5063 break;
5064
5065 case FSCTL_QUERY_USN_JOURNAL:
5066 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
5067 Status = STATUS_INVALID_DEVICE_REQUEST;
5068 break;
5069
5070 case FSCTL_DELETE_USN_JOURNAL:
5071 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
5072 Status = STATUS_INVALID_DEVICE_REQUEST;
5073 break;
5074
5075 case FSCTL_MARK_HANDLE:
5076 WARN("STUB: FSCTL_MARK_HANDLE\n");
5077 Status = STATUS_INVALID_DEVICE_REQUEST;
5078 break;
5079
5080 case FSCTL_SIS_COPYFILE:
5081 WARN("STUB: FSCTL_SIS_COPYFILE\n");
5082 Status = STATUS_INVALID_DEVICE_REQUEST;
5083 break;
5084
5085 case FSCTL_SIS_LINK_FILES:
5086 WARN("STUB: FSCTL_SIS_LINK_FILES\n");
5087 Status = STATUS_INVALID_DEVICE_REQUEST;
5088 break;
5089
5090 case FSCTL_RECALL_FILE:
5091 WARN("STUB: FSCTL_RECALL_FILE\n");
5092 Status = STATUS_INVALID_DEVICE_REQUEST;
5093 break;
5094
5095 case FSCTL_READ_FROM_PLEX:
5096 WARN("STUB: FSCTL_READ_FROM_PLEX\n");
5097 Status = STATUS_INVALID_DEVICE_REQUEST;
5098 break;
5099
5100 case FSCTL_FILE_PREFETCH:
5101 WARN("STUB: FSCTL_FILE_PREFETCH\n");
5102 Status = STATUS_INVALID_DEVICE_REQUEST;
5103 break;
5104
5105 #if _WIN32_WINNT >= 0x0600
5106 case FSCTL_MAKE_MEDIA_COMPATIBLE:
5107 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
5108 Status = STATUS_INVALID_DEVICE_REQUEST;
5109 break;
5110
5111 case FSCTL_SET_DEFECT_MANAGEMENT:
5112 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
5113 Status = STATUS_INVALID_DEVICE_REQUEST;
5114 break;
5115
5116 case FSCTL_QUERY_SPARING_INFO:
5117 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
5118 Status = STATUS_INVALID_DEVICE_REQUEST;
5119 break;
5120
5121 case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
5122 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
5123 Status = STATUS_INVALID_DEVICE_REQUEST;
5124 break;
5125
5126 case FSCTL_SET_VOLUME_COMPRESSION_STATE:
5127 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
5128 Status = STATUS_INVALID_DEVICE_REQUEST;
5129 break;
5130
5131 case FSCTL_TXFS_MODIFY_RM:
5132 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
5133 Status = STATUS_INVALID_DEVICE_REQUEST;
5134 break;
5135
5136 case FSCTL_TXFS_QUERY_RM_INFORMATION:
5137 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
5138 Status = STATUS_INVALID_DEVICE_REQUEST;
5139 break;
5140
5141 case FSCTL_TXFS_ROLLFORWARD_REDO:
5142 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
5143 Status = STATUS_INVALID_DEVICE_REQUEST;
5144 break;
5145
5146 case FSCTL_TXFS_ROLLFORWARD_UNDO:
5147 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
5148 Status = STATUS_INVALID_DEVICE_REQUEST;
5149 break;
5150
5151 case FSCTL_TXFS_START_RM:
5152 WARN("STUB: FSCTL_TXFS_START_RM\n");
5153 Status = STATUS_INVALID_DEVICE_REQUEST;
5154 break;
5155
5156 case FSCTL_TXFS_SHUTDOWN_RM:
5157 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
5158 Status = STATUS_INVALID_DEVICE_REQUEST;
5159 break;
5160
5161 case FSCTL_TXFS_READ_BACKUP_INFORMATION:
5162 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
5163 Status = STATUS_INVALID_DEVICE_REQUEST;
5164 break;
5165
5166 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
5167 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
5168 Status = STATUS_INVALID_DEVICE_REQUEST;
5169 break;
5170
5171 case FSCTL_TXFS_CREATE_SECONDARY_RM:
5172 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
5173 Status = STATUS_INVALID_DEVICE_REQUEST;
5174 break;
5175
5176 case FSCTL_TXFS_GET_METADATA_INFO:
5177 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
5178 Status = STATUS_INVALID_DEVICE_REQUEST;
5179 break;
5180
5181 case FSCTL_TXFS_GET_TRANSACTED_VERSION:
5182 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
5183 Status = STATUS_INVALID_DEVICE_REQUEST;
5184 break;
5185
5186 case FSCTL_TXFS_SAVEPOINT_INFORMATION:
5187 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
5188 Status = STATUS_INVALID_DEVICE_REQUEST;
5189 break;
5190
5191 case FSCTL_TXFS_CREATE_MINIVERSION:
5192 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
5193 Status = STATUS_INVALID_DEVICE_REQUEST;
5194 break;
5195
5196 case FSCTL_TXFS_TRANSACTION_ACTIVE:
5197 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
5198 Status = STATUS_INVALID_DEVICE_REQUEST;
5199 break;
5200
5201 case FSCTL_SET_ZERO_ON_DEALLOCATION:
5202 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
5203 Status = STATUS_INVALID_DEVICE_REQUEST;
5204 break;
5205
5206 case FSCTL_SET_REPAIR:
5207 WARN("STUB: FSCTL_SET_REPAIR\n");
5208 Status = STATUS_INVALID_DEVICE_REQUEST;
5209 break;
5210
5211 case FSCTL_GET_REPAIR:
5212 WARN("STUB: FSCTL_GET_REPAIR\n");
5213 Status = STATUS_INVALID_DEVICE_REQUEST;
5214 break;
5215
5216 case FSCTL_WAIT_FOR_REPAIR:
5217 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
5218 Status = STATUS_INVALID_DEVICE_REQUEST;
5219 break;
5220
5221 case FSCTL_INITIATE_REPAIR:
5222 WARN("STUB: FSCTL_INITIATE_REPAIR\n");
5223 Status = STATUS_INVALID_DEVICE_REQUEST;
5224 break;
5225
5226 case FSCTL_CSC_INTERNAL:
5227 WARN("STUB: FSCTL_CSC_INTERNAL\n");
5228 Status = STATUS_INVALID_DEVICE_REQUEST;
5229 break;
5230
5231 case FSCTL_SHRINK_VOLUME:
5232 WARN("STUB: FSCTL_SHRINK_VOLUME\n");
5233 Status = STATUS_INVALID_DEVICE_REQUEST;
5234 break;
5235
5236 case FSCTL_SET_SHORT_NAME_BEHAVIOR:
5237 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
5238 Status = STATUS_INVALID_DEVICE_REQUEST;
5239 break;
5240
5241 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
5242 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
5243 Status = STATUS_INVALID_DEVICE_REQUEST;
5244 break;
5245
5246 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
5247 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
5248 Status = STATUS_INVALID_DEVICE_REQUEST;
5249 break;
5250
5251 case FSCTL_TXFS_LIST_TRANSACTIONS:
5252 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
5253 Status = STATUS_INVALID_DEVICE_REQUEST;
5254 break;
5255
5256 case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
5257 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
5258 Status = STATUS_INVALID_DEVICE_REQUEST;
5259 break;
5260
5261 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
5262 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
5263 Status = STATUS_INVALID_DEVICE_REQUEST;
5264 break;
5265
5266 case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
5267 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
5268 Status = STATUS_INVALID_DEVICE_REQUEST;
5269 break;
5270
5271 case FSCTL_CSV_CONTROL:
5272 WARN("STUB: FSCTL_CSV_CONTROL\n");
5273 Status = STATUS_INVALID_DEVICE_REQUEST;
5274 break;
5275 #endif
5276 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl
5277 case FSCTL_QUERY_VOLUME_CONTAINER_STATE:
5278 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n");
5279 Status = STATUS_INVALID_DEVICE_REQUEST;
5280 break;
5281
5282 case FSCTL_GET_INTEGRITY_INFORMATION:
5283 Status = get_integrity_information(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority),
5284 IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5285 break;
5286
5287 case FSCTL_SET_INTEGRITY_INFORMATION:
5288 Status = set_integrity_information(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength);
5289 break;
5290
5291 case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
5292 Status = duplicate_extents(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5293 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5294 break;
5295
5296 case FSCTL_BTRFS_GET_FILE_IDS:
5297 Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5298 break;
5299
5300 case FSCTL_BTRFS_CREATE_SUBVOL:
5301 Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5302 break;
5303
5304 case FSCTL_BTRFS_CREATE_SNAPSHOT:
5305 Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5306 break;
5307
5308 case FSCTL_BTRFS_GET_INODE_INFO:
5309 Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5310 break;
5311
5312 case FSCTL_BTRFS_SET_INODE_INFO:
5313 Status = set_inode_info(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5314 break;
5315
5316 case FSCTL_BTRFS_GET_DEVICES:
5317 Status = get_devices(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5318 break;
5319
5320 case FSCTL_BTRFS_GET_USAGE:
5321 Status = get_usage(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
5322 break;
5323
5324 case FSCTL_BTRFS_START_BALANCE:
5325 Status = start_balance(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5326 break;
5327
5328 case FSCTL_BTRFS_QUERY_BALANCE:
5329 Status = query_balance(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5330 break;
5331
5332 case FSCTL_BTRFS_PAUSE_BALANCE:
5333 Status = pause_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5334 break;
5335
5336 case FSCTL_BTRFS_RESUME_BALANCE:
5337 Status = resume_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5338 break;
5339
5340 case FSCTL_BTRFS_STOP_BALANCE:
5341 Status = stop_balance(DeviceObject->DeviceExtension, Irp->RequestorMode);
5342 break;
5343
5344 case FSCTL_BTRFS_ADD_DEVICE:
5345 Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->RequestorMode);
5346 break;
5347
5348 case FSCTL_BTRFS_REMOVE_DEVICE:
5349 Status = remove_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5350 break;
5351
5352 case FSCTL_BTRFS_GET_UUID:
5353 Status = query_uuid(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5354 break;
5355
5356 case FSCTL_BTRFS_START_SCRUB:
5357 Status = start_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5358 break;
5359
5360 case FSCTL_BTRFS_QUERY_SCRUB:
5361 Status = query_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
5362 break;
5363
5364 case FSCTL_BTRFS_PAUSE_SCRUB:
5365 Status = pause_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5366 break;
5367
5368 case FSCTL_BTRFS_RESUME_SCRUB:
5369 Status = resume_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5370 break;
5371
5372 case FSCTL_BTRFS_STOP_SCRUB:
5373 Status = stop_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
5374 break;
5375
5376 case FSCTL_BTRFS_RESET_STATS:
5377 Status = reset_stats(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5378 break;
5379
5380 case FSCTL_BTRFS_MKNOD:
5381 Status = mknod(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5382 break;
5383
5384 case FSCTL_BTRFS_RECEIVED_SUBVOL:
5385 Status = recvd_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5386 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
5387 break;
5388
5389 case FSCTL_BTRFS_GET_XATTRS:
5390 Status = fsctl_get_xattrs(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp->RequestorMode);
5391 break;
5392
5393 case FSCTL_BTRFS_SET_XATTR:
5394 Status = fsctl_set_xattr(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
5395 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5396 break;
5397
5398 case FSCTL_BTRFS_RESERVE_SUBVOL:
5399 Status = reserve_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp);
5400 break;
5401
5402 case FSCTL_BTRFS_FIND_SUBVOL:
5403 Status = find_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength,
5404 Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
5405 break;
5406
5407 case FSCTL_BTRFS_SEND_SUBVOL:
5408 Status = send_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength,
5409 IrpSp->FileObject, Irp);
5410 break;
5411
5412 case FSCTL_BTRFS_READ_SEND_BUFFER:
5413 Status = read_send_buffer(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength,
5414 &Irp->IoStatus.Information, Irp->RequestorMode);
5415 break;
5416
5417 case FSCTL_BTRFS_RESIZE:
5418 Status = resize_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer,
5419 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
5420 break;
5421
5422 default:
5423 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
5424 IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
5425 (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
5426 IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
5427 Status = STATUS_INVALID_DEVICE_REQUEST;
5428 break;
5429 }
5430
5431 return Status;
5432 }