ReactOS  0.4.15-dev-4594-g505ac65
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 "crc32c.h"
21 #include <ntddstor.h>
22 #include <ntdddisk.h>
23 #ifndef __REACTOS__
24 #include <sys/stat.h>
25 #endif
26 
27 #ifndef FSCTL_CSV_CONTROL
28 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
29 #endif
30 
31 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
32 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
33 #endif
34 
35 #define DOTDOT ".."
36 
37 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
38 
39 #define SEF_SACL_AUTO_INHERIT 0x02
40 
41 extern LIST_ENTRY VcbList;
43 extern PDRIVER_OBJECT drvobj;
46 
48 
50  btrfs_get_file_ids* bgfi;
51  fcb* fcb;
52 
53  if (length < sizeof(btrfs_get_file_ids))
55 
56  if (!FileObject)
58 
59  fcb = FileObject->FsContext;
60 
61  if (!fcb)
63 
64  bgfi = data;
65 
66  bgfi->subvol = fcb->subvol->id;
67  bgfi->inode = fcb->inode;
68  bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
69 
70  return STATUS_SUCCESS;
71 }
72 
73 static void get_uuid(BTRFS_UUID* uuid) {
74  LARGE_INTEGER seed;
75  uint8_t i;
76 
78 
79  for (i = 0; i < 16; i+=2) {
80  ULONG rand = RtlRandomEx(&seed.LowPart);
81 
82  uuid->uuid[i] = (rand & 0xff00) >> 8;
83  uuid->uuid[i+1] = rand & 0xff;
84  }
85 }
86 
88  uint8_t* buf;
91  LIST_ENTRY* le;
92  tree t;
93  tree_header* th;
94  chunk* c;
95 
96  buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
97  if (!buf) {
98  ERR("out of memory\n");
100  }
101 
102  wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
103  wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
104 
105  Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, true, buf, NULL, NULL, Irp, 0, false, NormalPagePriority);
106  if (!NT_SUCCESS(Status)) {
107  ERR("read_data returned %08lx\n", Status);
108  goto end;
109  }
110 
111  th = (tree_header*)buf;
112 
113  RtlZeroMemory(&t, sizeof(tree));
114  t.root = subvol;
115  t.header.level = th->level;
116  t.header.tree_id = t.root->id;
117 
119  if (!NT_SUCCESS(Status)) {
120  ERR("get_tree_new_address returned %08lx\n", Status);
121  goto end;
122  }
123 
124  if (!t.has_new_address) {
125  ERR("tree new address not set\n");
127  goto end;
128  }
129 
130  c = get_chunk_from_address(Vcb, t.new_address);
131 
132  if (c)
133  c->used += Vcb->superblock.node_size;
134  else {
135  ERR("could not find chunk for address %I64x\n", t.new_address);
137  goto end;
138  }
139 
140  th->address = t.new_address;
141  th->tree_id = subvol->id;
142  th->generation = Vcb->superblock.generation;
143  th->fs_uuid = Vcb->superblock.metadata_uuid;
144 
145  if (th->level == 0) {
146  uint32_t i;
147  leaf_node* ln = (leaf_node*)&th[1];
148 
149  for (i = 0; i < th->num_items; i++) {
150  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)) {
151  EXTENT_DATA* ed = (EXTENT_DATA*)(((uint8_t*)&th[1]) + ln[i].offset);
152 
153  if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
154  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
155 
156  if (ed2->size != 0) { // not sparse
157  Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp);
158 
159  if (!NT_SUCCESS(Status)) {
160  ERR("increase_extent_refcount_data returned %08lx\n", Status);
161  goto end;
162  }
163  }
164  }
165  }
166  }
167  } else {
168  uint32_t i;
169  internal_node* in = (internal_node*)&th[1];
170 
171  for (i = 0; i < th->num_items; i++) {
172  TREE_BLOCK_REF tbr;
173 
174  tbr.offset = subvol->id;
175 
176  Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp);
177  if (!NT_SUCCESS(Status)) {
178  ERR("increase_extent_refcount returned %08lx\n", Status);
179  goto end;
180  }
181  }
182  }
183 
184  calc_tree_checksum(Vcb, th);
185 
188  wtc.stripes_left = 0;
189 
190  Status = write_data(Vcb, t.new_address, buf, Vcb->superblock.node_size, &wtc, NULL, NULL, false, 0, NormalPagePriority);
191  if (!NT_SUCCESS(Status)) {
192  ERR("write_data returned %08lx\n", Status);
193  goto end;
194  }
195 
196  if (wtc.stripes.Flink != &wtc.stripes) {
197  bool need_wait = false;
198 
199  // launch writes and wait
200  le = wtc.stripes.Flink;
201  while (le != &wtc.stripes) {
203 
204  if (stripe->status != WriteDataStatus_Ignore) {
205  need_wait = true;
207  }
208 
209  le = le->Flink;
210  }
211 
212  if (need_wait)
214 
215  le = wtc.stripes.Flink;
216  while (le != &wtc.stripes) {
218 
219  if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
220  Status = stripe->iosb.Status;
222  break;
223  }
224 
225  le = le->Flink;
226  }
227 
229  buf = NULL;
230  }
231 
232  if (NT_SUCCESS(Status))
233  *newaddr = t.new_address;
234 
235 end:
236 
237  if (buf)
238  ExFreePool(buf);
239 
240  return Status;
241 }
242 
243 void flush_subvol_fcbs(root* subvol) {
244  LIST_ENTRY* le = subvol->fcbs.Flink;
245 
246  if (IsListEmpty(&subvol->fcbs))
247  return;
248 
249  while (le != &subvol->fcbs) {
250  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
252 
253  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
254  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
255 
256  le = le->Flink;
257  }
258 }
259 
262  uint64_t id;
264  root *r, *subvol = subvol_fcb->subvol;
265  KEY searchkey;
267  uint64_t address, *root_num;
269  BTRFS_TIME now;
270  fcb* fcb = parent->FsContext;
271  ccb* ccb = parent->FsContext2;
272  LIST_ENTRY* le;
273  file_ref *fileref, *fr;
274  dir_child* dc = NULL;
275 
276  if (!ccb) {
277  ERR("error - ccb was NULL\n");
278  return STATUS_INTERNAL_ERROR;
279  }
280 
281  if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
282  WARN("insufficient privileges\n");
283  return STATUS_ACCESS_DENIED;
284  }
285 
286  fileref = ccb->fileref;
287 
288  if (fileref->fcb == Vcb->dummy_fcb)
289  return STATUS_ACCESS_DENIED;
290 
291  // flush open files on this subvol
292 
294 
295  // flush metadata
296 
297  if (Vcb->need_write)
298  Status = do_write(Vcb, Irp);
299  else
301 
302  free_trees(Vcb);
303 
304  if (!NT_SUCCESS(Status)) {
305  ERR("do_write returned %08lx\n", Status);
306  return Status;
307  }
308 
310 
311  // create new root
312 
313  id = InterlockedIncrement64(&Vcb->root_root->lastinode);
314  Status = create_root(Vcb, id, &r, true, Vcb->superblock.generation, Irp);
315 
316  if (!NT_SUCCESS(Status)) {
317  ERR("create_root returned %08lx\n", Status);
318  goto end;
319  }
320 
321  r->lastinode = subvol->lastinode;
322 
323  if (!Vcb->uuid_root) {
324  root* uuid_root;
325 
326  TRACE("uuid root doesn't exist, creating it\n");
327 
328  Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
329 
330  if (!NT_SUCCESS(Status)) {
331  ERR("create_root returned %08lx\n", Status);
332  goto end;
333  }
334 
335  Vcb->uuid_root = uuid_root;
336  }
337 
338  root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
339  if (!root_num) {
340  ERR("out of memory\n");
342  goto end;
343  }
344 
345  tp.tree = NULL;
346 
347  do {
348  get_uuid(&r->root_item.uuid);
349 
350  RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
351  searchkey.obj_type = TYPE_SUBVOL_UUID;
352  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
353 
354  Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
355  } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
356 
357  *root_num = r->id;
358 
359  Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
360  if (!NT_SUCCESS(Status)) {
361  ERR("insert_tree_item returned %08lx\n", Status);
362  ExFreePool(root_num);
363  goto end;
364  }
365 
366  searchkey.obj_id = r->id;
367  searchkey.obj_type = TYPE_ROOT_ITEM;
368  searchkey.offset = 0xffffffffffffffff;
369 
370  Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
371  if (!NT_SUCCESS(Status)) {
372  ERR("error - find_item returned %08lx\n", Status);
373  goto end;
374  }
375 
376  Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, &address, Irp, &rollback);
377  if (!NT_SUCCESS(Status)) {
378  ERR("snapshot_tree_copy returned %08lx\n", Status);
379  goto end;
380  }
381 
384 
385  r->root_item.inode.generation = 1;
386  r->root_item.inode.st_size = 3;
387  r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
388  r->root_item.inode.st_nlink = 1;
389  r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
390  r->root_item.inode.flags = 0x80000000; // FIXME - find out what these mean
391  r->root_item.inode.flags_ro = 0xffffffff; // 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 %08lx\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 %08lx\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 %08lx\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  Status = check_file_name_valid(&nameus, posix, false);
602  if (!NT_SUCCESS(Status))
603  return Status;
604 
605  utf8.Buffer = NULL;
606 
607  Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
608  if (!NT_SUCCESS(Status)) {
609  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
610  return Status;
611  }
612 
613  if (len == 0) {
614  ERR("utf16_to_utf8 returned a length of 0\n");
615  return STATUS_INTERNAL_ERROR;
616  }
617 
618  if (len > 0xffff) {
619  ERR("len was too long\n");
621  }
622 
623  utf8.MaximumLength = utf8.Length = (USHORT)len;
625 
626  if (!utf8.Buffer) {
627  ERR("out of memory\n");
629  }
630 
631  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
632  if (!NT_SUCCESS(Status)) {
633  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
634  goto end2;
635  }
636 
637  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
638 
639  // no need for fcb_lock as we have tree_lock exclusively
640  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || posix, Irp);
641 
642  if (NT_SUCCESS(Status)) {
643  if (!fr2->deleted) {
644  WARN("file already exists\n");
645  free_fileref(fr2);
647  goto end3;
648  } else
649  free_fileref(fr2);
651  ERR("open_fileref returned %08lx\n", Status);
652  goto end3;
653  }
654 
655  Status = ObReferenceObjectByHandle(subvolh, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&subvol_obj, NULL);
656  if (!NT_SUCCESS(Status)) {
657  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
658  goto end3;
659  }
660 
661  if (subvol_obj->DeviceObject != FileObject->DeviceObject) {
663  goto end;
664  }
665 
666  subvol_fcb = subvol_obj->FsContext;
667  if (!subvol_fcb) {
669  goto end;
670  }
671 
672  if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
673  WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
675  goto end;
676  }
677 
678  ccb = subvol_obj->FsContext2;
679 
680  if (!ccb) {
682  goto end;
683  }
684 
685  if (!(ccb->access & FILE_TRAVERSE)) {
686  WARN("insufficient privileges\n");
688  goto end;
689  }
690 
691  if (fcb == Vcb->dummy_fcb) {
693  goto end;
694  }
695 
696  // clear unique flag on extents of open files in subvol
697  if (!IsListEmpty(&subvol_fcb->subvol->fcbs)) {
698  LIST_ENTRY* le = subvol_fcb->subvol->fcbs.Flink;
699 
700  while (le != &subvol_fcb->subvol->fcbs) {
701  struct _fcb* openfcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
702  LIST_ENTRY* le2;
703 
704  le2 = openfcb->extents.Flink;
705 
706  while (le2 != &openfcb->extents) {
708 
709  ext->unique = false;
710 
711  le2 = le2->Flink;
712  }
713 
714  le = le->Flink;
715  }
716  }
717 
718  Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, readonly, Irp);
719 
720  if (NT_SUCCESS(Status)) {
721  file_ref* fr;
722 
723  Status = open_fileref(Vcb, &fr, &nameus, fileref, false, NULL, NULL, PagedPool, false, Irp);
724 
725  if (!NT_SUCCESS(Status)) {
726  ERR("open_fileref returned %08lx\n", Status);
728  } else {
730  free_fileref(fr);
731  }
732  }
733 
734 end:
735  ObDereferenceObject(subvol_obj);
736 
737 end3:
738  ExReleaseResourceLite(&Vcb->tree_lock);
739 
740 end2:
741  ExFreePool(utf8.Buffer);
742 
743  return Status;
744 }
745 
747  btrfs_create_subvol* bcs;
748  fcb *fcb, *rootfcb = NULL;
749  ccb* ccb;
750  file_ref* fileref;
752  uint64_t id;
753  root* r = NULL;
755  BTRFS_TIME now;
756  ULONG len;
757  uint16_t irsize;
758  UNICODE_STRING nameus;
759  ANSI_STRING utf8;
760  INODE_REF* ir;
761  KEY searchkey;
763  SECURITY_SUBJECT_CONTEXT subjcont;
764  PSID owner;
765  BOOLEAN defaulted;
766  uint64_t* root_num;
767  file_ref *fr = NULL, *fr2;
768  dir_child* dc = NULL;
769 
770  fcb = FileObject->FsContext;
771  if (!fcb) {
772  ERR("error - fcb was NULL\n");
773  return STATUS_INTERNAL_ERROR;
774  }
775 
776  ccb = FileObject->FsContext2;
777  if (!ccb) {
778  ERR("error - ccb was NULL\n");
779  return STATUS_INTERNAL_ERROR;
780  }
781 
782  fileref = ccb->fileref;
783 
784  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
785  ERR("parent FCB was not a directory\n");
786  return STATUS_NOT_A_DIRECTORY;
787  }
788 
789  if (!fileref) {
790  ERR("fileref was NULL\n");
792  }
793 
794  if (fileref->deleted || fcb->deleted) {
795  ERR("parent has been deleted\n");
796  return STATUS_FILE_DELETED;
797  }
798 
799  if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
800  WARN("insufficient privileges\n");
801  return STATUS_ACCESS_DENIED;
802  }
803 
804  if (Vcb->readonly)
806 
808  return STATUS_ACCESS_DENIED;
809 
810  if (fcb == Vcb->dummy_fcb)
811  return STATUS_ACCESS_DENIED;
812 
813  if (!data || datalen < sizeof(btrfs_create_subvol))
815 
816  bcs = (btrfs_create_subvol*)data;
817 
818  if (offsetof(btrfs_create_subvol, name[0]) + bcs->namelen > datalen)
820 
821  nameus.Length = nameus.MaximumLength = bcs->namelen;
822  nameus.Buffer = bcs->name;
823 
824  Status = check_file_name_valid(&nameus, bcs->posix, false);
825  if (!NT_SUCCESS(Status))
826  return Status;
827 
828  utf8.Buffer = NULL;
829 
830  Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
831  if (!NT_SUCCESS(Status)) {
832  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
833  return Status;
834  }
835 
836  if (len == 0) {
837  ERR("utf16_to_utf8 returned a length of 0\n");
838  return STATUS_INTERNAL_ERROR;
839  }
840 
841  if (len > 0xffff) {
842  ERR("len was too long\n");
844  }
845 
846  utf8.MaximumLength = utf8.Length = (USHORT)len;
848 
849  if (!utf8.Buffer) {
850  ERR("out of memory\n");
852  }
853 
854  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
855  if (!NT_SUCCESS(Status)) {
856  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
857  goto end2;
858  }
859 
860  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
861 
864 
865  // no need for fcb_lock as we have tree_lock exclusively
866  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || bcs->posix, Irp);
867 
868  if (NT_SUCCESS(Status)) {
869  if (!fr2->deleted) {
870  WARN("file already exists\n");
871  free_fileref(fr2);
873  goto end;
874  } else
875  free_fileref(fr2);
877  ERR("open_fileref returned %08lx\n", Status);
878  goto end;
879  }
880 
881  id = InterlockedIncrement64(&Vcb->root_root->lastinode);
882  Status = create_root(Vcb, id, &r, false, 0, Irp);
883 
884  if (!NT_SUCCESS(Status)) {
885  ERR("create_root returned %08lx\n", Status);
886  goto end;
887  }
888 
889  TRACE("created root %I64x\n", id);
890 
891  if (!Vcb->uuid_root) {
892  root* uuid_root;
893 
894  TRACE("uuid root doesn't exist, creating it\n");
895 
896  Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
897 
898  if (!NT_SUCCESS(Status)) {
899  ERR("create_root returned %08lx\n", Status);
900  goto end;
901  }
902 
903  Vcb->uuid_root = uuid_root;
904  }
905 
906  root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
907  if (!root_num) {
908  ERR("out of memory\n");
910  goto end;
911  }
912 
913  tp.tree = NULL;
914 
915  do {
916  get_uuid(&r->root_item.uuid);
917 
918  RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
919  searchkey.obj_type = TYPE_SUBVOL_UUID;
920  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
921 
922  Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
923  } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
924 
925  *root_num = r->id;
926 
927  Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
928  if (!NT_SUCCESS(Status)) {
929  ERR("insert_tree_item returned %08lx\n", Status);
930  ExFreePool(root_num);
931  goto end;
932  }
933 
934  r->root_item.inode.generation = 1;
935  r->root_item.inode.st_size = 3;
936  r->root_item.inode.st_blocks = Vcb->superblock.node_size;
937  r->root_item.inode.st_nlink = 1;
938  r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
939  r->root_item.inode.flags = 0x80000000; // FIXME - find out what these mean
940  r->root_item.inode.flags_ro = 0xffffffff; // FIXME - find out what these mean
941 
942  if (bcs->readonly)
943  r->root_item.flags |= BTRFS_SUBVOL_READONLY;
944 
945  r->root_item.objid = SUBVOL_ROOT_INODE;
946  r->root_item.bytes_used = Vcb->superblock.node_size;
947  r->root_item.ctransid = Vcb->superblock.generation;
948  r->root_item.otransid = Vcb->superblock.generation;
949  r->root_item.ctime = now;
950  r->root_item.otime = now;
951 
952  // add .. inode to new subvol
953 
954  rootfcb = create_fcb(Vcb, PagedPool);
955  if (!rootfcb) {
956  ERR("out of memory\n");
958  goto end;
959  }
960 
961  rootfcb->Vcb = Vcb;
962 
963  rootfcb->subvol = r;
964  rootfcb->type = BTRFS_TYPE_DIRECTORY;
965  rootfcb->inode = SUBVOL_ROOT_INODE;
966  rootfcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&rootfcb->inode, sizeof(uint64_t)); // FIXME - we can hardcode this
967 
968  rootfcb->inode_item.generation = Vcb->superblock.generation;
969  rootfcb->inode_item.transid = Vcb->superblock.generation;
970  rootfcb->inode_item.st_nlink = 1;
971  rootfcb->inode_item.st_mode = __S_IFDIR | inherit_mode(fileref->fcb, true);
972  rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
973  rootfcb->inode_item.st_gid = GID_NOBODY;
974 
975  rootfcb->atts = get_file_attributes(Vcb, rootfcb->subvol, rootfcb->inode, rootfcb->type, false, true, Irp);
976 
977  if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
978  rootfcb->atts |= FILE_ATTRIBUTE_READONLY;
979 
980  SeCaptureSubjectContext(&subjcont);
981 
982  Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
983 
984  if (!NT_SUCCESS(Status)) {
985  ERR("SeAssignSecurity returned %08lx\n", Status);
986  goto end;
987  }
988 
989  if (!rootfcb->sd) {
990  ERR("SeAssignSecurity returned NULL security descriptor\n");
992  goto end;
993  }
994 
995  Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted);
996  if (!NT_SUCCESS(Status)) {
997  ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
998  rootfcb->inode_item.st_uid = UID_NOBODY;
999  rootfcb->sd_dirty = true;
1000  } else {
1001  rootfcb->inode_item.st_uid = sid_to_uid(owner);
1002  rootfcb->sd_dirty = rootfcb->inode_item.st_uid == UID_NOBODY;
1003  }
1004 
1005  find_gid(rootfcb, fileref->fcb, &subjcont);
1006 
1007  rootfcb->inode_item_changed = true;
1008 
1009  acquire_fcb_lock_exclusive(Vcb);
1010  add_fcb_to_subvol(rootfcb);
1011  InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
1012  r->fcbs_version++;
1013  release_fcb_lock(Vcb);
1014 
1015  rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1016  rootfcb->Header.AllocationSize.QuadPart = 0;
1017  rootfcb->Header.FileSize.QuadPart = 0;
1018  rootfcb->Header.ValidDataLength.QuadPart = 0;
1019 
1020  rootfcb->created = true;
1021 
1022  if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1024 
1025  rootfcb->prop_compression = fileref->fcb->prop_compression;
1027 
1028  r->lastinode = rootfcb->inode;
1029 
1030  // add INODE_REF
1031 
1032  irsize = (uint16_t)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1033  ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1034  if (!ir) {
1035  ERR("out of memory\n");
1037  goto end;
1038  }
1039 
1040  ir->index = 0;
1041  ir->n = sizeof(DOTDOT) - 1;
1042  RtlCopyMemory(ir->name, DOTDOT, ir->n);
1043 
1044  Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1045  if (!NT_SUCCESS(Status)) {
1046  ERR("insert_tree_item returned %08lx\n", Status);
1047  ExFreePool(ir);
1048  goto end;
1049  }
1050 
1051  // create fileref for entry in other subvolume
1052 
1053  fr = create_fileref(Vcb);
1054  if (!fr) {
1055  ERR("out of memory\n");
1056 
1057  reap_fcb(rootfcb);
1058 
1060  goto end;
1061  }
1062 
1063  fr->fcb = rootfcb;
1064 
1065  mark_fcb_dirty(rootfcb);
1066 
1067  fr->parent = fileref;
1068 
1069  Status = add_dir_child(fileref->fcb, r->id, true, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1070  if (!NT_SUCCESS(Status))
1071  WARN("add_dir_child returned %08lx\n", Status);
1072 
1073  fr->dc = dc;
1074  dc->fileref = fr;
1075 
1077  if (!fr->fcb->hash_ptrs) {
1078  ERR("out of memory\n");
1079  free_fileref(fr);
1081  goto end;
1082  }
1083 
1084  RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1085 
1087  if (!fr->fcb->hash_ptrs_uc) {
1088  ERR("out of memory\n");
1089  free_fileref(fr);
1091  goto end;
1092  }
1093 
1094  RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1095 
1096  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
1097  InsertTailList(&fileref->children, &fr->list_entry);
1098  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1099 
1101 
1102  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1103  fr->fcb->fileref = fr;
1104 
1105  fr->created = true;
1106  mark_fileref_dirty(fr);
1107 
1108  // change fcb->subvol's ROOT_ITEM
1109 
1110  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1111  fcb->subvol->root_item.ctime = now;
1112 
1113  // change fcb's INODE_ITEM
1114 
1115  fcb->inode_item.transid = Vcb->superblock.generation;
1116  fcb->inode_item.st_size += utf8.Length * 2;
1117  fcb->inode_item.sequence++;
1118 
1119  if (!ccb->user_set_change_time)
1121 
1122  if (!ccb->user_set_write_time)
1124 
1125  fcb->inode_item_changed = true;
1127 
1128  fr->fcb->subvol->parent = fcb->subvol->id;
1129 
1131 
1132 end:
1133  if (!NT_SUCCESS(Status)) {
1134  if (fr) {
1135  fr->deleted = true;
1136  mark_fileref_dirty(fr);
1137  } else if (rootfcb) {
1138  rootfcb->deleted = true;
1139  mark_fcb_dirty(rootfcb);
1140  }
1141 
1142  if (r) {
1143  RemoveEntryList(&r->list_entry);
1144  InsertTailList(&Vcb->drop_roots, &r->list_entry);
1145  }
1146  }
1147 
1148  ExReleaseResourceLite(&Vcb->tree_lock);
1149 
1150  if (NT_SUCCESS(Status)) {
1153  }
1154 
1155 end2:
1156  if (fr)
1157  free_fileref(fr);
1158 
1159  return Status;
1160 }
1161 
1163  btrfs_inode_info* bii = data;
1164  fcb* fcb;
1165  ccb* ccb;
1166  bool old_style;
1167 
1168  if (length < offsetof(btrfs_inode_info, disk_size_zstd))
1169  return STATUS_BUFFER_OVERFLOW;
1170 
1171  if (!FileObject)
1172  return STATUS_INVALID_PARAMETER;
1173 
1174  fcb = FileObject->FsContext;
1175 
1176  if (!fcb)
1177  return STATUS_INVALID_PARAMETER;
1178 
1179  ccb = FileObject->FsContext2;
1180 
1181  if (!ccb)
1182  return STATUS_INVALID_PARAMETER;
1183 
1184  if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1185  WARN("insufficient privileges\n");
1186  return STATUS_ACCESS_DENIED;
1187  }
1188 
1189  if (fcb->ads)
1190  fcb = ccb->fileref->parent->fcb;
1191 
1192  old_style = length < offsetof(btrfs_inode_info, sparse_size) + sizeof(((btrfs_inode_info*)NULL)->sparse_size);
1193 
1194  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
1195 
1196  bii->subvol = fcb->subvol->id;
1197  bii->inode = fcb->inode;
1198  bii->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
1199  bii->type = fcb->type;
1200  bii->st_uid = fcb->inode_item.st_uid;
1201  bii->st_gid = fcb->inode_item.st_gid;
1202  bii->st_mode = fcb->inode_item.st_mode;
1203 
1204  if (fcb->inode_item.st_rdev == 0)
1205  bii->st_rdev = 0;
1206  else
1207  bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1208 
1209  bii->flags = fcb->inode_item.flags;
1210 
1211  bii->inline_length = 0;
1212  bii->disk_size_uncompressed = 0;
1213  bii->disk_size_zlib = 0;
1214  bii->disk_size_lzo = 0;
1215 
1216  if (!old_style) {
1217  bii->disk_size_zstd = 0;
1218  bii->sparse_size = 0;
1219  }
1220 
1221  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1222  uint64_t last_end = 0;
1223  LIST_ENTRY* le;
1224  bool extents_inline = false;
1225 
1226  le = fcb->extents.Flink;
1227  while (le != &fcb->extents) {
1229 
1230  if (!ext->ignore) {
1231  if (!old_style && ext->offset > last_end)
1232  bii->sparse_size += ext->offset - last_end;
1233 
1234  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1235  bii->inline_length += ext->datalen - (uint16_t)offsetof(EXTENT_DATA, data[0]);
1236  last_end = ext->offset + ext->extent_data.decoded_size;
1237  extents_inline = true;
1238  } else {
1239  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1240 
1241  // FIXME - compressed extents with a hole in them are counted more than once
1242  if (ed2->size != 0) {
1243  switch (ext->extent_data.compression) {
1245  bii->disk_size_uncompressed += ed2->num_bytes;
1246  break;
1247 
1249  bii->disk_size_zlib += ed2->size;
1250  break;
1251 
1252  case BTRFS_COMPRESSION_LZO:
1253  bii->disk_size_lzo += ed2->size;
1254  break;
1255 
1257  if (!old_style)
1258  bii->disk_size_zstd += ed2->size;
1259  break;
1260  }
1261  }
1262 
1263  last_end = ext->offset + ed2->num_bytes;
1264  }
1265  }
1266 
1267  le = le->Flink;
1268  }
1269 
1270  if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1271  bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1272 
1273  if (length >= offsetof(btrfs_inode_info, num_extents) + sizeof(((btrfs_inode_info*)NULL)->num_extents)) {
1274  EXTENT_DATA2* last_ed2 = NULL;
1275 
1276  le = fcb->extents.Flink;
1277 
1278  bii->num_extents = 0;
1279 
1280  while (le != &fcb->extents) {
1282 
1283  if (!ext->ignore && ext->extent_data.type != EXTENT_TYPE_INLINE) {
1284  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1285 
1286  if (ed2->size != 0) {
1287  if (!last_ed2 || ed2->offset != last_ed2->offset + last_ed2->num_bytes)
1288  bii->num_extents++;
1289 
1290  last_ed2 = ed2;
1291  } else
1292  last_ed2 = NULL;
1293  }
1294 
1295  le = le->Flink;
1296  }
1297  }
1298  }
1299 
1300  switch (fcb->prop_compression) {
1301  case PropCompression_Zlib:
1303  break;
1304 
1305  case PropCompression_LZO:
1307  break;
1308 
1309  case PropCompression_ZSTD:
1311  break;
1312 
1313  default:
1315  break;
1316  }
1317 
1318  ExReleaseResourceLite(fcb->Header.Resource);
1319 
1320  return STATUS_SUCCESS;
1321 }
1322 
1324  btrfs_set_inode_info* bsii = data;
1325  NTSTATUS Status;
1326  fcb* fcb;
1327  ccb* ccb;
1328 
1329  if (length < sizeof(btrfs_set_inode_info))
1330  return STATUS_INVALID_PARAMETER;
1331 
1332  if (!FileObject)
1333  return STATUS_INVALID_PARAMETER;
1334 
1335  fcb = FileObject->FsContext;
1336 
1337  if (!fcb)
1338  return STATUS_INVALID_PARAMETER;
1339 
1340  ccb = FileObject->FsContext2;
1341 
1342  if (!ccb)
1343  return STATUS_INVALID_PARAMETER;
1344 
1345  if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1346  WARN("insufficient privileges\n");
1347  return STATUS_ACCESS_DENIED;
1348  }
1349 
1350  if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1351  WARN("insufficient privileges\n");
1352  return STATUS_ACCESS_DENIED;
1353  }
1354 
1356  return STATUS_INVALID_PARAMETER;
1357 
1358  if (fcb->ads)
1359  fcb = ccb->fileref->parent->fcb;
1360 
1361  if (is_subvol_readonly(fcb->subvol, Irp)) {
1362  WARN("trying to change inode on readonly subvolume\n");
1363  return STATUS_ACCESS_DENIED;
1364  }
1365 
1366  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1367 
1368  if (bsii->flags_changed) {
1369  if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1371  WARN("trying to change nocow flag on non-empty file\n");
1373  goto end;
1374  }
1375 
1376  fcb->inode_item.flags = bsii->flags;
1377 
1380  else
1382  }
1383 
1384  if (bsii->mode_changed) {
1386  S_ISGID | S_ISVTX;
1387 
1388  if (ccb->access & WRITE_OWNER)
1389  allowed |= S_ISUID;
1390 
1391  fcb->inode_item.st_mode &= ~allowed;
1392  fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1393  }
1394 
1395  if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1396  fcb->inode_item.st_uid = bsii->st_uid;
1397 
1398  fcb->sd_dirty = true;
1399  fcb->sd_deleted = false;
1400  }
1401 
1402  if (bsii->gid_changed)
1403  fcb->inode_item.st_gid = bsii->st_gid;
1404 
1405  if (bsii->compression_type_changed) {
1406  switch (bsii->compression_type) {
1407  case BTRFS_COMPRESSION_ANY:
1409  break;
1410 
1413  break;
1414 
1415  case BTRFS_COMPRESSION_LZO:
1417  break;
1418 
1421  break;
1422  }
1423 
1424  fcb->prop_compression_changed = true;
1425  }
1426 
1427  if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1428  fcb->inode_item_changed = true;
1430  }
1431 
1433 
1434 end:
1435  ExReleaseResourceLite(fcb->Header.Resource);
1436 
1437  return Status;
1438 }
1439 
1441  btrfs_device* dev = NULL;
1442  NTSTATUS Status;
1443  LIST_ENTRY* le;
1444 
1445  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1446 
1447  le = Vcb->devices.Flink;
1448  while (le != &Vcb->devices) {
1449  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1450  ULONG structlen;
1451 
1452  if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1454  goto end;
1455  }
1456 
1457  if (!dev)
1458  dev = data;
1459  else {
1460  dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1461  dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry);
1462  }
1463 
1464  structlen = length - offsetof(btrfs_device, namelen);
1465 
1466  if (dev2->devobj) {
1467  Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL);
1468  if (!NT_SUCCESS(Status))
1469  goto end;
1470 
1471  dev->missing = false;
1472  } else {
1473  dev->namelen = 0;
1474  dev->missing = true;
1475  }
1476 
1477  dev->next_entry = 0;
1478  dev->dev_id = dev2->devitem.dev_id;
1479  dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false;
1480  dev->device_number = dev2->disk_num;
1481  dev->partition_number = dev2->part_num;
1482  dev->size = dev2->devitem.num_bytes;
1483 
1484  if (dev2->devobj) {
1486 
1487  Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
1488  if (!NT_SUCCESS(Status))
1489  goto end;
1490 
1491  dev->max_size = gli.Length.QuadPart;
1492  } else
1493  dev->max_size = dev->size;
1494 
1495  RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5);
1496 
1497  length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1498 
1499  le = le->Flink;
1500  }
1501 
1502 end:
1503  ExReleaseResourceLite(&Vcb->tree_lock);
1504 
1505  return Status;
1506 }
1507 
1510  btrfs_usage* lastbue = NULL;
1511  NTSTATUS Status;
1512  LIST_ENTRY* le;
1513 
1514  if (length < sizeof(btrfs_usage))
1515  return STATUS_BUFFER_OVERFLOW;
1516 
1517  if (!Vcb->chunk_usage_found) {
1518  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1519 
1520  if (!Vcb->chunk_usage_found)
1522  else
1524 
1525  ExReleaseResourceLite(&Vcb->tree_lock);
1526 
1527  if (!NT_SUCCESS(Status)) {
1528  ERR("find_chunk_usage returned %08lx\n", Status);
1529  return Status;
1530  }
1531  }
1532 
1534 
1535  ExAcquireResourceSharedLite(&Vcb->chunk_lock, true);
1536 
1537  le = Vcb->chunks.Flink;
1538  while (le != &Vcb->chunks) {
1539  bool addnew = false;
1540 
1542 
1543  if (!lastbue) // first entry
1544  addnew = true;
1545  else {
1546  btrfs_usage* bue = usage;
1547 
1548  addnew = true;
1549 
1550  while (true) {
1551  if (bue->type == c->chunk_item->type) {
1552  addnew = false;
1553  break;
1554  }
1555 
1556  if (bue->next_entry == 0)
1557  break;
1558  else
1559  bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry);
1560  }
1561  }
1562 
1563  if (addnew) {
1564  btrfs_usage* bue;
1565  LIST_ENTRY* le2;
1566  uint64_t factor;
1567 
1568  if (!lastbue) {
1569  bue = usage;
1570  } else {
1571  if (length < offsetof(btrfs_usage, devices)) {
1573  goto end;
1574  }
1575 
1577 
1578  lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1579 
1580  bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry);
1581  }
1582 
1583  bue->next_entry = 0;
1584  bue->type = c->chunk_item->type;
1585  bue->size = 0;
1586  bue->used = 0;
1587  bue->num_devices = 0;
1588 
1589  if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1590  factor = c->chunk_item->num_stripes;
1591  else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1592  factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1593  else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1594  factor = c->chunk_item->num_stripes - 1;
1595  else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1596  factor = c->chunk_item->num_stripes - 2;
1597  else
1598  factor = 1;
1599 
1600  le2 = le;
1601  while (le2 != &Vcb->chunks) {
1602  chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1603 
1604  if (c2->chunk_item->type == c->chunk_item->type) {
1605  uint16_t i;
1607  uint64_t stripesize;
1608 
1609  bue->size += c2->chunk_item->size;
1610  bue->used += c2->used;
1611 
1612  stripesize = c2->chunk_item->size / factor;
1613 
1614  for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1615  uint64_t j;
1616  bool found = false;
1617 
1618  for (j = 0; j < bue->num_devices; j++) {
1619  if (bue->devices[j].dev_id == cis[i].dev_id) {
1620  bue->devices[j].alloc += stripesize;
1621  found = true;
1622  break;
1623  }
1624  }
1625 
1626  if (!found) {
1627  if (length < sizeof(btrfs_usage_device)) {
1629  goto end;
1630  }
1631 
1632  length -= sizeof(btrfs_usage_device);
1633 
1634  bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1635  bue->devices[bue->num_devices].alloc = stripesize;
1636  bue->num_devices++;
1637  }
1638  }
1639  }
1640 
1641  le2 = le2->Flink;
1642  }
1643 
1644  lastbue = bue;
1645  }
1646 
1647  le = le->Flink;
1648  }
1649 
1651 
1652 end:
1653  ExReleaseResourceLite(&Vcb->chunk_lock);
1654 
1655  return Status;
1656 }
1657 
1659  NTSTATUS Status;
1660  ULONG cc;
1662  bool verify = false;
1663  LIST_ENTRY* le;
1664 
1665  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1666 
1667  le = Vcb->devices.Flink;
1668  while (le != &Vcb->devices) {
1670 
1671  if (dev->devobj && dev->removable) {
1672  Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb);
1673 
1674  if (iosb.Information != sizeof(ULONG))
1675  cc = 0;
1676 
1677  if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1678  dev->devobj->Flags |= DO_VERIFY_VOLUME;
1679  verify = true;
1680  }
1681 
1682  if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1683  dev->change_count = cc;
1684 
1685  if (!NT_SUCCESS(Status) || verify) {
1687  ExReleaseResourceLite(&Vcb->tree_lock);
1688 
1689  return verify ? STATUS_VERIFY_REQUIRED : Status;
1690  }
1691  }
1692 
1693  le = le->Flink;
1694  }
1695 
1696  ExReleaseResourceLite(&Vcb->tree_lock);
1697 
1698  return STATUS_SUCCESS;
1699 }
1700 
1701 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1702  FILESYSTEM_STATISTICS* fss;
1703 
1704  WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1705 
1706  // This is hideously wrong, but at least it stops SMB from breaking
1707 
1708  if (buflen < sizeof(FILESYSTEM_STATISTICS))
1709  return STATUS_BUFFER_TOO_SMALL;
1710 
1711  fss = buffer;
1712  RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1713 
1714  fss->Version = 1;
1717 
1718  *retlen = sizeof(FILESYSTEM_STATISTICS);
1719 
1720  return STATUS_SUCCESS;
1721 }
1722 
1724  FILE_SET_SPARSE_BUFFER* fssb = data;
1725  NTSTATUS Status;
1726  bool set;
1727  fcb* fcb;
1728  ccb* ccb = FileObject->FsContext2;
1729  file_ref* fileref = ccb ? ccb->fileref : NULL;
1730 
1731  if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1732  return STATUS_INVALID_PARAMETER;
1733 
1734  if (!FileObject) {
1735  ERR("FileObject was NULL\n");
1736  return STATUS_INVALID_PARAMETER;
1737  }
1738 
1739  fcb = FileObject->FsContext;
1740 
1741  if (!fcb) {
1742  ERR("FCB was NULL\n");
1743  return STATUS_INVALID_PARAMETER;
1744  }
1745 
1746  if (!ccb) {
1747  ERR("CCB was NULL\n");
1748  return STATUS_INVALID_PARAMETER;
1749  }
1750 
1751  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1752  WARN("insufficient privileges\n");
1753  return STATUS_ACCESS_DENIED;
1754  }
1755 
1756  if (!fileref) {
1757  ERR("no fileref\n");
1758  return STATUS_INVALID_PARAMETER;
1759  }
1760 
1761  if (fcb->ads) {
1762  fileref = fileref->parent;
1763  fcb = fileref->fcb;
1764  }
1765 
1766  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1767  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1768 
1769  if (fcb->type != BTRFS_TYPE_FILE) {
1770  WARN("FileObject did not point to a file\n");
1772  goto end;
1773  }
1774 
1775  if (fssb)
1776  set = fssb->SetSparse;
1777  else
1778  set = true;
1779 
1780  if (set) {
1782  fcb->atts_changed = true;
1783  } else {
1784  ULONG defda;
1785 
1787  fcb->atts_changed = true;
1788 
1790  fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
1791 
1792  fcb->atts_deleted = defda == fcb->atts;
1793  }
1794 
1797 
1799 
1800 end:
1801  ExReleaseResourceLite(fcb->Header.Resource);
1802  ExReleaseResourceLite(&Vcb->tree_lock);
1803 
1804  return Status;
1805 }
1806 
1808  NTSTATUS Status;
1809  bool make_inline, compress;
1810  uint64_t start_data, end_data;
1811  ULONG buf_head;
1812  uint8_t* data;
1813 
1814  make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1815 
1816  if (!make_inline)
1818 
1819  if (make_inline) {
1820  start_data = 0;
1821  end_data = fcb->inode_item.st_size;
1822  buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1823  } else if (compress) {
1826  sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1827  buf_head = 0;
1828  } else {
1829  start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1);
1830  end_data = sector_align(start + length, Vcb->superblock.sector_size);
1831  buf_head = 0;
1832  }
1833 
1834  data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1835  if (!data) {
1836  ERR("out of memory\n");
1838  }
1839 
1840  RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1841 
1842  if (start > start_data || start + length < end_data) {
1843  Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1844 
1845  if (!NT_SUCCESS(Status)) {
1846  ERR("read_file returned %08lx\n", Status);
1847  ExFreePool(data);
1848  return Status;
1849  }
1850  }
1851 
1852  RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1853 
1854  if (make_inline) {
1855  uint16_t edsize;
1856  EXTENT_DATA* ed = (EXTENT_DATA*)data;
1857 
1858  Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1859  if (!NT_SUCCESS(Status)) {
1860  ERR("excise_extents returned %08lx\n", Status);
1861  ExFreePool(data);
1862  return Status;
1863  }
1864 
1865  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data);
1866 
1867  ed->generation = Vcb->superblock.generation;
1868  ed->decoded_size = end_data;
1872  ed->type = EXTENT_TYPE_INLINE;
1873 
1874  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback);
1875  if (!NT_SUCCESS(Status)) {
1876  ERR("add_extent_to_fcb returned %08lx\n", Status);
1877  ExFreePool(data);
1878  return Status;
1879  }
1880 
1881  ExFreePool(data);
1882 
1883  fcb->inode_item.st_blocks += end_data;
1884  } else if (compress) {
1886 
1887  ExFreePool(data);
1888 
1889  if (!NT_SUCCESS(Status)) {
1890  ERR("write_compressed returned %08lx\n", Status);
1891  return Status;
1892  }
1893  } else {
1894  Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback);
1895 
1896  ExFreePool(data);
1897 
1898  if (!NT_SUCCESS(Status)) {
1899  ERR("do_write_file returned %08lx\n", Status);
1900  return Status;
1901  }
1902  }
1903 
1904  return STATUS_SUCCESS;
1905 }
1906 
1908  FILE_ZERO_DATA_INFORMATION* fzdi = data;
1909  NTSTATUS Status;
1910  fcb* fcb;
1911  ccb* ccb;
1912  file_ref* fileref;
1913  LIST_ENTRY rollback, *le;
1915  BTRFS_TIME now;
1916  uint64_t start, end;
1917  extent* ext;
1919 
1920  if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1921  return STATUS_INVALID_PARAMETER;
1922 
1923  if (!FileObject) {
1924  ERR("FileObject was NULL\n");
1925  return STATUS_INVALID_PARAMETER;
1926  }
1927 
1928  if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1929  WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1930  return STATUS_INVALID_PARAMETER;
1931  }
1932 
1933  fcb = FileObject->FsContext;
1934 
1935  if (!fcb) {
1936  ERR("FCB was NULL\n");
1937  return STATUS_INVALID_PARAMETER;
1938  }
1939 
1940  ccb = FileObject->FsContext2;
1941 
1942  if (!ccb) {
1943  ERR("ccb was NULL\n");
1944  return STATUS_INVALID_PARAMETER;
1945  }
1946 
1947  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1948  WARN("insufficient privileges\n");
1949  return STATUS_ACCESS_DENIED;
1950  }
1951 
1952  fileref = ccb->fileref;
1953 
1954  if (!fileref) {
1955  ERR("fileref was NULL\n");
1956  return STATUS_INVALID_PARAMETER;
1957  }
1958 
1960 
1961  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1962  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1963 
1964  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
1965 
1966  if (fcb->type != BTRFS_TYPE_FILE) {
1967  WARN("FileObject did not point to a file\n");
1969  goto end;
1970  }
1971 
1972  if (fcb->ads) {
1973  ERR("FileObject is stream\n");
1975  goto end;
1976  }
1977 
1978  if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1980  goto end;
1981  }
1982 
1983  ext = NULL;
1984  le = fcb->extents.Flink;
1985  while (le != &fcb->extents) {
1987 
1988  if (!ext2->ignore) {
1989  ext = ext2;
1990  break;
1991  }
1992 
1993  le = le->Flink;
1994  }
1995 
1996  if (!ext) {
1998  goto end;
1999  }
2000 
2001  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
2002  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2003  if (!NT_SUCCESS(Status)) {
2004  ERR("zero_data returned %08lx\n", Status);
2005  goto end;
2006  }
2007  } else {
2008  start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
2009 
2010  if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
2011  end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
2012  else
2013  end = (fzdi->BeyondFinalZero.QuadPart >> Vcb->sector_shift) << Vcb->sector_shift;
2014 
2015  if (end <= start) {
2016  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2017  if (!NT_SUCCESS(Status)) {
2018  ERR("zero_data returned %08lx\n", Status);
2019  goto end;
2020  }
2021  } else {
2022  if (start > (uint64_t)fzdi->FileOffset.QuadPart) {
2023  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
2024  if (!NT_SUCCESS(Status)) {
2025  ERR("zero_data returned %08lx\n", Status);
2026  goto end;
2027  }
2028  }
2029 
2030  if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) {
2031  Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2032  if (!NT_SUCCESS(Status)) {
2033  ERR("zero_data returned %08lx\n", Status);
2034  goto end;
2035  }
2036  }
2037 
2038  if (end > start) {
2040  if (!NT_SUCCESS(Status)) {
2041  ERR("excise_extents returned %08lx\n", Status);
2042  goto end;
2043  }
2044  }
2045  }
2046  }
2047 
2048  CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false);
2049 
2052 
2053  fcb->inode_item.transid = Vcb->superblock.generation;
2054  fcb->inode_item.sequence++;
2055 
2056  if (!ccb->user_set_change_time)
2058 
2059  if (!ccb->user_set_write_time)
2061 
2062  fcb->extents_changed = true;
2063  fcb->inode_item_changed = true;
2065 
2067 
2068  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2069  fcb->subvol->root_item.ctime = now;
2070 
2072 
2073 end:
2074  if (!NT_SUCCESS(Status))
2076  else
2078 
2079  ExReleaseResourceLite(fcb->Header.Resource);
2080  ExReleaseResourceLite(&Vcb->tree_lock);
2081 
2082  return Status;
2083 }
2084 
2085 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2086  NTSTATUS Status;
2087  fcb* fcb;
2088  LIST_ENTRY* le;
2089  FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2090  ULONG i = 0;
2091  uint64_t last_start, last_end;
2092 
2093  TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2094 
2095  if (!FileObject) {
2096  ERR("FileObject was NULL\n");
2097  return STATUS_INVALID_PARAMETER;
2098  }
2099 
2100  if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2101  return STATUS_INVALID_PARAMETER;
2102 
2103  fcb = FileObject->FsContext;
2104 
2105  if (!fcb) {
2106  ERR("FCB was NULL\n");
2107  return STATUS_INVALID_PARAMETER;
2108  }
2109 
2110  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2111 
2112  // If file is not marked as sparse, claim the whole thing as an allocated range
2113 
2114  if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2115  if (fcb->inode_item.st_size == 0)
2117  else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2119  else {
2120  ranges[i].FileOffset.QuadPart = 0;
2121  ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2122  i++;
2124  }
2125 
2126  goto end;
2127 
2128  }
2129 
2130  le = fcb->extents.Flink;
2131 
2132  last_start = 0;
2133  last_end = 0;
2134 
2135  while (le != &fcb->extents) {
2137 
2138  if (!ext->ignore) {
2139  EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2140  uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2141 
2142  if (ext->offset > last_end) { // first extent after a hole
2143  if (last_end > last_start) {
2144  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2145  ranges[i].FileOffset.QuadPart = last_start;
2146  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2147  i++;
2148  } else {
2150  goto end;
2151  }
2152  }
2153 
2154  last_start = ext->offset;
2155  }
2156 
2157  last_end = ext->offset + len;
2158  }
2159 
2160  le = le->Flink;
2161  }
2162 
2163  if (last_end > last_start) {
2164  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2165  ranges[i].FileOffset.QuadPart = last_start;
2166  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2167  i++;
2168  } else {
2170  goto end;
2171  }
2172  }
2173 
2175 
2176 end:
2177  *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2178 
2179  ExReleaseResourceLite(fcb->Header.Resource);
2180 
2181  return Status;
2182 }
2183 
2184 static NTSTATUS get_object_id(PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2185  fcb* fcb;
2186 
2187  TRACE("(%p, %p, %lx, %p)\n", FileObject, buf, buflen, retlen);
2188 
2189  if (!FileObject) {
2190  ERR("FileObject was NULL\n");
2191  return STATUS_INVALID_PARAMETER;
2192  }
2193 
2194  if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2195  return STATUS_INVALID_PARAMETER;
2196 
2197  fcb = FileObject->FsContext;
2198 
2199  if (!fcb) {
2200  ERR("FCB was NULL\n");
2201  return STATUS_INVALID_PARAMETER;
2202  }
2203 
2204  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2205 
2206  RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t));
2207  RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
2208 
2209  ExReleaseResourceLite(fcb->Header.Resource);
2210 
2211  RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2212 
2213  *retlen = sizeof(FILE_OBJECTID_BUFFER);
2214 
2215  return STATUS_SUCCESS;
2216 }
2217 
2219  LIST_ENTRY* le;
2220 
2221  le = Vcb->all_fcbs.Flink;
2222  while (le != &Vcb->all_fcbs) {
2223  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2225 
2226  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2227  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2228 
2229  le = le->Flink;
2230  }
2231 }
2232 
2235  NTSTATUS Status;
2236  KIRQL irql;
2237  bool lock_paused_balance = false;
2238 
2239  TRACE("FSCTL_LOCK_VOLUME\n");
2240 
2241  if (Vcb->scrub.thread) {
2242  WARN("cannot lock while scrub running\n");
2243  return STATUS_DEVICE_NOT_READY;
2244  }
2245 
2246  if (Vcb->balance.thread) {
2247  WARN("cannot lock while balance running\n");
2248  return STATUS_DEVICE_NOT_READY;
2249  }
2250 
2251  TRACE("locking volume\n");
2252 
2254 
2255  if (Vcb->locked)
2256  return STATUS_SUCCESS;
2257 
2258  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2259 
2260  if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2262  ExReleaseResourceLite(&Vcb->fileref_lock);
2263  goto end;
2264  }
2265 
2266  ExReleaseResourceLite(&Vcb->fileref_lock);
2267 
2268  if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2269  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2270  KeClearEvent(&Vcb->balance.event);
2271  ExReleaseResourceLite(&Vcb->tree_lock);
2272 
2273  lock_paused_balance = true;
2274  }
2275 
2276  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2277 
2279 
2280  if (Vcb->need_write && !Vcb->readonly)
2281  Status = do_write(Vcb, Irp);
2282  else
2284 
2285  free_trees(Vcb);
2286 
2287  ExReleaseResourceLite(&Vcb->tree_lock);
2288 
2289  if (!NT_SUCCESS(Status)) {
2290  ERR("do_write returned %08lx\n", Status);
2291  goto end;
2292  }
2293 
2295 
2296  if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2297  Vcb->Vpb->Flags |= VPB_LOCKED;
2298  Vcb->locked = true;
2299  Vcb->locked_fileobj = IrpSp->FileObject;
2300  Vcb->lock_paused_balance = lock_paused_balance;
2301  } else {
2304 
2305  if (lock_paused_balance)
2306  KeSetEvent(&Vcb->balance.event, 0, false);
2307 
2308  goto end;
2309  }
2310 
2312 
2314 
2315 end:
2316  if (!NT_SUCCESS(Status))
2318 
2319  return Status;
2320 }
2321 
2323  KIRQL irql;
2324 
2326 
2327  Vcb->locked = false;
2328  Vcb->Vpb->Flags &= ~VPB_LOCKED;
2329  Vcb->locked_fileobj = NULL;
2330 
2332 
2333  if (Vcb->lock_paused_balance)
2334  KeSetEvent(&Vcb->balance.event, 0, false);
2335 }
2336 
2339 
2340  TRACE("FSCTL_UNLOCK_VOLUME\n");
2341 
2342  if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2343  return STATUS_NOT_LOCKED;
2344 
2345  TRACE("unlocking volume\n");
2346 
2348 
2350 
2351  return STATUS_SUCCESS;
2352 }
2353 
2356  LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2357  NTSTATUS Status;
2358  HANDLE h;
2359  PFILE_OBJECT fileobj;
2360  PDEVICE_OBJECT devobj;
2361  LIST_ENTRY* le;
2362 
2363  TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2364 
2365  if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2367 
2368 #if defined(_WIN64)
2369  if (IoIs32bitProcess(Irp)) {
2370  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2371  return STATUS_INVALID_PARAMETER;
2372 
2373  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2374  } else {
2375 #endif
2376  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2377  return STATUS_INVALID_PARAMETER;
2378 
2379  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2380 #if defined(_WIN64)
2381  }
2382 #endif
2383 
2384  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2385 
2386  if (!NT_SUCCESS(Status)) {
2387  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2388  return Status;
2389  }
2390 
2391  devobj = fileobj->DeviceObject;
2392 
2394 
2395  le = VcbList.Flink;
2396 
2397  while (le != &VcbList) {
2399 
2400  if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2401  if (Vcb->Vpb == devobj->Vpb) {
2402  KIRQL irql;
2403  PVPB newvpb;
2404  bool free_newvpb = false;
2405 
2406  newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2407  if (!newvpb) {
2408  ERR("out of memory\n");
2410  goto end;
2411  }
2412 
2413  RtlZeroMemory(newvpb, sizeof(VPB));
2414 
2415  ObReferenceObject(devobj);
2416 
2417  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2418 
2419  Vcb->removing = true;
2420 
2421  ExReleaseResourceLite(&Vcb->tree_lock);
2422 
2424 
2425  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2426 
2428 
2429  if (Vcb->need_write && !Vcb->readonly)
2430  Status = do_write(Vcb, Irp);
2431  else
2433 
2434  free_trees(Vcb);
2435 
2436  if (!NT_SUCCESS(Status)) {
2437  ERR("do_write returned %08lx\n", Status);
2438  ExReleaseResourceLite(&Vcb->tree_lock);
2439  ExFreePool(newvpb);
2440  ObDereferenceObject(devobj);
2441  goto end;
2442  }
2443 
2445 
2446  ExReleaseResourceLite(&Vcb->tree_lock);
2447 
2449 
2450  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2451  newvpb->Type = IO_TYPE_VPB;
2452  newvpb->Size = sizeof(VPB);
2453  newvpb->RealDevice = devobj;
2454  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2455 
2456  devobj->Vpb = newvpb;
2457  } else
2458  free_newvpb = true;
2459 
2461 
2462  if (free_newvpb)
2463  ExFreePool(newvpb);
2464 
2465  if (Vcb->open_files == 0)
2466  uninit(Vcb);
2467 
2468  ObDereferenceObject(devobj);
2469  }
2470 
2471  break;
2472  }
2473 
2474  le = le->Flink;
2475  }
2476 
2478 
2479 end:
2481 
2482  ObDereferenceObject(fileobj);
2483 
2484  return Status;
2485 }
2486 
2489  ULONG* volstate;
2490 
2491  if (Irp->AssociatedIrp.SystemBuffer) {
2492  volstate = Irp->AssociatedIrp.SystemBuffer;
2493  } else if (Irp->MdlAddress != NULL) {
2494  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2495 
2496  if (!volstate)
2498  } else
2500 
2501  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2502  return STATUS_INVALID_PARAMETER;
2503 
2504  *volstate = 0;
2505 
2506  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2507  return STATUS_INVALID_PARAMETER;
2508 
2509  Irp->IoStatus.Information = sizeof(ULONG);
2510 
2511  return STATUS_SUCCESS;
2512 }
2513 
2517 
2518  TRACE("FSCTL_GET_COMPRESSION\n");
2519 
2520  if (Irp->AssociatedIrp.SystemBuffer) {
2521  compression = Irp->AssociatedIrp.SystemBuffer;
2522  } else if (Irp->MdlAddress != NULL) {
2524 
2525  if (!compression)
2527  } else
2529 
2530  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2531  return STATUS_INVALID_PARAMETER;
2532 
2534 
2535  Irp->IoStatus.Information = sizeof(USHORT);
2536 
2537  return STATUS_SUCCESS;
2538 }
2539 
2543 
2544  TRACE("FSCTL_SET_COMPRESSION\n");
2545 
2546  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2547  return STATUS_INVALID_PARAMETER;
2548 
2549  compression = Irp->AssociatedIrp.SystemBuffer;
2550 
2552  return STATUS_INVALID_PARAMETER;
2553 
2554  return STATUS_SUCCESS;
2555 }
2556 
2558  LIST_ENTRY* le;
2559  volume_device_extension* vde = Vcb->vde;
2560  pdo_device_extension* pdode = vde->pdode;
2561 
2562  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2563 
2565 
2566  le = pdode->children.Flink;
2567  while (le != &pdode->children) {
2569 
2570  vc->generation = Vcb->superblock.generation - 1;
2571 
2572  le = le->Flink;
2573  }
2574 
2576 
2577  ExReleaseResourceLite(&Vcb->tree_lock);
2578 }
2579 
2581  NTSTATUS Status;
2582  bool open_files;
2583 
2584  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2585 
2586  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2587  return STATUS_SUCCESS;
2588 
2589  if (!shutdown) {
2590  if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2591  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2592  return STATUS_ACCESS_DENIED;
2593  }
2594 
2596  if (!NT_SUCCESS(Status)) {
2597  WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status);
2598  }
2599  }
2600 
2601  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2602 
2603  if (!Vcb->locked) {
2605 
2606  if (Vcb->need_write && !Vcb->readonly) {
2607  Status = do_write(Vcb, Irp);
2608 
2609  if (!NT_SUCCESS(Status))
2610  ERR("do_write returned %08lx\n", Status);
2611  }
2612  }
2613 
2614  free_trees(Vcb);
2615 
2616  Vcb->removing = true;
2617 
2618  open_files = Vcb->open_files > 0;
2619 
2620  if (Vcb->vde) {
2622  Vcb->vde->mounted_device = NULL;
2623  }
2624 
2625  ExReleaseResourceLite(&Vcb->tree_lock);
2626 
2627  if (!open_files)
2628  uninit(Vcb);
2629 
2630  return STATUS_SUCCESS;
2631 }
2632 
2634  NTSTATUS Status;
2635  ULONG to_read;
2636  superblock* sb;
2637  BTRFS_UUID fsuuid, devuuid;
2638  LIST_ENTRY* le;
2639 
2640  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2641 
2643  if (!sb) {
2644  ERR("out of memory\n");
2646  }
2647 
2648  Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2649  if (!NT_SUCCESS(Status)) {
2650  ERR("sync_read_phys returned %08lx\n", Status);
2651  ExFreePool(sb);
2652  return Status;
2653  }
2654 
2655  if (sb->magic != BTRFS_MAGIC) {
2656  TRACE("device is not Btrfs\n");
2657  ExFreePool(sb);
2658  return STATUS_SUCCESS;
2659  }
2660 
2661  if (!check_superblock_checksum(sb)) {
2662  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2663  ExFreePool(sb);
2664  return STATUS_SUCCESS;
2665  }
2666 
2667  fsuuid = sb->uuid;
2668  devuuid = sb->dev_item.device_uuid;
2669 
2670  ExFreePool(sb);
2671 
2673 
2674  le = VcbList.Flink;
2675 
2676  while (le != &VcbList) {
2678 
2679  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2680  LIST_ENTRY* le2;
2681 
2682  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2683 
2684  if (Vcb->superblock.num_devices > 1) {
2685  le2 = Vcb->devices.Flink;
2686  while (le2 != &Vcb->devices) {
2688 
2689  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2690  ExReleaseResourceLite(&Vcb->tree_lock);
2692  return STATUS_DEVICE_NOT_READY;
2693  }
2694 
2695  le2 = le2->Flink;
2696  }
2697  }
2698 
2699  ExReleaseResourceLite(&Vcb->tree_lock);
2701  return STATUS_SUCCESS;
2702  }
2703 
2704  le = le->Flink;
2705  }
2706 
2708 
2709  return STATUS_SUCCESS;
2710 }
2711 
2714  NTSTATUS Status;
2715 
2716  // FIXME - avoid "bootloader area"??
2717 
2718  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2719  dmdsa.Action = DeviceDsmAction_Trim;
2721  dmdsa.ParameterBlockOffset = 0;
2722  dmdsa.ParameterBlockLength = 0;
2723  dmdsa.DataSetRangesOffset = 0;
2724  dmdsa.DataSetRangesLength = 0;
2725 
2727  if (!NT_SUCCESS(Status))
2728  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status);
2729 }
2730 
2733  NTSTATUS Status;
2734  PFILE_OBJECT fileobj, mountmgrfo;
2736  HANDLE h;
2737  LIST_ENTRY* le;
2738  device* dev;
2739  DEV_ITEM* di;
2740  uint64_t dev_id, size;
2741  uint8_t* mb;
2742  uint64_t* stats;
2743  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2744  volume_child* vc;
2746  KEY searchkey;
2747  traverse_ptr tp;
2750  pdo_device_extension* pdode;
2751  const GUID* pnp_guid;
2753 
2754  pnp_name.Buffer = NULL;
2755 
2756  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2758 
2759  if (!Vcb->vde) {
2760  WARN("not allowing second device to be added to non-PNP device\n");
2761  return STATUS_NOT_SUPPORTED;
2762  }
2763 
2764  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2766 
2767 #if defined(_WIN64)
2768  if (IoIs32bitProcess(Irp)) {
2769  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2770  return STATUS_INVALID_PARAMETER;
2771 
2772  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2773  } else {
2774 #endif
2775  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2776  return STATUS_INVALID_PARAMETER;
2777 
2778  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2779 #if defined(_WIN64)
2780  }
2781 #endif
2782 
2783  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2784 
2785  if (!NT_SUCCESS(Status)) {
2786  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2787  return Status;
2788  }
2789 
2790  DeviceObject = fileobj->DeviceObject;
2791 
2792  Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid);
2793  if (!NT_SUCCESS(Status)) {
2794  ERR("get_device_pnp_name returned %08lx\n", Status);
2795  ObDereferenceObject(fileobj);
2796  return Status;
2797  }
2798 
2799  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2800  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2801  DeviceObject = DeviceObject->AttachedDevice;
2802 
2804  if (!NT_SUCCESS(Status)) {
2805  ERR("IOCTL_DISK_IS_WRITABLE returned %08lx\n", Status);
2806  ObDereferenceObject(fileobj);
2807  return Status;
2808  }
2809 
2811  if (!NT_SUCCESS(Status)) {
2812  ERR("is_device_part_of_mounted_btrfs_raid returned %08lx\n", Status);
2813  ObDereferenceObject(fileobj);
2814  return Status;
2815  }
2816 
2817  // if disk, check it has no partitions
2818  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2819  ULONG dlisize;
2820  DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
2821 
2822  dlisize = 0;
2823 
2824  do {
2825  dlisize += 1024;
2826 
2827  if (dli)
2828  ExFreePool(dli);
2829 
2830  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2831  if (!dli) {
2832  ERR("out of memory\n");
2834  goto end2;
2835  }
2836 
2838  } while (Status == STATUS_BUFFER_TOO_SMALL);
2839 
2840  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2841  ExFreePool(dli);
2842  ERR("not adding disk which has partitions\n");
2844  goto end2;
2845  }
2846 
2847  ExFreePool(dli);
2848  }
2849 
2851  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2852  if (NT_SUCCESS(Status)) {
2853  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2854  WARN("device was not disk\n");
2855  ObDereferenceObject(fileobj);
2856  return STATUS_INVALID_PARAMETER;
2857  }
2858  } else {
2859  sdn.DeviceNumber = 0xffffffff;
2860  sdn.PartitionNumber = 0xffffffff;
2861  }
2862 
2864  &gli, sizeof(gli), true, NULL);
2865  if (!NT_SUCCESS(Status)) {
2866  ERR("error reading length information: %08lx\n", Status);
2867  ObDereferenceObject(fileobj);
2868  return Status;
2869  }
2870 
2871  size = gli.Length.QuadPart;
2872 
2873  if (size < 0x100000) {
2874  ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2875  ObDereferenceObject(fileobj);
2876  return STATUS_INTERNAL_ERROR;
2877  }
2878 
2879  volume_removal(&pnp_name);
2880 
2881  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2882 
2883  if (Vcb->need_write)
2884  Status = do_write(Vcb, Irp);
2885  else
2887 
2888  free_trees(Vcb);
2889 
2890  if (!NT_SUCCESS(Status)) {
2891  ERR("do_write returned %08lx\n", Status);
2892  goto end;
2893  }
2894 
2896  if (!dev) {
2897  ERR("out of memory\n");
2899  goto end;
2900  }
2901 
2902  RtlZeroMemory(dev, sizeof(device));
2903 
2904  dev->devobj = DeviceObject;
2905  dev->fileobj = fileobj;
2906  dev->seeding = false;
2907  init_device(Vcb, dev, true);
2908 
2909  InitializeListHead(&dev->space);
2910 
2911  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2912  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2913  if (!NT_SUCCESS(Status)) {
2914  ERR("add_space_entry returned %08lx\n", Status);
2915  goto end;
2916  }
2917  }
2918 
2919  dev_id = 0;
2920 
2921  le = Vcb->devices.Flink;
2922  while (le != &Vcb->devices) {
2923  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2924 
2925  if (dev2->devitem.dev_id > dev_id)
2926  dev_id = dev2->devitem.dev_id;
2927 
2928  le = le->Flink;
2929  }
2930 
2931  dev_id++;
2932 
2933  dev->devitem.dev_id = dev_id;
2934  dev->devitem.num_bytes = size;
2935  dev->devitem.bytes_used = 0;
2936  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2937  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2938  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2939  dev->devitem.type = 0;
2940  dev->devitem.generation = 0;
2941  dev->devitem.start_offset = 0;
2942  dev->devitem.dev_group = 0;
2943  dev->devitem.seek_speed = 0;
2944  dev->devitem.bandwidth = 0;
2945  get_uuid(&dev->devitem.device_uuid);
2946  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2947 
2949  if (!di) {
2950  ERR("out of memory\n");
2951  goto end;
2952  }
2953 
2954  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2955 
2956  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2957  if (!NT_SUCCESS(Status)) {
2958  ERR("insert_tree_item returned %08lx\n", Status);
2959  ExFreePool(di);
2960  goto end;
2961  }
2962 
2963  // add stats entry to dev tree
2964  stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2965  if (!stats) {
2966  ERR("out of memory\n");
2968  goto end;
2969  }
2970 
2971  RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2972 
2973  searchkey.obj_id = 0;
2974  searchkey.obj_type = TYPE_DEV_STATS;
2975  searchkey.offset = di->dev_id;
2976 
2977  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2978  if (!NT_SUCCESS(Status)) {
2979  ERR("error - find_item returned %08lx\n", Status);
2980  ExFreePool(stats);
2981  goto end;
2982  }
2983 
2984  if (!keycmp(tp.item->key, searchkey)) {
2986  if (!NT_SUCCESS(Status)) {
2987  ERR("delete_tree_item returned %08lx\n", Status);
2988  ExFreePool(stats);
2989  goto end;
2990  }
2991  }
2992 
2993  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2994  if (!NT_SUCCESS(Status)) {
2995  ERR("insert_tree_item returned %08lx\n", Status);
2996  ExFreePool(stats);
2997  goto end;
2998  }
2999 
3000  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
3002 
3003  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3004  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
3005  if (!mb) {
3006  ERR("out of memory\n");
3008  goto end;
3009  }
3010 
3011  RtlZeroMemory(mb, 0x100000);
3012 
3013  Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3014  if (!NT_SUCCESS(Status)) {
3015  ERR("write_data_phys returned %08lx\n", Status);
3016  ExFreePool(mb);
3017  goto end;
3018  }
3019 
3020  ExFreePool(mb);
3021 
3022  vde = Vcb->vde;
3023  pdode = vde->pdode;
3024 
3026  if (!vc) {
3027  ERR("out of memory\n");
3029  goto end;
3030  }
3031 
3032  vc->uuid = dev->devitem.device_uuid;
3033  vc->devid = dev_id;
3034  vc->generation = Vcb->superblock.generation;
3035  vc->devobj = DeviceObject;
3036  vc->fileobj = fileobj;
3037  vc->notification_entry = NULL;
3038  vc->boot_volume = false;
3039 
3041  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3042  if (!NT_SUCCESS(Status))
3043  WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
3044 
3045  pnp_name2 = pnp_name;
3046 
3047  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3048  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3049  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3050  pnp_name2.Length -= 3 * sizeof(WCHAR);
3051  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3052  }
3053 
3054  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3055 
3056  if (pnp_name2.Length == 0)
3057  vc->pnp_name.Buffer = NULL;
3058  else {
3060  if (!vc->pnp_name.Buffer) {
3061  ERR("out of memory\n");
3063  goto end;
3064  }
3065 
3066  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3067  }
3068 
3069  vc->size = size;
3070  vc->seeding = false;
3071  vc->disk_num = sdn.DeviceNumber;
3072  vc->part_num = sdn.PartitionNumber;
3073  vc->had_drive_letter = false;
3074 
3076  InsertTailList(&pdode->children, &vc->list_entry);
3077  pdode->num_children++;
3078  pdode->children_loaded++;
3080 
3082  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3083  if (!NT_SUCCESS(Status))
3084  ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
3085  else {
3086  Status = remove_drive_letter(mountmgr, &pnp_name);
3088  WARN("remove_drive_letter returned %08lx\n", Status);
3089 
3091 
3092  ObDereferenceObject(mountmgrfo);
3093  }
3094 
3095  Vcb->superblock.num_devices++;
3096  Vcb->superblock.total_bytes += size;
3097  Vcb->devices_loaded++;
3098  InsertTailList(&Vcb->devices, &dev->list_entry);
3099 
3100  // FIXME - send notification that volume size has increased
3101 
3102  ObReferenceObject(DeviceObject); // for Vcb
3103 
3104  Status = do_write(Vcb, Irp);
3105  if (!NT_SUCCESS(Status))
3106  ERR("do_write returned %08lx\n", Status);
3107 
3108  ObReferenceObject(fileobj);
3109 
3110 end:
3111  free_trees(Vcb);
3112 
3113  ExReleaseResourceLite(&Vcb->tree_lock);
3114 
3115 end2:
3116  ObDereferenceObject(fileobj);
3117 
3118  if (pnp_name.Buffer)
3119  ExFreePool(pnp_name.Buffer);
3120 
3121  if (NT_SUCCESS(Status))
3123 
3124  return Status;
3125 }
3126 
3128  fcb* fcb;
3129  ccb* ccb;
3130 
3131  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3132 
3133  if (!FileObject)
3134  return STATUS_INVALID_PARAMETER;
3135 
3136  fcb = FileObject->FsContext;
3137  ccb = FileObject->FsContext2;
3138 
3139  if (!fcb)
3140  return STATUS_INVALID_PARAMETER;
3141 
3142  if (fcb != Vcb->volume_fcb)
3143  return STATUS_INVALID_PARAMETER;
3144 
3145  if (!ccb)
3146  return STATUS_INVALID_PARAMETER;
3147 
3148  ccb->allow_extended_dasd_io = true;
3149 
3150  return STATUS_SUCCESS;
3151 }
3152 
3154  if (length < sizeof(BTRFS_UUID))
3155  return STATUS_BUFFER_OVERFLOW;
3156 
3157  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3158 
3159  return STATUS_SUCCESS;
3160 }
3161 
3163  uint64_t devid;
3164  NTSTATUS Status;
3165  LIST_ENTRY* le;
3166 
3167  if (length < sizeof(uint64_t))
3168  return STATUS_INVALID_PARAMETER;
3169 
3170  if (Vcb->readonly)
3172 
3173  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3175 
3176  devid = *((uint64_t*)data);
3177 
3178  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3179 
3180  le = Vcb->devices.Flink;
3181 
3182  while (le != &Vcb->devices) {
3184 
3185  if (dev->devitem.dev_id == devid) {
3186  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3187  dev->stats_changed = true;
3188  Vcb->stats_changed = true;
3189  Vcb->need_write = true;
3191  goto end;
3192  }
3193 
3194  le = le->Flink;
3195  }
3196 
3197  WARN("device %I64x not found\n", devid);
3199 
3200 end:
3201  ExReleaseResourceLite(&Vcb->tree_lock);
3202 
3203  return Status;
3204 }
3205 
3208 
3209  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3210 
3211  // STUB
3212 
3213  if (!FileObject)
3214  return STATUS_INVALID_PARAMETER;
3215 
3217  return STATUS_INVALID_PARAMETER;
3218 
3219  fgiib->ChecksumAlgorithm = 0;
3220  fgiib->Reserved = 0;
3221  fgiib->Flags = 0;
3222  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3223  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3224 
3225  return STATUS_SUCCESS;
3226 }
3227 
3229  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3230 
3231  // STUB
3232 
3233  if (!FileObject)
3234  return STATUS_INVALID_PARAMETER;
3235 
3237  return STATUS_INVALID_PARAMETER;
3238 
3239  return STATUS_SUCCESS;
3240 }
3241 
3243  LIST_ENTRY* le;
3244 
3245  le = fcb->extents.Flink;
3246  while (le != &fcb->extents) {
3248 
3249  if (!ext->ignore)
3250  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3251 
3252  le = le->Flink;
3253  }
3254 
3255  return false;
3256 }
3257 
3260  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3261  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3262  NTSTATUS Status;
3263  PFILE_OBJECT sourcefo;
3264  uint64_t sourcelen, nbytes = 0;
3265  LIST_ENTRY rollback, *le, newexts;
3267  BTRFS_TIME now;
3268  bool make_inline;
3269 
3270  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3271  return STATUS_BUFFER_TOO_SMALL;
3272 
3273  if (Vcb->readonly)
3275 
3276  if (ded->ByteCount.QuadPart == 0)
3277  return STATUS_SUCCESS;
3278 
3279  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3280  return STATUS_INVALID_PARAMETER;
3281 
3283  return STATUS_ACCESS_DENIED;
3284 
3285  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3286  WARN("insufficient privileges\n");
3287  return STATUS_ACCESS_DENIED;
3288  }
3289 
3290  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3291  return STATUS_INVALID_PARAMETER;
3292 
3293  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3294  if (!NT_SUCCESS(Status)) {
3295  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3296  return Status;
3297  }
3298 
3299  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3300  WARN("source and destination are on different volumes\n");
3301  ObDereferenceObject(sourcefo);
3302  return STATUS_INVALID_PARAMETER;
3303  }
3304 
3305  sourcefcb = sourcefo->FsContext;
3306  sourceccb = sourcefo->FsContext2;
3307 
3308  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3309  ObDereferenceObject(sourcefo);
3310  return STATUS_INVALID_PARAMETER;
3311  }
3312 
3313  if (!sourcefcb->ads && !fcb->ads) {
3314  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3315  ObDereferenceObject(sourcefo);
3316  return STATUS_INVALID_PARAMETER;
3317  }
3318 
3319  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3320  ObDereferenceObject(sourcefo);
3321  return STATUS_INVALID_PARAMETER;
3322  }
3323  }
3324 
3325  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3326  WARN("insufficient privileges\n");
3327  ObDereferenceObject(sourcefo);
3328  return STATUS_ACCESS_DENIED;
3329  }
3330 
3331  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3332  ObDereferenceObject(sourcefo);
3333  return STATUS_INVALID_PARAMETER;
3334  }
3335 
3336  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3337 
3338  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3339  ObDereferenceObject(sourcefo);
3340  return STATUS_NOT_SUPPORTED;
3341  }
3342 
3343  if (fcb == sourcefcb &&
3346  WARN("source and destination are the same, and the ranges overlap\n");
3347  ObDereferenceObject(sourcefo);
3348  return STATUS_INVALID_PARAMETER;
3349  }
3350 
3351  // fail if nocsum flag set on one file but not the other
3352  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3353  ObDereferenceObject(sourcefo);
3354  return STATUS_INVALID_PARAMETER;
3355  }
3356 
3358  InitializeListHead(&newexts);
3359 
3360  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3361 
3362  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3363 
3364  if (fcb != sourcefcb)
3365  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3366 
3369  goto end;
3370  }
3371 
3372  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3374  goto end;
3375  }
3376 
3377  make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3378 
3379  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3380  uint8_t* data2;
3381  ULONG bytes_read, dataoff, datalen2;
3382 
3383  if (make_inline) {
3384  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3385  datalen2 = (ULONG)fcb->inode_item.st_size;
3386  } else if (fcb->ads) {
3387  dataoff = 0;
3388  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3389  } else {
3390  dataoff = ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1);
3391  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3392  }
3393 
3395  if (!data2) {
3396  ERR("out of memory\n");
3398  goto end;
3399  }
3400 
3401  if (dataoff > 0) {
3402  if (make_inline)
3403  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3404  else
3405  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3406 
3407  if (!NT_SUCCESS(Status)) {
3408  ERR("read_file returned %08lx\n", Status);
3409  ExFreePool(data2);
3410  goto end;
3411  }
3412  }
3413 
3414  if (sourcefcb->ads) {
3415  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3416  if (!NT_SUCCESS(Status)) {
3417  ERR("read_stream returned %08lx\n", Status);
3418  ExFreePool(data2);
3419  goto end;
3420  }
3421  } else {
3422  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3423  if (!NT_SUCCESS(Status)) {
3424  ERR("read_file returned %08lx\n", Status);
3425  ExFreePool(data2);
3426  goto end;
3427  }
3428  }
3429 
3430  if (dataoff + bytes_read < datalen2)
3431  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3432 
3433  if (fcb->ads)
3435  else if (make_inline) {
3436  uint16_t edsize;
3437  EXTENT_DATA* ed;
3438 
3439  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3440  if (!NT_SUCCESS(Status)) {
3441  ERR("excise_extents returned %08lx\n", Status);
3442  ExFreePool(data2);
3443  goto end;
3444  }
3445 
3446  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3447 
3448  ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3449  if (!ed) {
3450  ERR("out of memory\n");
3451  ExFreePool(data2);
3453  goto end;
3454  }
3455 
3456  ed->generation = Vcb->superblock.generation;
3461  ed->type = EXTENT_TYPE_INLINE;
3462 
3463  RtlCopyMemory(ed->data, data2, datalen2);
3464 
3465  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3466  if (!NT_SUCCESS(Status)) {
3467  ERR("add_extent_to_fcb returned %08lx\n", Status);
3468  ExFreePool(data2);
3469  goto end;
3470  }
3471 
3472  fcb->inode_item.st_blocks += datalen2;
3473  } else {
3474  uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1));
3475 
3476  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3477  if (!NT_SUCCESS(Status)) {
3478  ERR("do_write_file returned %08lx\n", Status);
3479  ExFreePool(data2);
3480  goto end;
3481  }
3482  }
3483 
3484  ExFreePool(data2);
3485  } else {
3486  LIST_ENTRY* lastextle;
3487 
3488  le = sourcefcb->extents.Flink;
3489  while (le != &sourcefcb->extents) {
3491 
3492  if (!ext->ignore) {
3493  if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3494  break;
3495 
3496  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3497  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3498  extent* ext2;
3499  EXTENT_DATA2 *ed2s, *ed2d;
3500  chunk* c;
3501 
3502  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3503 
3504  if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3505  le = le->Flink;
3506  continue;
3507  }
3508 
3510  if (!ext2) {
3511  ERR("out of memory\n");
3513  goto end;
3514  }
3515 
3516  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3517  ext2->offset = ded->TargetFileOffset.QuadPart;
3518  else
3519  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3520 
3521  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3522  ext2->unique = false;
3523  ext2->ignore = false;
3524  ext2->inserted = true;
3525 
3526  ext2->extent_data.generation = Vcb->superblock.generation;
3527  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3528  ext2->extent_data.compression = ext->extent_data.compression;
3529  ext2->extent_data.encryption = ext->extent_data.encryption;
3530  ext2->extent_data.encoding = ext->extent_data.encoding;
3531  ext2->extent_data.type = ext->extent_data.type;
3532 
3533  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3534 
3535  ed2d->address = ed2s->address;
3536  ed2d->size = ed2s->size;
3537 
3538  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3539  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3540  ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3541  } else {
3542  ed2d->offset = ed2s->offset;
3543  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3544  }
3545 
3546  if (ext->csum) {
3547  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3548  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)((ed2d->num_bytes * Vcb->csum_size) >> Vcb->sector_shift), ALLOC_TAG);
3549  if (!ext2->csum) {
3550  ERR("out of memory\n");
3552  ExFreePool(ext2);
3553  goto end;
3554  }
3555 
3556  RtlCopyMemory(ext2->csum, (uint8_t*)ext->csum + (((ed2d->offset - ed2s->offset) * Vcb->csum_size) >> Vcb->sector_shift),
3557  (ULONG)((ed2d->num_bytes * Vcb->csum_size) >> Vcb->sector_shift));
3558  } else {
3559  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)((ed2d->size * Vcb->csum_size) >> Vcb->sector_shift), ALLOC_TAG);
3560  if (!ext2->csum) {
3561  ERR("out of memory\n");
3563  ExFreePool(ext2);
3564  goto end;
3565  }
3566 
3567  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)((ed2s->size * Vcb->csum_size) >> Vcb->sector_shift));
3568  }
3569  } else
3570  ext2->csum = NULL;
3571 
3572  InsertTailList(&newexts, &ext2->list_entry);
3573 
3574  c = get_chunk_from_address(Vcb, ed2s->address);
3575  if (!c) {
3576  ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3578  goto end;
3579  }
3580 
3581  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3582  1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3583  if (!NT_SUCCESS(Status)) {
3584  ERR("update_changed_extent_ref returned %08lx\n", Status);
3585  goto end;
3586  }
3587 
3588  nbytes += ed2d->num_bytes;
3589  }
3590  }
3591 
3592  le = le->Flink;
3593  }
3594 
3596  if (!NT_SUCCESS(Status)) {
3597  ERR("excise_extents returned %08lx\n", Status);
3598 
3599  while (!IsListEmpty(&newexts)) {
3601  ExFreePool(ext);
3602  }
3603 
3604  goto end;
3605  }
3606 
3607  // clear unique flags in source fcb
3608  le = sourcefcb->extents.Flink;
3609  while (le != &sourcefcb->extents) {
3611 
3612  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3613  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3614  LIST_ENTRY* le2;
3615 
3616  le2 = newexts.Flink;
3617  while (le2 != &newexts) {
3619 
3620  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3621  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3622 
3623  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3624  ext->unique = false;
3625  break;
3626  }
3627  }
3628 
3629  le2 = le2->Flink;
3630  }
3631  }
3632 
3633  le = le->Flink;
3634  }
3635 
3636  lastextle = &fcb->extents;
3637  while (!IsListEmpty(&newexts)) {
3639 
3640  add_extent(fcb, lastextle, ext);
3641  lastextle = &ext->list_entry;
3642  }
3643  }
3644 
3647 
3648  if (fcb->ads) {
3649  ccb->fileref->parent->fcb->inode_item.sequence++;
3650 
3651  if (!ccb->user_set_change_time)
3652  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3653 
3654  ccb->fileref->parent->fcb->inode_item_changed = true;
3655  mark_fcb_dirty(ccb->fileref->parent->fcb);
3656  } else {
3657  fcb->inode_item.st_blocks += nbytes;
3658  fcb->inode_item.sequence++;
3659 
3660  if (!ccb->user_set_change_time)
3662 
3663  if (!ccb->user_set_write_time) {
3666  }
3667 
3668  fcb->inode_item_changed = true;
3669  fcb->extents_changed = true;
3670  }
3671 
3673 
3674  if (FileObject->SectionObjectPointer->DataSectionObject)
3675  CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3676 
3678 
3679 end:
3680  ObDereferenceObject(sourcefo);
3681 
3682  if (NT_SUCCESS(Status))
3684  else
3686 
3687  if (fcb != sourcefcb)
3688  ExReleaseResourceLite(sourcefcb->Header.Resource);
3689 
3690  ExReleaseResourceLite(fcb->Header.Resource);
3691 
3692  ExReleaseResourceLite(&Vcb->tree_lock);
3693 
3694  return Status;
3695 }
3696 
3699  KEY searchkey;
3700  traverse_ptr tp;
3701  NTSTATUS Status;
3702  uint8_t c = hash >> 24;
3703 
3704  if (subvol->fcbs_ptrs[c]) {
3705  LIST_ENTRY* le = subvol->fcbs_ptrs[c];
3706 
3707  while (le != &subvol->fcbs) {
3708  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3709 
3710  if (fcb2->inode == inode)
3711  return STATUS_SUCCESS;
3712  else if (fcb2->hash > hash)
3713  break;
3714 
3715  le = le->Flink;
3716  }
3717  }
3718 
3719  searchkey.obj_id = inode;
3720  searchkey.obj_type = TYPE_INODE_ITEM;
3721  searchkey.offset = 0xffffffffffffffff;
3722 
3723  Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
3724  if (!NT_SUCCESS(Status)) {
3725  ERR("find_item returned %08lx\n", Status);
3726  return Status;
3727  }
3728 
3729  if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
3730  return STATUS_SUCCESS;
3731 
3732  return STATUS_NOT_FOUND;
3733 }
3734 
3736  NTSTATUS Status;
3737  btrfs_mknod* bmn;
3738  fcb *parfcb, *fcb;
3739  ccb* parccb;
3740  file_ref *parfileref, *fileref;
3742  root* subvol;
3743  uint64_t inode;
3744  dir_child* dc;
3746  BTRFS_TIME now;
3747  ANSI_STRING utf8;
3748  ULONG len, i;
3749  SECURITY_SUBJECT_CONTEXT subjcont;
3750  PSID owner;
3751  BOOLEAN defaulted;
3752 
3753  TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
3754 
3755  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3756  return STATUS_INVALID_PARAMETER;
3757 
3758  if (Vcb->readonly)
3760 
3761  parfcb = FileObject->FsContext;
3762 
3763  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3764  WARN("trying to create file in something other than a directory\n");
3765  return STATUS_INVALID_PARAMETER;
3766  }
3767 
3768  if (is_subvol_readonly(parfcb->subvol, Irp))
3769  return STATUS_ACCESS_DENIED;
3770 
3771  parccb = FileObject->FsContext2;
3772  parfileref = parccb->fileref;
3773 
3774  if (!parfileref)
3775  return STATUS_INVALID_PARAMETER;
3776 
3777  if (datalen < sizeof(btrfs_mknod))
3778  return STATUS_INVALID_PARAMETER;
3779 
3780  bmn = (btrfs_mknod*)data;
3781 
3782  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3783  return STATUS_INVALID_PARAMETER;
3784 
3785  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3786  return STATUS_INVALID_PARAMETER;
3787 
3788  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3789  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3790  WARN("insufficient privileges\n");
3791  return STATUS_ACCESS_DENIED;
3792  }
3793 
3794  if (bmn->inode != 0) {
3795  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3797  }
3798 
3799  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3800  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3802  }
3803 
3804  // don't allow files called . or ..
3805  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3807 
3808  Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3809  if (!NT_SUCCESS(Status)) {
3810  ERR("utf16_to_utf8 returned %08lx\n", Status);
3811  return Status;
3812  }
3813 
3814  if (len == 0) {
3815  ERR("utf16_to_utf8 returned a length of 0\n");
3816  return STATUS_INTERNAL_ERROR;
3817  }
3818 
3819  if (len > 0xffff) {
3820  ERR("len was too long (%lx)\n", len);
3821  return STATUS_INVALID_PARAMETER;
3822  }
3823 
3824  utf8.MaximumLength = utf8.Length = (USHORT)len;
3826 
3827  if (!utf8.Buffer) {
3828  ERR("out of memory\n");
3830  }
3831 
3832  Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3833  if (!NT_SUCCESS(Status)) {
3834  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
3835  ExFreePool(utf8.Buffer);
3836  return Status;
3837  }
3838 
3839  name.Length = name.MaximumLength = bmn->namelen;
3840  name.Buffer = bmn->name;
3841 
3842  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3844  ERR("find_file_in_dir returned %08lx\n", Status);
3845  goto end;
3846  }
3847 
3848  if (NT_SUCCESS(Status)) {
3849  WARN("filename already exists\n");
3851  goto end;
3852  }
3853 
3856 
3858  if (!fcb) {
3859  ERR("out of memory\n");
3861  goto end;
3862  }
3863 
3864  fcb->Vcb = Vcb;
3865 
3866  fcb->inode_item.generation = Vcb->superblock.generation;
3867  fcb->inode_item.transid = Vcb->superblock.generation;
3868  fcb->inode_item.st_size = 0;
3869  fcb->inode_item.st_blocks = 0;
3870  fcb->inode_item.block_group = 0;
3871  fcb->inode_item.st_nlink = 1;
3875 
3876  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3877  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3878  else
3879  fcb->inode_item.st_rdev = 0;
3880 
3881  fcb->inode_item.flags = 0;
3882  fcb->inode_item.sequence = 1;
3886  fcb->inode_item.otime = now;
3887 
3888  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3890  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3892  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3894  else if (bmn->type == BTRFS_TYPE_FIFO)
3896  else if (bmn->type == BTRFS_TYPE_SOCKET)
3898  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3900  else
3902 
3903  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3904  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3905 
3906  // inherit nodatacow flag from parent directory
3907  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3909 
3910  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3912  }
3913 
3914  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3916 
3919 
3920  fcb->inode_item_changed = true;
3921 
3922  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3923  fcb->Header.AllocationSize.QuadPart = 0;
3924  fcb->Header.FileSize.QuadPart = 0;
3925  fcb->Header.ValidDataLength.QuadPart = 0;
3926 
3927  fcb->atts = 0;
3928 
3929  if (bmn->name[0] == '.')
3931 
3932  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3934 
3935  fcb->atts_changed = false;
3936 
3937  InterlockedIncrement(&parfcb->refcount);
3938  fcb->subvol = parfcb->subvol;
3939 
3940  SeCaptureSubjectContext(&subjcont);
3941 
3942  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3944 
3945  if (!NT_SUCCESS(Status)) {
3946  ERR("SeAssignSecurityEx returned %08lx\n", Status);
3947  reap_fcb(fcb);
3948  goto end;
3949  }
3950 
3951  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3952  if (!NT_SUCCESS(Status)) {
3953  WARN("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
3954  fcb->sd_dirty = true;
3955  } else {
3956  fcb->inode_item.st_uid = sid_to_uid(owner);
3958  }
3959 
3960  find_gid(fcb, parfcb, &subjcont);
3961 
3962  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3963  acquire_fcb_lock_exclusive(Vcb);
3964 
3965  if (bmn->inode == 0) {
3966  fcb->inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3967  fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t));
3968  } else {
3969  if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3970  fcb->inode = parfcb->subvol->lastinode = bmn->inode;
3971  fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t));
3972  } else {
3973  uint32_t hash = calc_crc32c(0xffffffff, (uint8_t*)&bmn->inode, sizeof(uint64_t));
3974 
3976  if (NT_SUCCESS(Status)) { // STATUS_SUCCESS means inode found
3977  release_fcb_lock(Vcb);
3978  ExReleaseResourceLite(&Vcb->fileref_lock);
3979 
3980  WARN("inode collision\n");
3982  goto end;
3983  } else if (Status != STATUS_NOT_FOUND) {
3984  ERR("check_inode_used returned %08lx\n", Status);
3985 
3986  release_fcb_lock(Vcb);
3987  ExReleaseResourceLite(&Vcb->fileref_lock);
3988  goto end;
3989  }
3990 
3991  fcb->inode = bmn->inode;
3992  fcb->hash = hash;
3993  }
3994  }
3995 
3996  fcb->inode = inode;
3997  fcb->type = bmn->type;
3998 
4000  if (!fileref) {
4001  release_fcb_lock(Vcb);
4002  ExReleaseResourceLite(&Vcb->fileref_lock);
4003 
4004  ERR("out of memory\n");
4005  reap_fcb(fcb);
4007  goto end;
4008  }