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