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