ReactOS  0.4.14-dev-55-g2da92ac
fsctl.c
Go to the documentation of this file.
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;
46 extern PDRIVER_OBJECT drvobj;
47 
49 
51  btrfs_get_file_ids* bgfi;
52  fcb* fcb;
53 
54  if (length < sizeof(btrfs_get_file_ids))
56 
57  if (!FileObject)
59 
60  fcb = FileObject->FsContext;
61 
62  if (!fcb)
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_t i;
77 
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 
89  uint8_t* buf;
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");
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 
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");
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 %I64x\n", t.new_address);
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_t 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_t*)&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
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_t 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_t*)buf) = ~calc_crc32c(0xffffffff, (uint8_t*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
186 
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) {
204 
205  if (stripe->status != WriteDataStatus_Ignore) {
206  need_wait = true;
208  }
209 
210  le = le->Flink;
211  }
212 
213  if (need_wait)
215 
216  le = wtc.stripes.Flink;
217  while (le != &wtc.stripes) {
219 
220  if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
221  Status = stripe->iosb.Status;
223  break;
224  }
225 
226  le = le->Flink;
227  }
228 
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);
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 
263  uint64_t id;
265  root *r, *subvol = subvol_fcb->subvol;
266  KEY searchkey;
268  uint64_t address, *root_num;
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 
295 
296  // flush metadata
297 
298  if (Vcb->need_write)
299  Status = do_write(Vcb, Irp);
300  else
302 
303  free_trees(Vcb);
304 
305  if (!NT_SUCCESS(Status)) {
306  ERR("do_write returned %08x\n", Status);
307  return Status;
308  }
309 
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_t), ALLOC_TAG);
340  if (!root_num) {
341  ERR("out of memory\n");
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_t));
352  searchkey.obj_type = TYPE_SUBVOL_UUID;
353  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
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_t), 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 
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 %I64x\n", r->id);
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 
425 
426  // create fileref for entry in other subvolume
427 
428  fr = create_fileref(Vcb);
429  if (!fr) {
430  ERR("out of memory\n");
432  goto end;
433  }
434 
435  Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, false, fcb, &fr->fcb, PagedPool, Irp);
436  if (!NT_SUCCESS(Status)) {
437  ERR("open_fcb returned %08x\n", Status);
438  free_fileref(fr);
439  goto end;
440  }
441 
442  fr->parent = fileref;
443 
444  Status = add_dir_child(fileref->fcb, r->id, true, utf8, name, BTRFS_TYPE_DIRECTORY, &dc);
445  if (!NT_SUCCESS(Status))
446  WARN("add_dir_child returned %08x\n", Status);
447 
448  fr->dc = dc;
449  dc->fileref = fr;
450 
451  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
452  InsertTailList(&fileref->children, &fr->list_entry);
453  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
454 
456 
457  fr->created = true;
458  mark_fileref_dirty(fr);
459 
460  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
461  fr->fcb->fileref = fr;
462 
463  fr->fcb->subvol->parent = fileref->fcb->subvol->id;
464 
465  free_fileref(fr);
466 
467  // change fcb's INODE_ITEM
468 
469  fcb->inode_item.transid = Vcb->superblock.generation;
471  fcb->inode_item.st_size += utf8->Length * 2;
472 
475 
476  if (!ccb->user_set_write_time)
478 
479  fcb->inode_item_changed = true;
481 
482  fcb->subvol->root_item.ctime = now;
483  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
484 
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) {
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))
515  else
517 
518  return Status;
519 }
520 
522  PFILE_OBJECT subvol_obj;
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 
541 
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
556 
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)
573 
574  if (!FileObject || !FileObject->FsContext)
576 
577  fcb = FileObject->FsContext;
578  ccb = FileObject->FsContext2;
579 
580  if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
582 
583  fileref = ccb->fileref;
584 
585  if (!fileref) {
586  ERR("fileref was NULL\n");
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)
597 
599  return STATUS_ACCESS_DENIED;
600 
601  if (!is_file_name_valid(&nameus, posix))
603 
604  utf8.Buffer = NULL;
605 
606  Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
607  if (!NT_SUCCESS(Status)) {
608  ERR("utf16_to_utf8 failed with error %08x\n", Status);
609  return Status;
610  }
611 
612  if (len == 0) {
613  ERR("utf16_to_utf8 returned a length of 0\n");
614  return STATUS_INTERNAL_ERROR;
615  }
616 
617  if (len > 0xffff) {
618  ERR("len was too long\n");
620  }
621 
622  utf8.MaximumLength = utf8.Length = (USHORT)len;
624 
625  if (!utf8.Buffer) {
626  ERR("out of memory\n");
628  }
629 
630  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
631  if (!NT_SUCCESS(Status)) {
632  ERR("utf16_to_utf8 failed with error %08x\n", Status);
633  goto end2;
634  }
635 
636  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
637 
638  // no need for fcb_lock as we have tree_lock exclusively
639  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || posix, Irp);
640 
641  if (NT_SUCCESS(Status)) {
642  if (!fr2->deleted) {
643  WARN("file already exists\n");
644  free_fileref(fr2);
646  goto end3;
647  } else
648  free_fileref(fr2);
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) {
662  goto end;
663  }
664 
665  subvol_fcb = subvol_obj->FsContext;
666  if (!subvol_fcb) {
668  goto end;
669  }
670 
671  if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
672  WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
674  goto end;
675  }
676 
677  ccb = subvol_obj->FsContext2;
678 
679  if (!ccb) {
681  goto end;
682  }
683 
684  if (!(ccb->access & FILE_TRAVERSE)) {
685  WARN("insufficient privileges\n");
687  goto end;
688  }
689 
690  if (fcb == Vcb->dummy_fcb) {
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) {
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);
727  } else {
729  free_fileref(fr);
730  }
731  }
732 
733 end:
734  ObDereferenceObject(subvol_obj);
735 
736 end3:
737  ExReleaseResourceLite(&Vcb->tree_lock);
738 
739 end2:
740  ExFreePool(utf8.Buffer);
741 
742  return Status;
743 }
744 
746  btrfs_create_subvol* bcs;
747  fcb *fcb, *rootfcb = NULL;
748  ccb* ccb;
749  file_ref* fileref;
751  uint64_t id;
752  root* r = NULL;
754  BTRFS_TIME now;
755  ULONG len;
756  uint16_t irsize;
757  UNICODE_STRING nameus;
758  ANSI_STRING utf8;
759  INODE_REF* ir;
760  KEY searchkey;
762  SECURITY_SUBJECT_CONTEXT subjcont;
763  PSID owner;
764  BOOLEAN defaulted;
765  uint64_t* 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");
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)
805 
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))
814 
815  bcs = (btrfs_create_subvol*)data;
816 
817  if (offsetof(btrfs_create_subvol, name[0]) + bcs->namelen > datalen)
819 
820  nameus.Length = nameus.MaximumLength = bcs->namelen;
821  nameus.Buffer = bcs->name;
822 
823  if (!is_file_name_valid(&nameus, bcs->posix))
825 
826  utf8.Buffer = NULL;
827 
828  Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
829  if (!NT_SUCCESS(Status)) {
830  ERR("utf16_to_utf8 failed with error %08x\n", Status);
831  return Status;
832  }
833 
834  if (len == 0) {
835  ERR("utf16_to_utf8 returned a length of 0\n");
836  return STATUS_INTERNAL_ERROR;
837  }
838 
839  if (len > 0xffff) {
840  ERR("len was too long\n");
842  }
843 
844  utf8.MaximumLength = utf8.Length = (USHORT)len;
846 
847  if (!utf8.Buffer) {
848  ERR("out of memory\n");
850  }
851 
852  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
853  if (!NT_SUCCESS(Status)) {
854  ERR("utf16_to_utf8 failed with error %08x\n", Status);
855  goto end2;
856  }
857 
858  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
859 
862 
863  // no need for fcb_lock as we have tree_lock exclusively
864  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || bcs->posix, Irp);
865 
866  if (NT_SUCCESS(Status)) {
867  if (!fr2->deleted) {
868  WARN("file already exists\n");
869  free_fileref(fr2);
871  goto end;
872  } else
873  free_fileref(fr2);
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 %I64x\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_t), ALLOC_TAG);
905  if (!root_num) {
906  ERR("out of memory\n");
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_t));
917  searchkey.obj_type = TYPE_SUBVOL_UUID;
918  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
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_t), 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");
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");
988  goto end;
989  }
990 
991  Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted);
992  if (!NT_SUCCESS(Status)) {
993  ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
994  rootfcb->inode_item.st_uid = UID_NOBODY;
995  rootfcb->sd_dirty = true;
996  } else {
997  rootfcb->inode_item.st_uid = sid_to_uid(owner);
998  rootfcb->sd_dirty = rootfcb->inode_item.st_uid == UID_NOBODY;
999  }
1000 
1001  find_gid(rootfcb, fileref->fcb, &subjcont);
1002 
1003  rootfcb->inode_item_changed = true;
1004 
1005  acquire_fcb_lock_exclusive(Vcb);
1006  InsertTailList(&r->fcbs, &rootfcb->list_entry);
1007  InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
1008  r->fcbs_version++;
1009  release_fcb_lock(Vcb);
1010 
1011  rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1012  rootfcb->Header.AllocationSize.QuadPart = 0;
1013  rootfcb->Header.FileSize.QuadPart = 0;
1014  rootfcb->Header.ValidDataLength.QuadPart = 0;
1015 
1016  rootfcb->created = true;
1017 
1018  if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1020 
1021  rootfcb->prop_compression = fileref->fcb->prop_compression;
1023 
1024  r->lastinode = rootfcb->inode;
1025 
1026  // add INODE_REF
1027 
1028  irsize = (uint16_t)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1029  ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1030  if (!ir) {
1031  ERR("out of memory\n");
1033  goto end;
1034  }
1035 
1036  ir->index = 0;
1037  ir->n = sizeof(DOTDOT) - 1;
1038  RtlCopyMemory(ir->name, DOTDOT, ir->n);
1039 
1040  Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1041  if (!NT_SUCCESS(Status)) {
1042  ERR("insert_tree_item returned %08x\n", Status);
1043  ExFreePool(ir);
1044  goto end;
1045  }
1046 
1047  // create fileref for entry in other subvolume
1048 
1049  fr = create_fileref(Vcb);
1050  if (!fr) {
1051  ERR("out of memory\n");
1052 
1053  reap_fcb(rootfcb);
1054 
1056  goto end;
1057  }
1058 
1059  fr->fcb = rootfcb;
1060 
1061  mark_fcb_dirty(rootfcb);
1062 
1063  fr->parent = fileref;
1064 
1065  Status = add_dir_child(fileref->fcb, r->id, true, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1066  if (!NT_SUCCESS(Status))
1067  WARN("add_dir_child returned %08x\n", Status);
1068 
1069  fr->dc = dc;
1070  dc->fileref = fr;
1071 
1073  if (!fr->fcb->hash_ptrs) {
1074  ERR("out of memory\n");
1075  free_fileref(fr);
1077  goto end;
1078  }
1079 
1080  RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1081 
1083  if (!fr->fcb->hash_ptrs_uc) {
1084  ERR("out of memory\n");
1085  free_fileref(fr);
1087  goto end;
1088  }
1089 
1090  RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1091 
1092  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
1093  InsertTailList(&fileref->children, &fr->list_entry);
1094  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1095 
1097 
1098  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1099  fr->fcb->fileref = fr;
1100 
1101  fr->created = true;
1102  mark_fileref_dirty(fr);
1103 
1104  // change fcb->subvol's ROOT_ITEM
1105 
1106  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1107  fcb->subvol->root_item.ctime = now;
1108 
1109  // change fcb's INODE_ITEM
1110 
1111  fcb->inode_item.transid = Vcb->superblock.generation;
1112  fcb->inode_item.st_size += utf8.Length * 2;
1113  fcb->inode_item.sequence++;
1114 
1115  if (!ccb->user_set_change_time)
1117 
1118  if (!ccb->user_set_write_time)
1120 
1121  fcb->inode_item_changed = true;
1123 
1124  fr->fcb->subvol->parent = fcb->subvol->id;
1125 
1127 
1128 end:
1129  if (!NT_SUCCESS(Status)) {
1130  if (fr) {
1131  fr->deleted = true;
1132  mark_fileref_dirty(fr);
1133  } else if (rootfcb) {
1134  rootfcb->deleted = true;
1135  mark_fcb_dirty(rootfcb);
1136  }
1137 
1138  if (r) {
1139  RemoveEntryList(&r->list_entry);
1140  InsertTailList(&Vcb->drop_roots, &r->list_entry);
1141  }
1142  }
1143 
1144  ExReleaseResourceLite(&Vcb->tree_lock);
1145 
1146  if (NT_SUCCESS(Status)) {
1149  }
1150 
1151 end2:
1152  if (fr)
1153  free_fileref(fr);
1154 
1155  return Status;
1156 }
1157 
1159  btrfs_inode_info* bii = data;
1160  fcb* fcb;
1161  ccb* ccb;
1162  bool old_style;
1163 
1164  if (length < offsetof(btrfs_inode_info, disk_size_zstd))
1165  return STATUS_BUFFER_OVERFLOW;
1166 
1167  if (!FileObject)
1168  return STATUS_INVALID_PARAMETER;
1169 
1170  fcb = FileObject->FsContext;
1171 
1172  if (!fcb)
1173  return STATUS_INVALID_PARAMETER;
1174 
1175  ccb = FileObject->FsContext2;
1176 
1177  if (!ccb)
1178  return STATUS_INVALID_PARAMETER;
1179 
1180  if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1181  WARN("insufficient privileges\n");
1182  return STATUS_ACCESS_DENIED;
1183  }
1184 
1185  if (fcb->ads)
1186  fcb = ccb->fileref->parent->fcb;
1187 
1188  old_style = length < offsetof(btrfs_inode_info, sparse_size) + sizeof(((btrfs_inode_info*)NULL)->sparse_size);
1189 
1190  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
1191 
1192  bii->subvol = fcb->subvol->id;
1193  bii->inode = fcb->inode;
1194  bii->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
1195  bii->type = fcb->type;
1196  bii->st_uid = fcb->inode_item.st_uid;
1197  bii->st_gid = fcb->inode_item.st_gid;
1198  bii->st_mode = fcb->inode_item.st_mode;
1199 
1200  if (fcb->inode_item.st_rdev == 0)
1201  bii->st_rdev = 0;
1202  else
1203  bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1204 
1205  bii->flags = fcb->inode_item.flags;
1206 
1207  bii->inline_length = 0;
1208  bii->disk_size_uncompressed = 0;
1209  bii->disk_size_zlib = 0;
1210  bii->disk_size_lzo = 0;
1211 
1212  if (!old_style) {
1213  bii->disk_size_zstd = 0;
1214  bii->sparse_size = 0;
1215  }
1216 
1217  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1218  uint64_t last_end = 0;
1219  LIST_ENTRY* le;
1220  bool extents_inline = false;
1221 
1222  le = fcb->extents.Flink;
1223  while (le != &fcb->extents) {
1225 
1226  if (!ext->ignore) {
1227  if (!old_style && ext->offset > last_end)
1228  bii->sparse_size += ext->offset - last_end;
1229 
1230  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1231  bii->inline_length += ext->datalen - (uint16_t)offsetof(EXTENT_DATA, data[0]);
1232  last_end = ext->offset + ext->extent_data.decoded_size;
1233  extents_inline = true;
1234  } else {
1235  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1236 
1237  // FIXME - compressed extents with a hole in them are counted more than once
1238  if (ed2->size != 0) {
1239  switch (ext->extent_data.compression) {
1242  break;
1243 
1245  bii->disk_size_zlib += ed2->size;
1246  break;
1247 
1248  case BTRFS_COMPRESSION_LZO:
1249  bii->disk_size_lzo += ed2->size;
1250  break;
1251 
1253  if (!old_style)
1254  bii->disk_size_zstd += ed2->size;
1255  break;
1256  }
1257  }
1258 
1259  last_end = ext->offset + ed2->num_bytes;
1260  }
1261  }
1262 
1263  le = le->Flink;
1264  }
1265 
1266  if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1267  bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1268 
1269  if (length >= offsetof(btrfs_inode_info, num_extents) + sizeof(((btrfs_inode_info*)NULL)->num_extents)) {
1270  EXTENT_DATA2* last_ed2 = NULL;
1271 
1272  le = fcb->extents.Flink;
1273 
1274  bii->num_extents = 0;
1275 
1276  while (le != &fcb->extents) {
1278 
1279  if (!ext->ignore && ext->extent_data.type != EXTENT_TYPE_INLINE) {
1280  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1281 
1282  if (ed2->size != 0) {
1283  if (!last_ed2 || ed2->offset != last_ed2->offset + last_ed2->num_bytes)
1284  bii->num_extents++;
1285 
1286  last_ed2 = ed2;
1287  } else
1288  last_ed2 = NULL;
1289  }
1290 
1291  le = le->Flink;
1292  }
1293  }
1294  }
1295 
1296  switch (fcb->prop_compression) {
1297  case PropCompression_Zlib:
1299  break;
1300 
1301  case PropCompression_LZO:
1303  break;
1304 
1305  case PropCompression_ZSTD:
1307  break;
1308 
1309  default:
1311  break;
1312  }
1313 
1314  ExReleaseResourceLite(fcb->Header.Resource);
1315 
1316  return STATUS_SUCCESS;
1317 }
1318 
1320  btrfs_set_inode_info* bsii = data;
1321  NTSTATUS Status;
1322  fcb* fcb;
1323  ccb* ccb;
1324 
1325  if (length < sizeof(btrfs_set_inode_info))
1326  return STATUS_INVALID_PARAMETER;
1327 
1328  if (!FileObject)
1329  return STATUS_INVALID_PARAMETER;
1330 
1331  fcb = FileObject->FsContext;
1332 
1333  if (!fcb)
1334  return STATUS_INVALID_PARAMETER;
1335 
1336  ccb = FileObject->FsContext2;
1337 
1338  if (!ccb)
1339  return STATUS_INVALID_PARAMETER;
1340 
1341  if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1342  WARN("insufficient privileges\n");
1343  return STATUS_ACCESS_DENIED;
1344  }
1345 
1346  if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1347  WARN("insufficient privileges\n");
1348  return STATUS_ACCESS_DENIED;
1349  }
1350 
1352  return STATUS_INVALID_PARAMETER;
1353 
1354  if (fcb->ads)
1355  fcb = ccb->fileref->parent->fcb;
1356 
1357  if (is_subvol_readonly(fcb->subvol, Irp)) {
1358  WARN("trying to change inode on readonly subvolume\n");
1359  return STATUS_ACCESS_DENIED;
1360  }
1361 
1362  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1363 
1364  if (bsii->flags_changed) {
1365  if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1367  WARN("trying to change nocow flag on non-empty file\n");
1369  goto end;
1370  }
1371 
1372  fcb->inode_item.flags = bsii->flags;
1373 
1376  else
1378  }
1379 
1380  if (bsii->mode_changed) {
1382  S_ISGID | S_ISVTX;
1383 
1384  if (ccb->access & WRITE_OWNER)
1385  allowed |= S_ISUID;
1386 
1387  fcb->inode_item.st_mode &= ~allowed;
1388  fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1389  }
1390 
1391  if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1392  fcb->inode_item.st_uid = bsii->st_uid;
1393 
1394  fcb->sd_dirty = true;
1395  fcb->sd_deleted = false;
1396  }
1397 
1398  if (bsii->gid_changed)
1399  fcb->inode_item.st_gid = bsii->st_gid;
1400 
1401  if (bsii->compression_type_changed) {
1402  switch (bsii->compression_type) {
1403  case BTRFS_COMPRESSION_ANY:
1405  break;
1406 
1409  break;
1410 
1411  case BTRFS_COMPRESSION_LZO:
1413  break;
1414 
1417  break;
1418  }
1419 
1420  fcb->prop_compression_changed = true;
1421  }
1422 
1423  if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1424  fcb->inode_item_changed = true;
1426  }
1427 
1429 
1430 end:
1431  ExReleaseResourceLite(fcb->Header.Resource);
1432 
1433  return Status;
1434 }
1435 
1437  btrfs_device* dev = NULL;
1438  NTSTATUS Status;
1439  LIST_ENTRY* le;
1440 
1441  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1442 
1443  le = Vcb->devices.Flink;
1444  while (le != &Vcb->devices) {
1445  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1446  ULONG structlen;
1447 
1448  if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1450  goto end;
1451  }
1452 
1453  if (!dev)
1454  dev = data;
1455  else {
1456  dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1457  dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry);
1458  }
1459 
1460  structlen = length - offsetof(btrfs_device, namelen);
1461 
1462  if (dev2->devobj) {
1463  Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL);
1464  if (!NT_SUCCESS(Status))
1465  goto end;
1466 
1467  dev->missing = false;
1468  } else {
1469  dev->namelen = 0;
1470  dev->missing = true;
1471  }
1472 
1473  dev->next_entry = 0;
1474  dev->dev_id = dev2->devitem.dev_id;
1475  dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false;
1476  dev->device_number = dev2->disk_num;
1477  dev->partition_number = dev2->part_num;
1478  dev->size = dev2->devitem.num_bytes;
1479 
1480  if (dev2->devobj) {
1482 
1483  Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
1484  if (!NT_SUCCESS(Status))
1485  goto end;
1486 
1487  dev->max_size = gli.Length.QuadPart;
1488  } else
1489  dev->max_size = dev->size;
1490 
1491  RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5);
1492 
1493  length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1494 
1495  le = le->Flink;
1496  }
1497 
1498 end:
1499  ExReleaseResourceLite(&Vcb->tree_lock);
1500 
1501  return Status;
1502 }
1503 
1506  btrfs_usage* lastbue = NULL;
1507  NTSTATUS Status;
1508  LIST_ENTRY* le;
1509 
1510  if (length < sizeof(btrfs_usage))
1511  return STATUS_BUFFER_OVERFLOW;
1512 
1513  if (!Vcb->chunk_usage_found) {
1514  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1515 
1516  if (!Vcb->chunk_usage_found)
1518  else
1520 
1521  ExReleaseResourceLite(&Vcb->tree_lock);
1522 
1523  if (!NT_SUCCESS(Status)) {
1524  ERR("find_chunk_usage returned %08x\n", Status);
1525  return Status;
1526  }
1527  }
1528 
1530 
1531  ExAcquireResourceSharedLite(&Vcb->chunk_lock, true);
1532 
1533  le = Vcb->chunks.Flink;
1534  while (le != &Vcb->chunks) {
1535  bool addnew = false;
1536 
1538 
1539  if (!lastbue) // first entry
1540  addnew = true;
1541  else {
1542  btrfs_usage* bue = usage;
1543 
1544  addnew = true;
1545 
1546  while (true) {
1547  if (bue->type == c->chunk_item->type) {
1548  addnew = false;
1549  break;
1550  }
1551 
1552  if (bue->next_entry == 0)
1553  break;
1554  else
1555  bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry);
1556  }
1557  }
1558 
1559  if (addnew) {
1560  btrfs_usage* bue;
1561  LIST_ENTRY* le2;
1562  uint64_t factor;
1563 
1564  if (!lastbue) {
1565  bue = usage;
1566  } else {
1567  if (length < offsetof(btrfs_usage, devices)) {
1569  goto end;
1570  }
1571 
1573 
1574  lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1575 
1576  bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry);
1577  }
1578 
1579  bue->next_entry = 0;
1580  bue->type = c->chunk_item->type;
1581  bue->size = 0;
1582  bue->used = 0;
1583  bue->num_devices = 0;
1584 
1585  if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1586  factor = c->chunk_item->num_stripes;
1587  else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1588  factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1589  else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1590  factor = c->chunk_item->num_stripes - 1;
1591  else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1592  factor = c->chunk_item->num_stripes - 2;
1593  else
1594  factor = 1;
1595 
1596  le2 = le;
1597  while (le2 != &Vcb->chunks) {
1598  chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1599 
1600  if (c2->chunk_item->type == c->chunk_item->type) {
1601  uint16_t i;
1603  uint64_t stripesize;
1604 
1605  bue->size += c2->chunk_item->size;
1606  bue->used += c2->used;
1607 
1608  stripesize = c2->chunk_item->size / factor;
1609 
1610  for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1611  uint64_t j;
1612  bool found = false;
1613 
1614  for (j = 0; j < bue->num_devices; j++) {
1615  if (bue->devices[j].dev_id == cis[i].dev_id) {
1616  bue->devices[j].alloc += stripesize;
1617  found = true;
1618  break;
1619  }
1620  }
1621 
1622  if (!found) {
1623  if (length < sizeof(btrfs_usage_device)) {
1625  goto end;
1626  }
1627 
1628  length -= sizeof(btrfs_usage_device);
1629 
1630  bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1631  bue->devices[bue->num_devices].alloc = stripesize;
1632  bue->num_devices++;
1633  }
1634  }
1635  }
1636 
1637  le2 = le2->Flink;
1638  }
1639 
1640  lastbue = bue;
1641  }
1642 
1643  le = le->Flink;
1644  }
1645 
1647 
1648 end:
1649  ExReleaseResourceLite(&Vcb->chunk_lock);
1650 
1651  return Status;
1652 }
1653 
1655  NTSTATUS Status;
1656  ULONG cc;
1658  bool verify = false;
1659  LIST_ENTRY* le;
1660 
1661  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1662 
1663  le = Vcb->devices.Flink;
1664  while (le != &Vcb->devices) {
1666 
1667  if (dev->devobj && dev->removable) {
1668  Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb);
1669 
1670  if (iosb.Information != sizeof(ULONG))
1671  cc = 0;
1672 
1673  if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1674  dev->devobj->Flags |= DO_VERIFY_VOLUME;
1675  verify = true;
1676  }
1677 
1678  if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1679  dev->change_count = cc;
1680 
1681  if (!NT_SUCCESS(Status) || verify) {
1683  ExReleaseResourceLite(&Vcb->tree_lock);
1684 
1685  return verify ? STATUS_VERIFY_REQUIRED : Status;
1686  }
1687  }
1688 
1689  le = le->Flink;
1690  }
1691 
1692  ExReleaseResourceLite(&Vcb->tree_lock);
1693 
1694  return STATUS_SUCCESS;
1695 }
1696 
1697 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1698  FILESYSTEM_STATISTICS* fss;
1699 
1700  WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1701 
1702  // This is hideously wrong, but at least it stops SMB from breaking
1703 
1704  if (buflen < sizeof(FILESYSTEM_STATISTICS))
1705  return STATUS_BUFFER_TOO_SMALL;
1706 
1707  fss = buffer;
1708  RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1709 
1710  fss->Version = 1;
1713 
1714  *retlen = sizeof(FILESYSTEM_STATISTICS);
1715 
1716  return STATUS_SUCCESS;
1717 }
1718 
1720  FILE_SET_SPARSE_BUFFER* fssb = data;
1721  NTSTATUS Status;
1722  bool set;
1723  fcb* fcb;
1724  ccb* ccb = FileObject->FsContext2;
1725  file_ref* fileref = ccb ? ccb->fileref : NULL;
1726 
1727  if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1728  return STATUS_INVALID_PARAMETER;
1729 
1730  if (!FileObject) {
1731  ERR("FileObject was NULL\n");
1732  return STATUS_INVALID_PARAMETER;
1733  }
1734 
1735  fcb = FileObject->FsContext;
1736 
1737  if (!fcb) {
1738  ERR("FCB was NULL\n");
1739  return STATUS_INVALID_PARAMETER;
1740  }
1741 
1742  if (!ccb) {
1743  ERR("CCB was NULL\n");
1744  return STATUS_INVALID_PARAMETER;
1745  }
1746 
1747  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1748  WARN("insufficient privileges\n");
1749  return STATUS_ACCESS_DENIED;
1750  }
1751 
1752  if (!fileref) {
1753  ERR("no fileref\n");
1754  return STATUS_INVALID_PARAMETER;
1755  }
1756 
1757  if (fcb->ads) {
1758  fileref = fileref->parent;
1759  fcb = fileref->fcb;
1760  }
1761 
1762  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1763  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1764 
1765  if (fcb->type != BTRFS_TYPE_FILE) {
1766  WARN("FileObject did not point to a file\n");
1768  goto end;
1769  }
1770 
1771  if (fssb)
1772  set = fssb->SetSparse;
1773  else
1774  set = true;
1775 
1776  if (set) {
1778  fcb->atts_changed = true;
1779  } else {
1780  ULONG defda;
1781 
1783  fcb->atts_changed = true;
1784 
1786  fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
1787 
1788  fcb->atts_deleted = defda == fcb->atts;
1789  }
1790 
1793 
1795 
1796 end:
1797  ExReleaseResourceLite(fcb->Header.Resource);
1798  ExReleaseResourceLite(&Vcb->tree_lock);
1799 
1800  return Status;
1801 }
1802 
1804  NTSTATUS Status;
1805  bool make_inline, compress;
1806  uint64_t start_data, end_data;
1807  ULONG buf_head;
1808  uint8_t* data;
1809 
1810  make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1811 
1812  if (!make_inline)
1814 
1815  if (make_inline) {
1816  start_data = 0;
1817  end_data = fcb->inode_item.st_size;
1818  buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1819  } else if (compress) {
1822  sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1823  buf_head = 0;
1824  } else {
1825  start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1);
1826  end_data = sector_align(start + length, Vcb->superblock.sector_size);
1827  buf_head = 0;
1828  }
1829 
1830  data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1831  if (!data) {
1832  ERR("out of memory\n");
1834  }
1835 
1836  RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1837 
1838  if (start > start_data || start + length < end_data) {
1839  Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1840 
1841  if (!NT_SUCCESS(Status)) {
1842  ERR("read_file returned %08x\n", Status);
1843  ExFreePool(data);
1844  return Status;
1845  }
1846  }
1847 
1848  RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1849 
1850  if (make_inline) {
1851  uint16_t edsize;
1853 
1854  Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1855  if (!NT_SUCCESS(Status)) {
1856  ERR("excise_extents returned %08x\n", Status);
1857  ExFreePool(data);
1858  return Status;
1859  }
1860 
1861  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data);
1862 
1863  ed->generation = Vcb->superblock.generation;
1864  ed->decoded_size = end_data;
1869 
1870  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback);
1871  if (!NT_SUCCESS(Status)) {
1872  ERR("add_extent_to_fcb returned %08x\n", Status);
1873  ExFreePool(data);
1874  return Status;
1875  }
1876 
1877  ExFreePool(data);
1878 
1879  fcb->inode_item.st_blocks += end_data;
1880  } else if (compress) {
1882 
1883  ExFreePool(data);
1884 
1885  if (!NT_SUCCESS(Status)) {
1886  ERR("write_compressed returned %08x\n", Status);
1887  return Status;
1888  }
1889  } else {
1890  Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback);
1891 
1892  ExFreePool(data);
1893 
1894  if (!NT_SUCCESS(Status)) {
1895  ERR("do_write_file returned %08x\n", Status);
1896  return Status;
1897  }
1898  }
1899 
1900  return STATUS_SUCCESS;
1901 }
1902 
1904  FILE_ZERO_DATA_INFORMATION* fzdi = data;
1905  NTSTATUS Status;
1906  fcb* fcb;
1907  ccb* ccb;
1908  file_ref* fileref;
1909  LIST_ENTRY rollback, *le;
1911  BTRFS_TIME now;
1912  uint64_t start, end;
1913  extent* ext;
1915 
1916  if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1917  return STATUS_INVALID_PARAMETER;
1918 
1919  if (!FileObject) {
1920  ERR("FileObject was NULL\n");
1921  return STATUS_INVALID_PARAMETER;
1922  }
1923 
1924  if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1925  WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1926  return STATUS_INVALID_PARAMETER;
1927  }
1928 
1929  fcb = FileObject->FsContext;
1930 
1931  if (!fcb) {
1932  ERR("FCB was NULL\n");
1933  return STATUS_INVALID_PARAMETER;
1934  }
1935 
1936  ccb = FileObject->FsContext2;
1937 
1938  if (!ccb) {
1939  ERR("ccb was NULL\n");
1940  return STATUS_INVALID_PARAMETER;
1941  }
1942 
1943  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1944  WARN("insufficient privileges\n");
1945  return STATUS_ACCESS_DENIED;
1946  }
1947 
1948  fileref = ccb->fileref;
1949 
1950  if (!fileref) {
1951  ERR("fileref was NULL\n");
1952  return STATUS_INVALID_PARAMETER;
1953  }
1954 
1956 
1957  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1958  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1959 
1960  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
1961 
1962  if (fcb->type != BTRFS_TYPE_FILE) {
1963  WARN("FileObject did not point to a file\n");
1965  goto end;
1966  }
1967 
1968  if (fcb->ads) {
1969  ERR("FileObject is stream\n");
1971  goto end;
1972  }
1973 
1974  if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1976  goto end;
1977  }
1978 
1979  ext = NULL;
1980  le = fcb->extents.Flink;
1981  while (le != &fcb->extents) {
1983 
1984  if (!ext2->ignore) {
1985  ext = ext2;
1986  break;
1987  }
1988 
1989  le = le->Flink;
1990  }
1991 
1992  if (!ext) {
1994  goto end;
1995  }
1996 
1997  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1998  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1999  if (!NT_SUCCESS(Status)) {
2000  ERR("zero_data returned %08x\n", Status);
2001  goto end;
2002  }
2003  } else {
2004  start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
2005 
2006  if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
2007  end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
2008  else
2009  end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
2010 
2011  if (end <= start) {
2012  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2013  if (!NT_SUCCESS(Status)) {
2014  ERR("zero_data returned %08x\n", Status);
2015  goto end;
2016  }
2017  } else {
2018  if (start > (uint64_t)fzdi->FileOffset.QuadPart) {
2019  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
2020  if (!NT_SUCCESS(Status)) {
2021  ERR("zero_data returned %08x\n", Status);
2022  goto end;
2023  }
2024  }
2025 
2026  if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) {
2027  Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2028  if (!NT_SUCCESS(Status)) {
2029  ERR("zero_data returned %08x\n", Status);
2030  goto end;
2031  }
2032  }
2033 
2034  if (end > start) {
2036  if (!NT_SUCCESS(Status)) {
2037  ERR("excise_extents returned %08x\n", Status);
2038  goto end;
2039  }
2040  }
2041  }
2042  }
2043 
2044  CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false);
2045 
2048 
2049  fcb->inode_item.transid = Vcb->superblock.generation;
2050  fcb->inode_item.sequence++;
2051 
2052  if (!ccb->user_set_change_time)
2054 
2055  if (!ccb->user_set_write_time)
2057 
2058  fcb->extents_changed = true;
2059  fcb->inode_item_changed = true;
2061 
2063 
2064  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2065  fcb->subvol->root_item.ctime = now;
2066 
2068 
2069 end:
2070  if (!NT_SUCCESS(Status))
2072  else
2074 
2075  ExReleaseResourceLite(fcb->Header.Resource);
2076  ExReleaseResourceLite(&Vcb->tree_lock);
2077 
2078  return Status;
2079 }
2080 
2081 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2082  NTSTATUS Status;
2083  fcb* fcb;
2084  LIST_ENTRY* le;
2085  FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2086  ULONG i = 0;
2087  uint64_t last_start, last_end;
2088 
2089  TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2090 
2091  if (!FileObject) {
2092  ERR("FileObject was NULL\n");
2093  return STATUS_INVALID_PARAMETER;
2094  }
2095 
2096  if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2097  return STATUS_INVALID_PARAMETER;
2098 
2099  fcb = FileObject->FsContext;
2100 
2101  if (!fcb) {
2102  ERR("FCB was NULL\n");
2103  return STATUS_INVALID_PARAMETER;
2104  }
2105 
2106  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2107 
2108  // If file is not marked as sparse, claim the whole thing as an allocated range
2109 
2110  if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2111  if (fcb->inode_item.st_size == 0)
2113  else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2115  else {
2116  ranges[i].FileOffset.QuadPart = 0;
2117  ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2118  i++;
2120  }
2121 
2122  goto end;
2123 
2124  }
2125 
2126  le = fcb->extents.Flink;
2127 
2128  last_start = 0;
2129  last_end = 0;
2130 
2131  while (le != &fcb->extents) {
2133 
2134  if (!ext->ignore) {
2135  EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2136  uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2137 
2138  if (ext->offset > last_end) { // first extent after a hole
2139  if (last_end > last_start) {
2140  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2141  ranges[i].FileOffset.QuadPart = last_start;
2142  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2143  i++;
2144  } else {
2146  goto end;
2147  }
2148  }
2149 
2150  last_start = ext->offset;
2151  }
2152 
2153  last_end = ext->offset + len;
2154  }
2155 
2156  le = le->Flink;
2157  }
2158 
2159  if (last_end > last_start) {
2160  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2161  ranges[i].FileOffset.QuadPart = last_start;
2162  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2163  i++;
2164  } else {
2166  goto end;
2167  }
2168  }
2169 
2171 
2172 end:
2173  *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2174 
2175  ExReleaseResourceLite(fcb->Header.Resource);
2176 
2177  return Status;
2178 }
2179 
2180 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2181  fcb* fcb;
2182 
2183  TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen);
2184 
2185  if (!FileObject) {
2186  ERR("FileObject was NULL\n");
2187  return STATUS_INVALID_PARAMETER;
2188  }
2189 
2190  if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2191  return STATUS_INVALID_PARAMETER;
2192 
2193  fcb = FileObject->FsContext;
2194 
2195  if (!fcb) {
2196  ERR("FCB was NULL\n");
2197  return STATUS_INVALID_PARAMETER;
2198  }
2199 
2200  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2201 
2202  RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t));
2203  RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
2204 
2205  ExReleaseResourceLite(fcb->Header.Resource);
2206 
2207  RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2208 
2209  *retlen = sizeof(FILE_OBJECTID_BUFFER);
2210 
2211  return STATUS_SUCCESS;
2212 }
2213 
2215  LIST_ENTRY* le;
2216 
2217  le = Vcb->all_fcbs.Flink;
2218  while (le != &Vcb->all_fcbs) {
2219  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2221 
2222  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2223  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2224 
2225  le = le->Flink;
2226  }
2227 }
2228 
2231  NTSTATUS Status;
2232  KIRQL irql;
2233  bool lock_paused_balance = false;
2234 
2235  TRACE("FSCTL_LOCK_VOLUME\n");
2236 
2237  if (Vcb->scrub.thread) {
2238  WARN("cannot lock while scrub running\n");
2239  return STATUS_DEVICE_NOT_READY;
2240  }
2241 
2242  if (Vcb->balance.thread) {
2243  WARN("cannot lock while balance running\n");
2244  return STATUS_DEVICE_NOT_READY;
2245  }
2246 
2247  TRACE("locking volume\n");
2248 
2250 
2251  if (Vcb->locked)
2252  return STATUS_SUCCESS;
2253 
2254  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2255 
2256  if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2258  ExReleaseResourceLite(&Vcb->fileref_lock);
2259  goto end;
2260  }
2261 
2262  ExReleaseResourceLite(&Vcb->fileref_lock);
2263 
2264  if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2265  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2266  KeClearEvent(&Vcb->balance.event);
2267  ExReleaseResourceLite(&Vcb->tree_lock);
2268 
2269  lock_paused_balance = true;
2270  }
2271 
2272  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2273 
2275 
2276  if (Vcb->need_write && !Vcb->readonly)
2277  Status = do_write(Vcb, Irp);
2278  else
2280 
2281  free_trees(Vcb);
2282 
2283  ExReleaseResourceLite(&Vcb->tree_lock);
2284 
2285  if (!NT_SUCCESS(Status)) {
2286  ERR("do_write returned %08x\n", Status);
2287  goto end;
2288  }
2289 
2291 
2292  if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2293  Vcb->Vpb->Flags |= VPB_LOCKED;
2294  Vcb->locked = true;
2295  Vcb->locked_fileobj = IrpSp->FileObject;
2296  Vcb->lock_paused_balance = lock_paused_balance;
2297  } else {
2300 
2301  if (lock_paused_balance)
2302  KeSetEvent(&Vcb->balance.event, 0, false);
2303 
2304  goto end;
2305  }
2306 
2308 
2310 
2311 end:
2312  if (!NT_SUCCESS(Status))
2314 
2315  return Status;
2316 }
2317 
2319  KIRQL irql;
2320 
2322 
2323  Vcb->locked = false;
2324  Vcb->Vpb->Flags &= ~VPB_LOCKED;
2325  Vcb->locked_fileobj = NULL;
2326 
2328 
2329  if (Vcb->lock_paused_balance)
2330  KeSetEvent(&Vcb->balance.event, 0, false);
2331 }
2332 
2335 
2336  TRACE("FSCTL_UNLOCK_VOLUME\n");
2337 
2338  if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2339  return STATUS_NOT_LOCKED;
2340 
2341  TRACE("unlocking volume\n");
2342 
2344 
2346 
2347  return STATUS_SUCCESS;
2348 }
2349 
2352  LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2353  NTSTATUS Status;
2354  HANDLE h;
2355  PFILE_OBJECT fileobj;
2356  PDEVICE_OBJECT devobj;
2357  LIST_ENTRY* le;
2358 
2359  TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2360 
2361  if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2363 
2364 #if defined(_WIN64)
2365  if (IoIs32bitProcess(Irp)) {
2366  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2367  return STATUS_INVALID_PARAMETER;
2368 
2369  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2370  } else {
2371 #endif
2372  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2373  return STATUS_INVALID_PARAMETER;
2374 
2375  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2376 #if defined(_WIN64)
2377  }
2378 #endif
2379 
2380  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2381 
2382  if (!NT_SUCCESS(Status)) {
2383  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2384  return Status;
2385  }
2386 
2387  devobj = fileobj->DeviceObject;
2388 
2390 
2391  le = VcbList.Flink;
2392 
2393  while (le != &VcbList) {
2395 
2396  if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2397  if (Vcb->Vpb == devobj->Vpb) {
2398  KIRQL irql;
2399  PVPB newvpb;
2400  bool free_newvpb = false;
2401 
2402  newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2403  if (!newvpb) {
2404  ERR("out of memory\n");
2406  goto end;
2407  }
2408 
2409  RtlZeroMemory(newvpb, sizeof(VPB));
2410 
2411  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2412 
2413  Vcb->removing = true;
2414 
2415  ExReleaseResourceLite(&Vcb->tree_lock);
2416 
2418 
2419  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2420 
2422 
2423  if (Vcb->need_write && !Vcb->readonly)
2424  Status = do_write(Vcb, Irp);
2425  else
2427 
2428  free_trees(Vcb);
2429 
2430  if (!NT_SUCCESS(Status)) {
2431  ERR("do_write returned %08x\n", Status);
2432  ExReleaseResourceLite(&Vcb->tree_lock);
2433  ExFreePool(newvpb);
2434  goto end;
2435  }
2436 
2438 
2439  ExReleaseResourceLite(&Vcb->tree_lock);
2440 
2442 
2443  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2444  newvpb->Type = IO_TYPE_VPB;
2445  newvpb->Size = sizeof(VPB);
2446  newvpb->RealDevice = devobj;
2447  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2448 
2449  devobj->Vpb = newvpb;
2450  } else
2451  free_newvpb = true;
2452 
2454 
2455  if (free_newvpb)
2456  ExFreePool(newvpb);
2457 
2458  if (Vcb->open_files == 0)
2459  uninit(Vcb);
2460  }
2461 
2462  break;
2463  }
2464 
2465  le = le->Flink;
2466  }
2467 
2469 
2470 end:
2472 
2473  ObDereferenceObject(fileobj);
2474 
2475  return Status;
2476 }
2477 
2480  ULONG* volstate;
2481 
2482  if (Irp->AssociatedIrp.SystemBuffer) {
2483  volstate = Irp->AssociatedIrp.SystemBuffer;
2484  } else if (Irp->MdlAddress != NULL) {
2485  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2486 
2487  if (!volstate)
2489  } else
2491 
2492  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2493  return STATUS_INVALID_PARAMETER;
2494 
2495  *volstate = 0;
2496 
2497  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2498  return STATUS_INVALID_PARAMETER;
2499 
2500  Irp->IoStatus.Information = sizeof(ULONG);
2501 
2502  return STATUS_SUCCESS;
2503 }
2504 
2508 
2509  TRACE("FSCTL_GET_COMPRESSION\n");
2510 
2511  if (Irp->AssociatedIrp.SystemBuffer) {
2512  compression = Irp->AssociatedIrp.SystemBuffer;
2513  } else if (Irp->MdlAddress != NULL) {
2515 
2516  if (!compression)
2518  } else
2520 
2521  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2522  return STATUS_INVALID_PARAMETER;
2523 
2525 
2526  Irp->IoStatus.Information = sizeof(USHORT);
2527 
2528  return STATUS_SUCCESS;
2529 }
2530 
2534 
2535  TRACE("FSCTL_SET_COMPRESSION\n");
2536 
2537  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2538  return STATUS_INVALID_PARAMETER;
2539 
2540  compression = Irp->AssociatedIrp.SystemBuffer;
2541 
2543  return STATUS_INVALID_PARAMETER;
2544 
2545  return STATUS_SUCCESS;
2546 }
2547 
2549  LIST_ENTRY* le;
2550  volume_device_extension* vde = Vcb->vde;
2551  pdo_device_extension* pdode = vde->pdode;
2552 
2553  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2554 
2556 
2557  le = pdode->children.Flink;
2558  while (le != &pdode->children) {
2560 
2561  vc->generation = Vcb->superblock.generation - 1;
2562 
2563  le = le->Flink;
2564  }
2565 
2567 
2568  ExReleaseResourceLite(&Vcb->tree_lock);
2569 }
2570 
2572  NTSTATUS Status;
2573 
2574  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2575 
2576  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2577  return STATUS_SUCCESS;
2578 
2579  if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2580  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2581  return STATUS_ACCESS_DENIED;
2582  }
2583 
2585  if (!NT_SUCCESS(Status)) {
2586  WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
2587  }
2588 
2589  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2590 
2591  if (!Vcb->locked) {
2593 
2594  if (Vcb->need_write && !Vcb->readonly) {
2595  Status = do_write(Vcb, Irp);
2596 
2597  if (!NT_SUCCESS(Status))
2598  ERR("do_write returned %08x\n", Status);
2599  }
2600  }
2601 
2602  free_trees(Vcb);
2603 
2604  Vcb->removing = true;
2605 
2606  if (Vcb->vde) {
2608  Vcb->vde->mounted_device = NULL;
2609  }
2610 
2611  ExReleaseResourceLite(&Vcb->tree_lock);
2612 
2613  return STATUS_SUCCESS;
2614 }
2615 
2617  NTSTATUS Status;
2618  ULONG to_read;
2619  superblock* sb;
2620  uint32_t crc32;
2621  BTRFS_UUID fsuuid, devuuid;
2622  LIST_ENTRY* le;
2623 
2624  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2625 
2627  if (!sb) {
2628  ERR("out of memory\n");
2630  }
2631 
2632  Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2633  if (!NT_SUCCESS(Status)) {
2634  ERR("sync_read_phys returned %08x\n", Status);
2635  ExFreePool(sb);
2636  return Status;
2637  }
2638 
2639  if (sb->magic != BTRFS_MAGIC) {
2640  TRACE("device is not Btrfs\n");
2641  ExFreePool(sb);
2642  return STATUS_SUCCESS;
2643  }
2644 
2645  crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2646 
2647  if (crc32 != *((uint32_t*)sb->checksum)) {
2648  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2649  ExFreePool(sb);
2650  return STATUS_SUCCESS;
2651  }
2652 
2653  fsuuid = sb->uuid;
2654  devuuid = sb->dev_item.device_uuid;
2655 
2656  ExFreePool(sb);
2657 
2659 
2660  le = VcbList.Flink;
2661 
2662  while (le != &VcbList) {
2664 
2665  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2666  LIST_ENTRY* le2;
2667 
2668  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2669 
2670  if (Vcb->superblock.num_devices > 1) {
2671  le2 = Vcb->devices.Flink;
2672  while (le2 != &Vcb->devices) {
2674 
2675  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2676  ExReleaseResourceLite(&Vcb->tree_lock);
2678  return STATUS_DEVICE_NOT_READY;
2679  }
2680 
2681  le2 = le2->Flink;
2682  }
2683  }
2684 
2685  ExReleaseResourceLite(&Vcb->tree_lock);
2687  return STATUS_SUCCESS;
2688  }
2689 
2690  le = le->Flink;
2691  }
2692 
2694 
2695  return STATUS_SUCCESS;
2696 }
2697 
2700  NTSTATUS Status;
2701 
2702  // FIXME - avoid "bootloader area"??
2703 
2704  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2705  dmdsa.Action = DeviceDsmAction_Trim;
2707  dmdsa.ParameterBlockOffset = 0;
2708  dmdsa.ParameterBlockLength = 0;
2709  dmdsa.DataSetRangesOffset = 0;
2710  dmdsa.DataSetRangesLength = 0;
2711 
2713  if (!NT_SUCCESS(Status))
2714  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
2715 }
2716 
2719  NTSTATUS Status;
2720  PFILE_OBJECT fileobj, mountmgrfo;
2722  HANDLE h;
2723  LIST_ENTRY* le;
2724  device* dev;
2725  DEV_ITEM* di;
2726  uint64_t dev_id, size;
2727  uint8_t* mb;
2728  uint64_t* stats;
2729  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2730  volume_child* vc;
2731  PDEVICE_OBJECT mountmgr;
2732  KEY searchkey;
2733  traverse_ptr tp;
2736  pdo_device_extension* pdode;
2737  const GUID* pnp_guid;
2739 
2740  pnp_name.Buffer = NULL;
2741 
2742  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2744 
2745  if (!Vcb->vde) {
2746  WARN("not allowing second device to be added to non-PNP device\n");
2747  return STATUS_NOT_SUPPORTED;
2748  }
2749 
2750  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2752 
2753 #if defined(_WIN64)
2754  if (IoIs32bitProcess(Irp)) {
2755  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2756  return STATUS_INVALID_PARAMETER;
2757 
2758  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2759  } else {
2760 #endif
2761  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2762  return STATUS_INVALID_PARAMETER;
2763 
2764  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2765 #if defined(_WIN64)
2766  }
2767 #endif
2768 
2769  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2770 
2771  if (!NT_SUCCESS(Status)) {
2772  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2773  return Status;
2774  }
2775 
2776  DeviceObject = fileobj->DeviceObject;
2777 
2779  if (!NT_SUCCESS(Status)) {
2780  ERR("get_device_pnp_name returned %08x\n", Status);
2781  ObDereferenceObject(fileobj);
2782  return Status;
2783  }
2784 
2785  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2786  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2787  DeviceObject = DeviceObject->AttachedDevice;
2788 
2790  if (!NT_SUCCESS(Status)) {
2791  ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
2792  ObDereferenceObject(fileobj);
2793  return Status;
2794  }
2795 
2797  if (!NT_SUCCESS(Status)) {
2798  ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
2799  ObDereferenceObject(fileobj);
2800  return Status;
2801  }
2802 
2803  // if disk, check it has no partitions
2804  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2805  ULONG dlisize;
2807 
2808  dlisize = 0;
2809 
2810  do {
2811  dlisize += 1024;
2812 
2813  if (dli)
2814  ExFreePool(dli);
2815 
2816  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2817  if (!dli) {
2818  ERR("out of memory\n");
2820  goto end2;
2821  }
2822 
2824  } while (Status == STATUS_BUFFER_TOO_SMALL);
2825 
2826  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2827  ExFreePool(dli);
2828  ERR("not adding disk which has partitions\n");
2830  goto end2;
2831  }
2832 
2833  ExFreePool(dli);
2834  }
2835 
2837  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2838  if (NT_SUCCESS(Status)) {
2839  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2840  WARN("device was not disk\n");
2841  ObDereferenceObject(fileobj);
2842  return STATUS_INVALID_PARAMETER;
2843  }
2844  } else {
2845  sdn.DeviceNumber = 0xffffffff;
2846  sdn.PartitionNumber = 0xffffffff;
2847  }
2848 
2850  &gli, sizeof(gli), true, NULL);
2851  if (!NT_SUCCESS(Status)) {
2852  ERR("error reading length information: %08x\n", Status);
2853  ObDereferenceObject(fileobj);
2854  return Status;
2855  }
2856 
2857  size = gli.Length.QuadPart;
2858 
2859  if (size < 0x100000) {
2860  ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2861  ObDereferenceObject(fileobj);
2862  return STATUS_INTERNAL_ERROR;
2863  }
2864 
2866 
2867  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2868 
2869  if (Vcb->need_write)
2870  Status = do_write(Vcb, Irp);
2871  else
2873 
2874  free_trees(Vcb);
2875 
2876  if (!NT_SUCCESS(Status)) {
2877  ERR("do_write returned %08x\n", Status);
2878  goto end;
2879  }
2880 
2882  if (!dev) {
2883  ERR("out of memory\n");
2885  goto end;
2886  }
2887 
2888  RtlZeroMemory(dev, sizeof(device));
2889 
2890  dev->devobj = DeviceObject;
2891  dev->fileobj = fileobj;
2892  dev->seeding = false;
2893  init_device(Vcb, dev, true);
2894 
2895  InitializeListHead(&dev->space);
2896 
2897  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2898  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2899  if (!NT_SUCCESS(Status)) {
2900  ERR("add_space_entry returned %08x\n", Status);
2901  goto end;
2902  }
2903  }
2904 
2905  dev_id = 0;
2906 
2907  le = Vcb->devices.Flink;
2908  while (le != &Vcb->devices) {
2909  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2910 
2911  if (dev2->devitem.dev_id > dev_id)
2912  dev_id = dev2->devitem.dev_id;
2913 
2914  le = le->Flink;
2915  }
2916 
2917  dev_id++;
2918 
2919  dev->devitem.dev_id = dev_id;
2920  dev->devitem.num_bytes = size;
2921  dev->devitem.bytes_used = 0;
2922  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2923  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2924  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2925  dev->devitem.type = 0;
2926  dev->devitem.generation = 0;
2927  dev->devitem.start_offset = 0;
2928  dev->devitem.dev_group = 0;
2929  dev->devitem.seek_speed = 0;
2930  dev->devitem.bandwidth = 0;
2931  get_uuid(&dev->devitem.device_uuid);
2932  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2933 
2935  if (!di) {
2936  ERR("out of memory\n");
2937  goto end;
2938  }
2939 
2940  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2941 
2942  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2943  if (!NT_SUCCESS(Status)) {
2944  ERR("insert_tree_item returned %08x\n", Status);
2945  ExFreePool(di);
2946  goto end;
2947  }
2948 
2949  // add stats entry to dev tree
2950  stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2951  if (!stats) {
2952  ERR("out of memory\n");
2954  goto end;
2955  }
2956 
2957  RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2958 
2959  searchkey.obj_id = 0;
2960  searchkey.obj_type = TYPE_DEV_STATS;
2961  searchkey.offset = di->dev_id;
2962 
2963  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2964  if (!NT_SUCCESS(Status)) {
2965  ERR("error - find_item returned %08x\n", Status);
2966  ExFreePool(stats);
2967  goto end;
2968  }
2969 
2970  if (!keycmp(tp.item->key, searchkey)) {
2972  if (!NT_SUCCESS(Status)) {
2973  ERR("delete_tree_item returned %08x\n", Status);
2974  ExFreePool(stats);
2975  goto end;
2976  }
2977  }
2978 
2979  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2980  if (!NT_SUCCESS(Status)) {
2981  ERR("insert_tree_item returned %08x\n", Status);
2982  ExFreePool(stats);
2983  goto end;
2984  }
2985 
2986  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
2988 
2989  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2990  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
2991  if (!mb) {
2992  ERR("out of memory\n");
2994  goto end;
2995  }
2996 
2997  RtlZeroMemory(mb, 0x100000);
2998 
2999  Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3000  if (!NT_SUCCESS(Status)) {
3001  ERR("write_data_phys returned %08x\n", Status);
3002  ExFreePool(mb);
3003  goto end;
3004  }
3005 
3006  ExFreePool(mb);
3007 
3008  vde = Vcb->vde;
3009  pdode = vde->pdode;
3010 
3012  if (!vc) {
3013  ERR("out of memory\n");
3015  goto end;
3016  }
3017 
3018  vc->uuid = dev->devitem.device_uuid;
3019  vc->devid = dev_id;
3020  vc->generation = Vcb->superblock.generation;
3021  vc->devobj = DeviceObject;
3022  vc->fileobj = fileobj;
3023  vc->notification_entry = NULL;
3024 
3026  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3027  if (!NT_SUCCESS(Status))
3028  WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
3029 
3030  pnp_name2 = pnp_name;
3031 
3032  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3033  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3034  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3035  pnp_name2.Length -= 3 * sizeof(WCHAR);
3036  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3037  }
3038 
3039  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3040 
3041  if (pnp_name2.Length == 0)
3042  vc->pnp_name.Buffer = NULL;
3043  else {
3045  if (!vc->pnp_name.Buffer) {
3046  ERR("out of memory\n");
3048  goto end;
3049  }
3050 
3051  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3052  }
3053 
3054  vc->size = size;
3055  vc->seeding = false;
3056  vc->disk_num = sdn.DeviceNumber;
3057  vc->part_num = sdn.PartitionNumber;
3058  vc->had_drive_letter = false;
3059 
3061  InsertTailList(&pdode->children, &vc->list_entry);
3062  pdode->num_children++;
3063  pdode->children_loaded++;
3065 
3067  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3068  if (!NT_SUCCESS(Status))
3069  ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
3070  else {
3071  Status = remove_drive_letter(mountmgr, &pnp_name);
3073  WARN("remove_drive_letter returned %08x\n", Status);
3074 
3076 
3077  ObDereferenceObject(mountmgrfo);
3078  }
3079 
3080  Vcb->superblock.num_devices++;
3081  Vcb->superblock.total_bytes += size;
3082  Vcb->devices_loaded++;
3083  InsertTailList(&Vcb->devices, &dev->list_entry);
3084 
3085  // FIXME - send notification that volume size has increased
3086 
3087  ObReferenceObject(DeviceObject); // for Vcb
3088 
3089  Status = do_write(Vcb, Irp);
3090  if (!NT_SUCCESS(Status))
3091  ERR("do_write returned %08x\n", Status);
3092 
3093  ObReferenceObject(fileobj);
3094 
3095 end:
3096  free_trees(Vcb);
3097 
3098  ExReleaseResourceLite(&Vcb->tree_lock);
3099 
3100 end2:
3101  ObDereferenceObject(fileobj);
3102 
3103  if (pnp_name.Buffer)
3105 
3106  if (NT_SUCCESS(Status))
3108 
3109  return Status;
3110 }
3111 
3113  fcb* fcb;
3114  ccb* ccb;
3115 
3116  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3117 
3118  if (!FileObject)
3119  return STATUS_INVALID_PARAMETER;
3120 
3121  fcb = FileObject->FsContext;
3122  ccb = FileObject->FsContext2;
3123 
3124  if (!fcb)
3125  return STATUS_INVALID_PARAMETER;
3126 
3127  if (fcb != Vcb->volume_fcb)
3128  return STATUS_INVALID_PARAMETER;
3129 
3130  if (!ccb)
3131  return STATUS_INVALID_PARAMETER;
3132 
3133  ccb->allow_extended_dasd_io = true;
3134 
3135  return STATUS_SUCCESS;
3136 }
3137 
3139  if (length < sizeof(BTRFS_UUID))
3140  return STATUS_BUFFER_OVERFLOW;
3141 
3142  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3143 
3144  return STATUS_SUCCESS;
3145 }
3146 
3148  uint64_t devid;
3149  NTSTATUS Status;
3150  LIST_ENTRY* le;
3151 
3152  if (length < sizeof(uint64_t))
3153  return STATUS_INVALID_PARAMETER;
3154 
3155  if (Vcb->readonly)
3157 
3158  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3160 
3161  devid = *((uint64_t*)data);
3162 
3163  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3164 
3165  le = Vcb->devices.Flink;
3166 
3167  while (le != &Vcb->devices) {
3169 
3170  if (dev->devitem.dev_id == devid) {
3171  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3172  dev->stats_changed = true;
3173  Vcb->stats_changed = true;
3174  Vcb->need_write = true;
3176  goto end;
3177  }
3178 
3179  le = le->Flink;
3180  }
3181 
3182  WARN("device %I64x not found\n", devid);
3184 
3185 end:
3186  ExReleaseResourceLite(&Vcb->tree_lock);
3187 
3188  return Status;
3189 }
3190 
3193 
3194  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3195 
3196  // STUB
3197 
3198  if (!FileObject)
3199  return STATUS_INVALID_PARAMETER;
3200 
3202  return STATUS_INVALID_PARAMETER;
3203 
3204  fgiib->ChecksumAlgorithm = 0;
3205  fgiib->Reserved = 0;
3206  fgiib->Flags = 0;
3207  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3208  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3209 
3210  return STATUS_SUCCESS;
3211 }
3212 
3214  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3215 
3216  // STUB
3217 
3218  if (!FileObject)
3219  return STATUS_INVALID_PARAMETER;
3220 
3222  return STATUS_INVALID_PARAMETER;
3223 
3224  return STATUS_SUCCESS;
3225 }
3226 
3228  LIST_ENTRY* le;
3229 
3230  le = fcb->extents.Flink;
3231  while (le != &fcb->extents) {
3233 
3234  if (!ext->ignore)
3235  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3236 
3237  le = le->Flink;
3238  }
3239 
3240  return false;
3241 }
3242 
3245  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3246  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3247  NTSTATUS Status;
3248  PFILE_OBJECT sourcefo;
3249  uint64_t sourcelen, nbytes = 0;
3250  LIST_ENTRY rollback, *le, newexts;
3252  BTRFS_TIME now;
3253  bool make_inline;
3254 
3255  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3256  return STATUS_BUFFER_TOO_SMALL;
3257 
3258  if (Vcb->readonly)
3260 
3261  if (ded->ByteCount.QuadPart == 0)
3262  return STATUS_SUCCESS;
3263 
3264  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3265  return STATUS_INVALID_PARAMETER;
3266 
3268  return STATUS_ACCESS_DENIED;
3269 
3270  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3271  WARN("insufficient privileges\n");
3272  return STATUS_ACCESS_DENIED;
3273  }
3274 
3275  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3276  return STATUS_INVALID_PARAMETER;
3277 
3278  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3279  if (!NT_SUCCESS(Status)) {
3280  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3281  return Status;
3282  }
3283 
3284  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3285  WARN("source and destination are on different volumes\n");
3286  ObDereferenceObject(sourcefo);
3287  return STATUS_INVALID_PARAMETER;
3288  }
3289 
3290  sourcefcb = sourcefo->FsContext;
3291  sourceccb = sourcefo->FsContext2;
3292 
3293  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3294  ObDereferenceObject(sourcefo);
3295  return STATUS_INVALID_PARAMETER;
3296  }
3297 
3298  if (!sourcefcb->ads && !fcb->ads) {
3299  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3300  ObDereferenceObject(sourcefo);
3301  return STATUS_INVALID_PARAMETER;
3302  }
3303 
3304  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3305  ObDereferenceObject(sourcefo);
3306  return STATUS_INVALID_PARAMETER;
3307  }
3308  }
3309 
3310  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3311  WARN("insufficient privileges\n");
3312  ObDereferenceObject(sourcefo);
3313  return STATUS_ACCESS_DENIED;
3314  }
3315 
3316  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3317  ObDereferenceObject(sourcefo);
3318  return STATUS_INVALID_PARAMETER;
3319  }
3320 
3321  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3322 
3323  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3324  ObDereferenceObject(sourcefo);
3325  return STATUS_NOT_SUPPORTED;
3326  }
3327 
3328  if (fcb == sourcefcb &&
3331  WARN("source and destination are the same, and the ranges overlap\n");
3332  ObDereferenceObject(sourcefo);
3333  return STATUS_INVALID_PARAMETER;
3334  }
3335 
3336  // fail if nocsum flag set on one file but not the other
3337  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3338  ObDereferenceObject(sourcefo);
3339  return STATUS_INVALID_PARAMETER;
3340  }
3341 
3343  InitializeListHead(&newexts);
3344 
3345  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3346 
3347  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3348 
3349  if (fcb != sourcefcb)
3350  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3351 
3354  goto end;
3355  }
3356 
3357  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3359  goto end;
3360  }
3361 
3362  make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3363 
3364  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3365  uint8_t* data2;
3366  ULONG bytes_read, dataoff, datalen2;
3367 
3368  if (make_inline) {
3369  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3370  datalen2 = (ULONG)fcb->inode_item.st_size;
3371  } else if (fcb->ads) {
3372  dataoff = 0;
3373  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3374  } else {
3375  dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3376  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3377  }
3378 
3380  if (!data2) {
3381  ERR("out of memory\n");
3383  goto end;
3384  }
3385 
3386  if (dataoff > 0) {
3387  if (make_inline)
3388  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3389  else
3390  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3391 
3392  if (!NT_SUCCESS(Status)) {
3393  ERR("read_file returned %08x\n", Status);
3394  ExFreePool(data2);
3395  goto end;
3396  }
3397  }
3398 
3399  if (sourcefcb->ads) {
3400  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3401  if (!NT_SUCCESS(Status)) {
3402  ERR("read_stream returned %08x\n", Status);
3403  ExFreePool(data2);
3404  goto end;
3405  }
3406  } else {
3407  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3408  if (!NT_SUCCESS(Status)) {
3409  ERR("read_file returned %08x\n", Status);
3410  ExFreePool(data2);
3411  goto end;
3412  }
3413  }
3414 
3415  if (dataoff + bytes_read < datalen2)
3416  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3417 
3418  if (fcb->ads)
3420  else if (make_inline) {
3421  uint16_t edsize;
3422  EXTENT_DATA* ed;
3423 
3424  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3425  if (!NT_SUCCESS(Status)) {
3426  ERR("excise_extents returned %08x\n", Status);
3427  ExFreePool(data2);
3428  goto end;
3429  }
3430 
3431  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3432 
3434  if (!ed) {
3435  ERR("out of memory\n");
3436  ExFreePool(data2);
3438  goto end;
3439  }
3440 
3441  ed->generation = Vcb->superblock.generation;
3447 
3448  RtlCopyMemory(ed->data, data2, datalen2);
3449 
3450  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3451  if (!NT_SUCCESS(Status)) {
3452  ERR("add_extent_to_fcb returned %08x\n", Status);
3453  ExFreePool(data2);
3454  goto end;
3455  }
3456 
3457  fcb->inode_item.st_blocks += datalen2;
3458  } else {
3459  uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3460 
3461  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3462  if (!NT_SUCCESS(Status)) {
3463  ERR("do_write_file returned %08x\n", Status);
3464  ExFreePool(data2);
3465  goto end;
3466  }
3467  }
3468 
3469  ExFreePool(data2);
3470  } else {
3471  LIST_ENTRY* lastextle;
3472 
3473  le = sourcefcb->extents.Flink;
3474  while (le != &sourcefcb->extents) {
3476 
3477  if (!ext->ignore) {
3478  if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3479  break;
3480 
3481  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3482  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3483  extent* ext2;
3484  EXTENT_DATA2 *ed2s, *ed2d;
3485  chunk* c;
3486 
3487  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3488 
3489  if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3490  le = le->Flink;
3491  continue;
3492  }
3493 
3495  if (!ext2) {
3496  ERR("out of memory\n");
3498  goto end;
3499  }
3500 
3501  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3502  ext2->offset = ded->TargetFileOffset.QuadPart;
3503  else
3504  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3505 
3506  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3507  ext2->unique = false;
3508  ext2->ignore = false;
3509  ext2->inserted = true;
3510 
3511  ext2->extent_data.generation = Vcb->superblock.generation;
3512  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3513  ext2->extent_data.compression = ext->extent_data.compression;
3514  ext2->extent_data.encryption = ext->extent_data.encryption;
3515  ext2->extent_data.encoding = ext->extent_data.encoding;
3516  ext2->extent_data.type = ext->extent_data.type;
3517 
3518  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3519 
3520  ed2d->address = ed2s->address;
3521  ed2d->size = ed2s->size;
3522 
3523  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3524  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3525  ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3526  } else {
3527  ed2d->offset = ed2s->offset;
3528  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3529  }
3530 
3531  if (ext->csum) {
3532  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3533  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG);
3534  if (!ext2->csum) {
3535  ERR("out of memory\n");
3537  ExFreePool(ext2);
3538  goto end;
3539  }
3540 
3541  RtlCopyMemory(ext2->csum, &ext->csum[(ed2d->offset - ed2s->offset) / Vcb->superblock.sector_size],
3542  (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size));
3543  } else {
3544  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG);
3545  if (!ext2->csum) {
3546  ERR("out of memory\n");
3548  ExFreePool(ext2);
3549  goto end;
3550  }
3551 
3552  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * sizeof(uint32_t) / Vcb->superblock.sector_size));
3553  }
3554  } else
3555  ext2->csum = NULL;
3556 
3557  InsertTailList(&newexts, &ext2->list_entry);
3558 
3559  c = get_chunk_from_address(Vcb, ed2s->address);
3560  if (!c) {
3561  ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3563  goto end;
3564  }
3565 
3566  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3567  1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3568  if (!NT_SUCCESS(Status)) {
3569  ERR("update_changed_extent_ref returned %08x\n", Status);
3570  goto end;
3571  }
3572 
3573  nbytes += ed2d->num_bytes;
3574  }
3575  }
3576 
3577  le = le->Flink;
3578  }
3579 
3581  if (!NT_SUCCESS(Status)) {
3582  ERR("excise_extents returned %08x\n", Status);
3583 
3584  while (!IsListEmpty(&newexts)) {
3586  ExFreePool(ext);
3587  }
3588 
3589  goto end;
3590  }
3591 
3592  // clear unique flags in source fcb
3593  le = sourcefcb->extents.Flink;
3594  while (le != &sourcefcb->extents) {
3596 
3597  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3598  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3599  LIST_ENTRY* le2;
3600 
3601  le2 = newexts.Flink;
3602  while (le2 != &newexts) {
3604 
3605  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3606  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3607 
3608  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3609  ext->unique = false;
3610  break;
3611  }
3612  }
3613 
3614  le2 = le2->Flink;
3615  }
3616  }
3617 
3618  le = le->Flink;
3619  }
3620 
3621  lastextle = &fcb->extents;
3622  while (!IsListEmpty(&newexts)) {
3624 
3625  add_extent(fcb, lastextle, ext);
3626  lastextle = &ext->list_entry;
3627  }
3628  }
3629 
3632 
3633  if (fcb->ads) {
3634  ccb->fileref->parent->fcb->inode_item.sequence++;
3635 
3636  if (!ccb->user_set_change_time)
3637  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3638 
3639  ccb->fileref->parent->fcb->inode_item_changed = true;
3640  mark_fcb_dirty(ccb->fileref->parent->fcb);
3641  } else {
3642  fcb->inode_item.st_blocks += nbytes;
3643  fcb->inode_item.sequence++;
3644 
3645  if (!ccb->user_set_change_time)
3647 
3648  if (!ccb->user_set_write_time) {
3651  }
3652 
3653  fcb->inode_item_changed = true;
3654  fcb->extents_changed = true;
3655  }
3656 
3658 
3659  if (FileObject->SectionObjectPointer->DataSectionObject)
3660  CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3661 
3663 
3664 end:
3665  ObDereferenceObject(sourcefo);
3666 
3667  if (NT_SUCCESS(Status))
3669  else
3671 
3672  if (fcb != sourcefcb)
3673  ExReleaseResourceLite(sourcefcb->Header.Resource);
3674 
3675  ExReleaseResourceLite(fcb->Header.Resource);
3676 
3677  ExReleaseResourceLite(&Vcb->tree_lock);
3678 
3679  return Status;
3680 }
3681 
3683  NTSTATUS Status;
3684  btrfs_mknod* bmn;
3685  fcb *parfcb, *fcb;
3686  ccb* parccb;
3687  file_ref *parfileref, *fileref;
3689  root* subvol;
3690  uint64_t inode;
3691  dir_child* dc;
3693  BTRFS_TIME now;
3694  LIST_ENTRY* lastle;
3695  ANSI_STRING utf8;
3696  ULONG len, i;
3697  SECURITY_SUBJECT_CONTEXT subjcont;
3698  PSID owner;
3699  BOOLEAN defaulted;
3700 
3701  TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
3702 
3703  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3704  return STATUS_INVALID_PARAMETER;
3705 
3706  if (Vcb->readonly)
3708 
3709  parfcb = FileObject->FsContext;
3710 
3711  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3712  WARN("trying to create file in something other than a directory\n");
3713  return STATUS_INVALID_PARAMETER;
3714  }
3715 
3716  if (is_subvol_readonly(parfcb->subvol, Irp))
3717  return STATUS_ACCESS_DENIED;
3718 
3719  parccb = FileObject->FsContext2;
3720  parfileref = parccb->fileref;
3721 
3722  if (!parfileref)
3723  return STATUS_INVALID_PARAMETER;
3724 
3725  if (datalen < sizeof(btrfs_mknod))
3726  return STATUS_INVALID_PARAMETER;
3727 
3728  bmn = (btrfs_mknod*)data;
3729 
3730  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3731  return STATUS_INVALID_PARAMETER;
3732 
3733  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3734  return STATUS_INVALID_PARAMETER;
3735 
3736  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3737  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3738  WARN("insufficient privileges\n");
3739  return STATUS_ACCESS_DENIED;
3740  }
3741 
3742  if (bmn->inode != 0) {
3743  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3745  }
3746 
3747  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3748  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3750  }
3751 
3752  // don't allow files called . or ..
3753  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3755 
3756  Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3757  if (!NT_SUCCESS(Status)) {
3758  ERR("utf16_to_utf8 return %08x\n", Status);
3759  return Status;
3760  }
3761 
3762  if (len == 0) {
3763  ERR("utf16_to_utf8 returned a length of 0\n");
3764  return STATUS_INTERNAL_ERROR;
3765  }
3766 
3767  if (len > 0xffff) {
3768  ERR("len was too long (%x)\n", len);
3769  return STATUS_INVALID_PARAMETER;
3770  }
3771 
3772  utf8.MaximumLength = utf8.Length = (USHORT)len;
3774 
3775  if (!utf8.Buffer) {
3776  ERR("out of memory\n");
3778  }
3779 
3780  Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3781  if (!NT_SUCCESS(Status)) {
3782  ERR("utf16_to_utf8 failed with error %08x\n", Status);
3783  ExFreePool(utf8.Buffer);
3784  return Status;
3785  }
3786 
3787  name.Length = name.MaximumLength = bmn->namelen;
3788  name.Buffer = bmn->name;
3789 
3790  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3792  ERR("find_file_in_dir returned %08x\n", Status);
3793  goto end;
3794  }
3795 
3796  if (NT_SUCCESS(Status)) {
3797  WARN("filename already exists\n");
3799  goto end;
3800  }
3801 
3804 
3806  if (!fcb) {
3807  ERR("out of memory\n");
3809  goto end;
3810  }
3811 
3812  fcb->Vcb = Vcb;
3813 
3814  fcb->inode_item.generation = Vcb->superblock.generation;
3815  fcb->inode_item.transid = Vcb->superblock.generation;
3816  fcb->inode_item.st_size = 0;
3817  fcb->inode_item.st_blocks = 0;
3818  fcb->inode_item.block_group = 0;
3819  fcb->inode_item.st_nlink = 1;
3823 
3824  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3825  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3826  else
3827  fcb->inode_item.st_rdev = 0;
3828 
3829  fcb->inode_item.flags = 0;
3830  fcb->inode_item.sequence = 1;
3834  fcb->inode_item.otime = now;
3835 
3836  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3838  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3840  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3842  else if (bmn->type == BTRFS_TYPE_FIFO)
3844  else if (bmn->type == BTRFS_TYPE_SOCKET)
3846  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3848  else
3850 
3851  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3852  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3853 
3854  // inherit nodatacow flag from parent directory
3855  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3857 
3858  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3860  }
3861 
3862  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3864 
3867 
3868  fcb->inode_item_changed = true;
3869 
3870  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3871  fcb->Header.AllocationSize.QuadPart = 0;
3872  fcb->Header.FileSize.QuadPart = 0;
3873  fcb->Header.ValidDataLength.QuadPart = 0;
3874 
3875  fcb->atts = 0;
3876 
3877  if (bmn->name[0] == '.')
3879 
3880  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3882 
3883  fcb->atts_changed = false;
3884 
3885  InterlockedIncrement(&parfcb->refcount);
3886  fcb->subvol = parfcb->subvol;
3887 
3888  SeCaptureSubjectContext(&subjcont);
3889 
3890  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3892 
3893  if (!NT_SUCCESS(Status)) {
3894  ERR("SeAssignSecurityEx returned %08x\n", Status);
3895  reap_fcb(fcb);
3896  goto end;
3897  }
3898 
3899  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3900  if (!NT_SUCCESS(Status)) {
3901  WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
3902  fcb->sd_dirty = true;
3903  } else {
3904  fcb->inode_item.st_uid = sid_to_uid(owner);
3906  }
3907 
3908  find_gid(fcb, parfcb, &subjcont);
3909 
3910  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3911  acquire_fcb_lock_exclusive(Vcb);
3912 
3913  if (bmn->inode == 0) {
3914  inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3915  lastle = parfcb->subvol->fcbs.Blink;
3916  } else {
3917  if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3918  inode = parfcb->subvol->lastinode = bmn->inode;
3919  lastle = parfcb->subvol->fcbs.Blink;
3920  } else {
3921  LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3922 
3923  lastle = parfcb->subvol->fcbs.Blink;;
3924  while (le != &parfcb->subvol->fcbs) {
3925  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3926 
3927  if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3928  release_fcb_lock(Vcb);
3929  ExReleaseResourceLite(&Vcb->fileref_lock);
3930 
3931  WARN("inode collision\n");
3933  goto end;
3934  } else if (fcb2->inode > bmn->inode) {
3935  lastle = fcb2->list_entry.Blink;
3936  break;
3937  }
3938 
3939  le = le->Flink;
3940  }
3941 
3942  inode = bmn->inode;
3943  }
3944  }
3945 
3946  fcb->inode = inode;
3947  fcb->type = bmn->type;
3948 
3950  if (!fileref) {
3951  release_fcb_lock(Vcb);
3952  ExReleaseResourceLite(&Vcb->fileref_lock);
3953 
3954  ERR("out of memory\n");
3955  reap_fcb(fcb);
3957  goto end;
3958  }
3959 
3960  fileref->fcb = fcb;
3961 
3962  fcb->created = true;
3963  fileref->created = true;
3964 
3965  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3966  fcb->subvol->root_item.ctime = now;
3967 
3968  fileref->parent = parfileref;
3969 
3972 
3973  Status = add_dir_child(fileref->parent->fcb, fcb->inode, false, &utf8, &name, fcb->type, &dc);
3974  if (!NT_SUCCESS(Status))
3975  WARN("add_dir_child returned %08x\n", Status);
3976 
3977  fileref->dc = dc;
3978  dc->fileref = fileref;
3979 
3980  ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
3981  InsertTailList(&parfileref->children, &fileref->list_entry);
3982  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
3983 
3984  increase_fileref_refcount(parfileref);
3985 
3986  if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3988  if (!fcb->hash_ptrs) {
3989  release_fcb_lock(Vcb);
3990  ExReleaseResourceLite(&Vcb->fileref_lock);
3991 
3992  ERR("out of memory\n");
3995  goto