ReactOS  0.4.15-dev-5122-g72bdbdd
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  // nocow and compression are mutually exclusive
1367  if (bsii->flags_changed && bsii->flags & BTRFS_INODE_NODATACOW && bsii->flags & BTRFS_INODE_COMPRESS)
1368  return STATUS_INVALID_PARAMETER;
1369 
1370  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1371 
1372  if (bsii->flags_changed) {
1373  if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1375  WARN("trying to change nocow flag on non-empty file\n");
1377  goto end;
1378  }
1379 
1380  fcb->inode_item.flags = bsii->flags;
1381 
1384  else
1386  }
1387 
1388  if (bsii->mode_changed) {
1390  S_ISGID | S_ISVTX;
1391 
1392  if (ccb->access & WRITE_OWNER)
1393  allowed |= S_ISUID;
1394 
1395  fcb->inode_item.st_mode &= ~allowed;
1396  fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1397  }
1398 
1399  if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1400  fcb->inode_item.st_uid = bsii->st_uid;
1401 
1402  fcb->sd_dirty = true;
1403  fcb->sd_deleted = false;
1404  }
1405 
1406  if (bsii->gid_changed)
1407  fcb->inode_item.st_gid = bsii->st_gid;
1408 
1409  if (bsii->compression_type_changed) {
1410  switch (bsii->compression_type) {
1411  case BTRFS_COMPRESSION_ANY:
1413  break;
1414 
1417  break;
1418 
1419  case BTRFS_COMPRESSION_LZO:
1421  break;
1422 
1425  break;
1426  }
1427 
1428  fcb->prop_compression_changed = true;
1429  }
1430 
1431  if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1432  fcb->inode_item_changed = true;
1434  }
1435 
1437 
1438 end:
1439  ExReleaseResourceLite(fcb->Header.Resource);
1440 
1441  return Status;
1442 }
1443 
1445  btrfs_device* dev = NULL;
1446  NTSTATUS Status;
1447  LIST_ENTRY* le;
1448 
1449  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1450 
1451  le = Vcb->devices.Flink;
1452  while (le != &Vcb->devices) {
1453  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1454  ULONG structlen;
1455 
1456  if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1458  goto end;
1459  }
1460 
1461  if (!dev)
1462  dev = data;
1463  else {
1464  dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1465  dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry);
1466  }
1467 
1468  structlen = length - offsetof(btrfs_device, namelen);
1469 
1470  if (dev2->devobj) {
1471  Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL);
1472  if (!NT_SUCCESS(Status))
1473  goto end;
1474 
1475  dev->missing = false;
1476  } else {
1477  dev->namelen = 0;
1478  dev->missing = true;
1479  }
1480 
1481  dev->next_entry = 0;
1482  dev->dev_id = dev2->devitem.dev_id;
1483  dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false;
1484  dev->device_number = dev2->disk_num;
1485  dev->partition_number = dev2->part_num;
1486  dev->size = dev2->devitem.num_bytes;
1487 
1488  if (dev2->devobj) {
1490 
1491  Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
1492  if (!NT_SUCCESS(Status))
1493  goto end;
1494 
1495  dev->max_size = gli.Length.QuadPart;
1496  } else
1497  dev->max_size = dev->size;
1498 
1499  RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5);
1500 
1501  length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1502 
1503  le = le->Flink;
1504  }
1505 
1506 end:
1507  ExReleaseResourceLite(&Vcb->tree_lock);
1508 
1509  return Status;
1510 }
1511 
1514  btrfs_usage* lastbue = NULL;
1515  NTSTATUS Status;
1516  LIST_ENTRY* le;
1517 
1518  if (length < sizeof(btrfs_usage))
1519  return STATUS_BUFFER_OVERFLOW;
1520 
1521  if (!Vcb->chunk_usage_found) {
1522  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1523 
1524  if (!Vcb->chunk_usage_found)
1526  else
1528 
1529  ExReleaseResourceLite(&Vcb->tree_lock);
1530 
1531  if (!NT_SUCCESS(Status)) {
1532  ERR("find_chunk_usage returned %08lx\n", Status);
1533  return Status;
1534  }
1535  }
1536 
1538 
1539  ExAcquireResourceSharedLite(&Vcb->chunk_lock, true);
1540 
1541  le = Vcb->chunks.Flink;
1542  while (le != &Vcb->chunks) {
1543  bool addnew = false;
1544 
1546 
1547  if (!lastbue) // first entry
1548  addnew = true;
1549  else {
1550  btrfs_usage* bue = usage;
1551 
1552  addnew = true;
1553 
1554  while (true) {
1555  if (bue->type == c->chunk_item->type) {
1556  addnew = false;
1557  break;
1558  }
1559 
1560  if (bue->next_entry == 0)
1561  break;
1562  else
1563  bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry);
1564  }
1565  }
1566 
1567  if (addnew) {
1568  btrfs_usage* bue;
1569  LIST_ENTRY* le2;
1570  uint64_t factor;
1571 
1572  if (!lastbue) {
1573  bue = usage;
1574  } else {
1575  if (length < offsetof(btrfs_usage, devices)) {
1577  goto end;
1578  }
1579 
1581 
1582  lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1583 
1584  bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry);
1585  }
1586 
1587  bue->next_entry = 0;
1588  bue->type = c->chunk_item->type;
1589  bue->size = 0;
1590  bue->used = 0;
1591  bue->num_devices = 0;
1592 
1593  if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1594  factor = c->chunk_item->num_stripes;
1595  else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1596  factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1597  else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1598  factor = c->chunk_item->num_stripes - 1;
1599  else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1600  factor = c->chunk_item->num_stripes - 2;
1601  else
1602  factor = 1;
1603 
1604  le2 = le;
1605  while (le2 != &Vcb->chunks) {
1606  chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1607 
1608  if (c2->chunk_item->type == c->chunk_item->type) {
1609  uint16_t i;
1611  uint64_t stripesize;
1612 
1613  bue->size += c2->chunk_item->size;
1614  bue->used += c2->used;
1615 
1616  stripesize = c2->chunk_item->size / factor;
1617 
1618  for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1619  uint64_t j;
1620  bool found = false;
1621 
1622  for (j = 0; j < bue->num_devices; j++) {
1623  if (bue->devices[j].dev_id == cis[i].dev_id) {
1624  bue->devices[j].alloc += stripesize;
1625  found = true;
1626  break;
1627  }
1628  }
1629 
1630  if (!found) {
1631  if (length < sizeof(btrfs_usage_device)) {
1633  goto end;
1634  }
1635 
1636  length -= sizeof(btrfs_usage_device);
1637 
1638  bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1639  bue->devices[bue->num_devices].alloc = stripesize;
1640  bue->num_devices++;
1641  }
1642  }
1643  }
1644 
1645  le2 = le2->Flink;
1646  }
1647 
1648  lastbue = bue;
1649  }
1650 
1651  le = le->Flink;
1652  }
1653 
1655 
1656 end:
1657  ExReleaseResourceLite(&Vcb->chunk_lock);
1658 
1659  return Status;
1660 }
1661 
1663  NTSTATUS Status;
1664  ULONG cc;
1666  bool verify = false;
1667  LIST_ENTRY* le;
1668 
1669  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1670 
1671  le = Vcb->devices.Flink;
1672  while (le != &Vcb->devices) {
1674 
1675  if (dev->devobj && dev->removable) {
1676  Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb);
1677 
1678  if (iosb.Information != sizeof(ULONG))
1679  cc = 0;
1680 
1681  if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1682  dev->devobj->Flags |= DO_VERIFY_VOLUME;
1683  verify = true;
1684  }
1685 
1686  if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1687  dev->change_count = cc;
1688 
1689  if (!NT_SUCCESS(Status) || verify) {
1691  ExReleaseResourceLite(&Vcb->tree_lock);
1692 
1693  return verify ? STATUS_VERIFY_REQUIRED : Status;
1694  }
1695  }
1696 
1697  le = le->Flink;
1698  }
1699 
1700  ExReleaseResourceLite(&Vcb->tree_lock);
1701 
1702  return STATUS_SUCCESS;
1703 }
1704 
1705 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1706  FILESYSTEM_STATISTICS* fss;
1707 
1708  WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1709 
1710  // This is hideously wrong, but at least it stops SMB from breaking
1711 
1712  if (buflen < sizeof(FILESYSTEM_STATISTICS))
1713  return STATUS_BUFFER_TOO_SMALL;
1714 
1715  fss = buffer;
1716  RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1717 
1718  fss->Version = 1;
1721 
1722  *retlen = sizeof(FILESYSTEM_STATISTICS);
1723 
1724  return STATUS_SUCCESS;
1725 }
1726 
1728  FILE_SET_SPARSE_BUFFER* fssb = data;
1729  NTSTATUS Status;
1730  bool set;
1731  fcb* fcb;
1732  ccb* ccb = FileObject->FsContext2;
1733  file_ref* fileref = ccb ? ccb->fileref : NULL;
1734 
1735  if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1736  return STATUS_INVALID_PARAMETER;
1737 
1738  if (!FileObject) {
1739  ERR("FileObject was NULL\n");
1740  return STATUS_INVALID_PARAMETER;
1741  }
1742 
1743  fcb = FileObject->FsContext;
1744 
1745  if (!fcb) {
1746  ERR("FCB was NULL\n");
1747  return STATUS_INVALID_PARAMETER;
1748  }
1749 
1750  if (!ccb) {
1751  ERR("CCB was NULL\n");
1752  return STATUS_INVALID_PARAMETER;
1753  }
1754 
1755  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1756  WARN("insufficient privileges\n");
1757  return STATUS_ACCESS_DENIED;
1758  }
1759 
1760  if (!fileref) {
1761  ERR("no fileref\n");
1762  return STATUS_INVALID_PARAMETER;
1763  }
1764 
1765  if (fcb->ads) {
1766  fileref = fileref->parent;
1767  fcb = fileref->fcb;
1768  }
1769 
1770  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1771  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1772 
1773  if (fcb->type != BTRFS_TYPE_FILE) {
1774  WARN("FileObject did not point to a file\n");
1776  goto end;
1777  }
1778 
1779  if (fssb)
1780  set = fssb->SetSparse;
1781  else
1782  set = true;
1783 
1784  if (set) {
1786  fcb->atts_changed = true;
1787  } else {
1788  ULONG defda;
1789 
1791  fcb->atts_changed = true;
1792 
1794  fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
1795 
1796  fcb->atts_deleted = defda == fcb->atts;
1797  }
1798 
1801 
1803 
1804 end:
1805  ExReleaseResourceLite(fcb->Header.Resource);
1806  ExReleaseResourceLite(&Vcb->tree_lock);
1807 
1808  return Status;
1809 }
1810 
1812  NTSTATUS Status;
1813  bool make_inline, compress;
1814  uint64_t start_data, end_data;
1815  ULONG buf_head;
1816  uint8_t* data;
1817 
1818  make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1819 
1820  if (!make_inline)
1822 
1823  if (make_inline) {
1824  start_data = 0;
1825  end_data = fcb->inode_item.st_size;
1826  buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1827  } else if (compress) {
1830  sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1831  buf_head = 0;
1832  } else {
1833  start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1);
1834  end_data = sector_align(start + length, Vcb->superblock.sector_size);
1835  buf_head = 0;
1836  }
1837 
1838  data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1839  if (!data) {
1840  ERR("out of memory\n");
1842  }
1843 
1844  RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1845 
1846  if (start > start_data || start + length < end_data) {
1847  Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1848 
1849  if (!NT_SUCCESS(Status)) {
1850  ERR("read_file returned %08lx\n", Status);
1851  ExFreePool(data);
1852  return Status;
1853  }
1854  }
1855 
1856  RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1857 
1858  if (make_inline) {
1859  uint16_t edsize;
1860  EXTENT_DATA* ed = (EXTENT_DATA*)data;
1861 
1862  Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1863  if (!NT_SUCCESS(Status)) {
1864  ERR("excise_extents returned %08lx\n", Status);
1865  ExFreePool(data);
1866  return Status;
1867  }
1868 
1869  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data);
1870 
1871  ed->generation = Vcb->superblock.generation;
1872  ed->decoded_size = end_data;
1876  ed->type = EXTENT_TYPE_INLINE;
1877 
1878  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback);
1879  if (!NT_SUCCESS(Status)) {
1880  ERR("add_extent_to_fcb returned %08lx\n", Status);
1881  ExFreePool(data);
1882  return Status;
1883  }
1884 
1885  ExFreePool(data);
1886 
1887  fcb->inode_item.st_blocks += end_data;
1888  } else if (compress) {
1890 
1891  ExFreePool(data);
1892 
1893  if (!NT_SUCCESS(Status)) {
1894  ERR("write_compressed returned %08lx\n", Status);
1895  return Status;
1896  }
1897  } else {
1898  Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback);
1899 
1900  ExFreePool(data);
1901 
1902  if (!NT_SUCCESS(Status)) {
1903  ERR("do_write_file returned %08lx\n", Status);
1904  return Status;
1905  }
1906  }
1907 
1908  return STATUS_SUCCESS;
1909 }
1910 
1912  FILE_ZERO_DATA_INFORMATION* fzdi = data;
1913  NTSTATUS Status;
1914  fcb* fcb;
1915  ccb* ccb;
1916  file_ref* fileref;
1917  LIST_ENTRY rollback, *le;
1919  BTRFS_TIME now;
1920  uint64_t start, end;
1921  extent* ext;
1923 
1924  if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1925  return STATUS_INVALID_PARAMETER;
1926 
1927  if (!FileObject) {
1928  ERR("FileObject was NULL\n");
1929  return STATUS_INVALID_PARAMETER;
1930  }
1931 
1932  if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1933  WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1934  return STATUS_INVALID_PARAMETER;
1935  }
1936 
1937  fcb = FileObject->FsContext;
1938 
1939  if (!fcb) {
1940  ERR("FCB was NULL\n");
1941  return STATUS_INVALID_PARAMETER;
1942  }
1943 
1944  ccb = FileObject->FsContext2;
1945 
1946  if (!ccb) {
1947  ERR("ccb was NULL\n");
1948  return STATUS_INVALID_PARAMETER;
1949  }
1950 
1951  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1952  WARN("insufficient privileges\n");
1953  return STATUS_ACCESS_DENIED;
1954  }
1955 
1956  fileref = ccb->fileref;
1957 
1958  if (!fileref) {
1959  ERR("fileref was NULL\n");
1960  return STATUS_INVALID_PARAMETER;
1961  }
1962 
1964 
1965  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1966  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1967 
1968  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
1969 
1970  if (fcb->type != BTRFS_TYPE_FILE) {
1971  WARN("FileObject did not point to a file\n");
1973  goto end;
1974  }
1975 
1976  if (fcb->ads) {
1977  ERR("FileObject is stream\n");
1979  goto end;
1980  }
1981 
1982  if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1984  goto end;
1985  }
1986 
1987  ext = NULL;
1988  le = fcb->extents.Flink;
1989  while (le != &fcb->extents) {
1991 
1992  if (!ext2->ignore) {
1993  ext = ext2;
1994  break;
1995  }
1996 
1997  le = le->Flink;
1998  }
1999 
2000  if (!ext) {
2002  goto end;
2003  }
2004 
2005  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
2006  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2007  if (!NT_SUCCESS(Status)) {
2008  ERR("zero_data returned %08lx\n", Status);
2009  goto end;
2010  }
2011  } else {
2012  start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
2013 
2014  if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
2015  end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
2016  else
2017  end = (fzdi->BeyondFinalZero.QuadPart >> Vcb->sector_shift) << Vcb->sector_shift;
2018 
2019  if (end <= start) {
2020  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2021  if (!NT_SUCCESS(Status)) {
2022  ERR("zero_data returned %08lx\n", Status);
2023  goto end;
2024  }
2025  } else {
2026  if (start > (uint64_t)fzdi->FileOffset.QuadPart) {
2027  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
2028  if (!NT_SUCCESS(Status)) {
2029  ERR("zero_data returned %08lx\n", Status);
2030  goto end;
2031  }
2032  }
2033 
2034  if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) {
2035  Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2036  if (!NT_SUCCESS(Status)) {
2037  ERR("zero_data returned %08lx\n", Status);
2038  goto end;
2039  }
2040  }
2041 
2042  if (end > start) {
2044  if (!NT_SUCCESS(Status)) {
2045  ERR("excise_extents returned %08lx\n", Status);
2046  goto end;
2047  }
2048  }
2049  }
2050  }
2051 
2052  CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false);
2053 
2056 
2057  fcb->inode_item.transid = Vcb->superblock.generation;
2058  fcb->inode_item.sequence++;
2059 
2060  if (!ccb->user_set_change_time)
2062 
2063  if (!ccb->user_set_write_time)
2065 
2066  fcb->extents_changed = true;
2067  fcb->inode_item_changed = true;
2069 
2071 
2072  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2073  fcb->subvol->root_item.ctime = now;
2074 
2076 
2077 end:
2078  if (!NT_SUCCESS(Status))
2080  else
2082 
2083  ExReleaseResourceLite(fcb->Header.Resource);
2084  ExReleaseResourceLite(&Vcb->tree_lock);
2085 
2086  return Status;
2087 }
2088 
2089 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2090  NTSTATUS Status;
2091  fcb* fcb;
2092  LIST_ENTRY* le;
2093  FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2094  ULONG i = 0;
2095  uint64_t last_start, last_end;
2096 
2097  TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2098 
2099  if (!FileObject) {
2100  ERR("FileObject was NULL\n");
2101  return STATUS_INVALID_PARAMETER;
2102  }
2103 
2104  if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2105  return STATUS_INVALID_PARAMETER;
2106 
2107  fcb = FileObject->FsContext;
2108 
2109  if (!fcb) {
2110  ERR("FCB was NULL\n");
2111  return STATUS_INVALID_PARAMETER;
2112  }
2113 
2114  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2115 
2116  // If file is not marked as sparse, claim the whole thing as an allocated range
2117 
2118  if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2119  if (fcb->inode_item.st_size == 0)
2121  else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2123  else {
2124  ranges[i].FileOffset.QuadPart = 0;
2125  ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2126  i++;
2128  }
2129 
2130  goto end;
2131 
2132  }
2133 
2134  le = fcb->extents.Flink;
2135 
2136  last_start = 0;
2137  last_end = 0;
2138 
2139  while (le != &fcb->extents) {
2141 
2142  if (!ext->ignore) {
2143  EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2144  uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2145 
2146  if (ext->offset > last_end) { // first extent after a hole
2147  if (last_end > last_start) {
2148  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2149  ranges[i].FileOffset.QuadPart = last_start;
2150  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2151  i++;
2152  } else {
2154  goto end;
2155  }
2156  }
2157 
2158  last_start = ext->offset;
2159  }
2160 
2161  last_end = ext->offset + len;
2162  }
2163 
2164  le = le->Flink;
2165  }
2166 
2167  if (last_end > last_start) {
2168  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2169  ranges[i].FileOffset.QuadPart = last_start;
2170  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2171  i++;
2172  } else {
2174  goto end;
2175  }
2176  }
2177 
2179 
2180 end:
2181  *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2182 
2183  ExReleaseResourceLite(fcb->Header.Resource);
2184 
2185  return Status;
2186 }
2187 
2188 static NTSTATUS get_object_id(PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2189  fcb* fcb;
2190 
2191  TRACE("(%p, %p, %lx, %p)\n", FileObject, buf, buflen, retlen);
2192 
2193  if (!FileObject) {
2194  ERR("FileObject was NULL\n");
2195  return STATUS_INVALID_PARAMETER;
2196  }
2197 
2198  if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2199  return STATUS_INVALID_PARAMETER;
2200 
2201  fcb = FileObject->FsContext;
2202 
2203  if (!fcb) {
2204  ERR("FCB was NULL\n");
2205  return STATUS_INVALID_PARAMETER;
2206  }
2207 
2208  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2209 
2210  RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t));
2211  RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
2212 
2213  ExReleaseResourceLite(fcb->Header.Resource);
2214 
2215  RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2216 
2217  *retlen = sizeof(FILE_OBJECTID_BUFFER);
2218 
2219  return STATUS_SUCCESS;
2220 }
2221 
2223  LIST_ENTRY* le;
2224 
2225  le = Vcb->all_fcbs.Flink;
2226  while (le != &Vcb->all_fcbs) {
2227  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2229 
2230  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2231  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2232 
2233  le = le->Flink;
2234  }
2235 }
2236 
2239  NTSTATUS Status;
2240  KIRQL irql;
2241  bool lock_paused_balance = false;
2242 
2243  TRACE("FSCTL_LOCK_VOLUME\n");
2244 
2245  if (Vcb->scrub.thread) {
2246  WARN("cannot lock while scrub running\n");
2247  return STATUS_DEVICE_NOT_READY;
2248  }
2249 
2250  if (Vcb->balance.thread) {
2251  WARN("cannot lock while balance running\n");
2252  return STATUS_DEVICE_NOT_READY;
2253  }
2254 
2255  TRACE("locking volume\n");
2256 
2258 
2259  if (Vcb->locked)
2260  return STATUS_SUCCESS;
2261 
2262  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2263 
2264  if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2266  ExReleaseResourceLite(&Vcb->fileref_lock);
2267  goto end;
2268  }
2269 
2270  ExReleaseResourceLite(&Vcb->fileref_lock);
2271 
2272  if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2273  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2274  KeClearEvent(&Vcb->balance.event);
2275  ExReleaseResourceLite(&Vcb->tree_lock);
2276 
2277  lock_paused_balance = true;
2278  }
2279 
2280  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2281 
2283 
2284  if (Vcb->need_write && !Vcb->readonly)
2285  Status = do_write(Vcb, Irp);
2286  else
2288 
2289  free_trees(Vcb);
2290 
2291  ExReleaseResourceLite(&Vcb->tree_lock);
2292 
2293  if (!NT_SUCCESS(Status)) {
2294  ERR("do_write returned %08lx\n", Status);
2295  goto end;
2296  }
2297 
2299 
2300  if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2301  Vcb->Vpb->Flags |= VPB_LOCKED;
2302  Vcb->locked = true;
2303  Vcb->locked_fileobj = IrpSp->FileObject;
2304  Vcb->lock_paused_balance = lock_paused_balance;
2305  } else {
2308 
2309  if (lock_paused_balance)
2310  KeSetEvent(&Vcb->balance.event, 0, false);
2311 
2312  goto end;
2313  }
2314 
2316 
2318 
2319 end:
2320  if (!NT_SUCCESS(Status))
2322 
2323  return Status;
2324 }
2325 
2327  KIRQL irql;
2328 
2330 
2331  Vcb->locked = false;
2332  Vcb->Vpb->Flags &= ~VPB_LOCKED;
2333  Vcb->locked_fileobj = NULL;
2334 
2336 
2337  if (Vcb->lock_paused_balance)
2338  KeSetEvent(&Vcb->balance.event, 0, false);
2339 }
2340 
2343 
2344  TRACE("FSCTL_UNLOCK_VOLUME\n");
2345 
2346  if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2347  return STATUS_NOT_LOCKED;
2348 
2349  TRACE("unlocking volume\n");
2350 
2352 
2354 
2355  return STATUS_SUCCESS;
2356 }
2357 
2360  LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2361  NTSTATUS Status;
2362  HANDLE h;
2363  PFILE_OBJECT fileobj;
2364  PDEVICE_OBJECT devobj;
2365  LIST_ENTRY* le;
2366 
2367  TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2368 
2369  if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2371 
2372 #if defined(_WIN64)
2373  if (IoIs32bitProcess(Irp)) {
2374  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2375  return STATUS_INVALID_PARAMETER;
2376 
2377  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2378  } else {
2379 #endif
2380  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2381  return STATUS_INVALID_PARAMETER;
2382 
2383  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2384 #if defined(_WIN64)
2385  }
2386 #endif
2387 
2388  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2389 
2390  if (!NT_SUCCESS(Status)) {
2391  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2392  return Status;
2393  }
2394 
2395  devobj = fileobj->DeviceObject;
2396 
2398 
2399  le = VcbList.Flink;
2400 
2401  while (le != &VcbList) {
2403 
2404  if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2405  if (Vcb->Vpb == devobj->Vpb) {
2406  KIRQL irql;
2407  PVPB newvpb;
2408  bool free_newvpb = false;
2409 
2410  newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2411  if (!newvpb) {
2412  ERR("out of memory\n");
2414  goto end;
2415  }
2416 
2417  RtlZeroMemory(newvpb, sizeof(VPB));
2418 
2419  ObReferenceObject(devobj);
2420 
2421  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2422 
2423  Vcb->removing = true;
2424 
2425  ExReleaseResourceLite(&Vcb->tree_lock);
2426 
2428 
2429  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2430 
2432 
2433  if (Vcb->need_write && !Vcb->readonly)
2434  Status = do_write(Vcb, Irp);
2435  else
2437 
2438  free_trees(Vcb);
2439 
2440  if (!NT_SUCCESS(Status)) {
2441  ERR("do_write returned %08lx\n", Status);
2442  ExReleaseResourceLite(&Vcb->tree_lock);
2443  ExFreePool(newvpb);
2444  ObDereferenceObject(devobj);
2445  goto end;
2446  }
2447 
2449 
2450  ExReleaseResourceLite(&Vcb->tree_lock);
2451 
2453 
2454  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2455  newvpb->Type = IO_TYPE_VPB;
2456  newvpb->Size = sizeof(VPB);
2457  newvpb->RealDevice = devobj;
2458  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2459 
2460  devobj->Vpb = newvpb;
2461  } else
2462  free_newvpb = true;
2463 
2465 
2466  if (free_newvpb)
2467  ExFreePool(newvpb);
2468 
2469  if (Vcb->open_files == 0)
2470  uninit(Vcb);
2471 
2472  ObDereferenceObject(devobj);
2473  }
2474 
2475  break;
2476  }
2477 
2478  le = le->Flink;
2479  }
2480 
2482 
2483 end:
2485 
2486  ObDereferenceObject(fileobj);
2487 
2488  return Status;
2489 }
2490 
2493  ULONG* volstate;
2494 
2495  if (Irp->AssociatedIrp.SystemBuffer) {
2496  volstate = Irp->AssociatedIrp.SystemBuffer;
2497  } else if (Irp->MdlAddress != NULL) {
2498  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2499 
2500  if (!volstate)
2502  } else
2504 
2505  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2506  return STATUS_INVALID_PARAMETER;
2507 
2508  *volstate = 0;
2509 
2510  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2511  return STATUS_INVALID_PARAMETER;
2512 
2513  Irp->IoStatus.Information = sizeof(ULONG);
2514 
2515  return STATUS_SUCCESS;
2516 }
2517 
2521 
2522  TRACE("FSCTL_GET_COMPRESSION\n");
2523 
2524  if (Irp->AssociatedIrp.SystemBuffer) {
2525  compression = Irp->AssociatedIrp.SystemBuffer;
2526  } else if (Irp->MdlAddress != NULL) {
2528 
2529  if (!compression)
2531  } else
2533 
2534  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2535  return STATUS_INVALID_PARAMETER;
2536 
2538 
2539  Irp->IoStatus.Information = sizeof(USHORT);
2540 
2541  return STATUS_SUCCESS;
2542 }
2543 
2547 
2548  TRACE("FSCTL_SET_COMPRESSION\n");
2549 
2550  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2551  return STATUS_INVALID_PARAMETER;
2552 
2553  compression = Irp->AssociatedIrp.SystemBuffer;
2554 
2556  return STATUS_INVALID_PARAMETER;
2557 
2558  return STATUS_SUCCESS;
2559 }
2560 
2562  LIST_ENTRY* le;
2563  volume_device_extension* vde = Vcb->vde;
2564  pdo_device_extension* pdode = vde->pdode;
2565 
2566  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2567 
2569 
2570  le = pdode->children.Flink;
2571  while (le != &pdode->children) {
2573 
2574  vc->generation = Vcb->superblock.generation - 1;
2575 
2576  le = le->Flink;
2577  }
2578 
2580 
2581  ExReleaseResourceLite(&Vcb->tree_lock);
2582 }
2583 
2585  NTSTATUS Status;
2586  bool open_files;
2587 
2588  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2589 
2590  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2591  return STATUS_SUCCESS;
2592 
2593  if (!shutdown) {
2594  if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2595  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2596  return STATUS_ACCESS_DENIED;
2597  }
2598 
2600  if (!NT_SUCCESS(Status)) {
2601  WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status);
2602  }
2603  }
2604 
2605  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2606 
2607  if (!Vcb->locked) {
2609 
2610  if (Vcb->need_write && !Vcb->readonly) {
2611  Status = do_write(Vcb, Irp);
2612 
2613  if (!NT_SUCCESS(Status))
2614  ERR("do_write returned %08lx\n", Status);
2615  }
2616  }
2617 
2618  free_trees(Vcb);
2619 
2620  Vcb->removing = true;
2621 
2622  open_files = Vcb->open_files > 0;
2623 
2624  if (Vcb->vde) {
2626  Vcb->vde->mounted_device = NULL;
2627  }
2628 
2629  ExReleaseResourceLite(&Vcb->tree_lock);
2630 
2631  if (!open_files)
2632  uninit(Vcb);
2633 
2634  return STATUS_SUCCESS;
2635 }
2636 
2638  NTSTATUS Status;
2639  ULONG to_read;
2640  superblock* sb;
2641  BTRFS_UUID fsuuid, devuuid;
2642  LIST_ENTRY* le;
2643 
2644  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2645 
2647  if (!sb) {
2648  ERR("out of memory\n");
2650  }
2651 
2652  Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2653  if (!NT_SUCCESS(Status)) {
2654  ERR("sync_read_phys returned %08lx\n", Status);
2655  ExFreePool(sb);
2656  return Status;
2657  }
2658 
2659  if (sb->magic != BTRFS_MAGIC) {
2660  TRACE("device is not Btrfs\n");
2661  ExFreePool(sb);
2662  return STATUS_SUCCESS;
2663  }
2664 
2665  if (!check_superblock_checksum(sb)) {
2666  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2667  ExFreePool(sb);
2668  return STATUS_SUCCESS;
2669  }
2670 
2671  fsuuid = sb->uuid;
2672  devuuid = sb->dev_item.device_uuid;
2673 
2674  ExFreePool(sb);
2675 
2677 
2678  le = VcbList.Flink;
2679 
2680  while (le != &VcbList) {
2682 
2683  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2684  LIST_ENTRY* le2;
2685 
2686  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2687 
2688  if (Vcb->superblock.num_devices > 1) {
2689  le2 = Vcb->devices.Flink;
2690  while (le2 != &Vcb->devices) {
2692 
2693  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2694  ExReleaseResourceLite(&Vcb->tree_lock);
2696  return STATUS_DEVICE_NOT_READY;
2697  }
2698 
2699  le2 = le2->Flink;
2700  }
2701  }
2702 
2703  ExReleaseResourceLite(&Vcb->tree_lock);
2705  return STATUS_SUCCESS;
2706  }
2707 
2708  le = le->Flink;
2709  }
2710 
2712 
2713  return STATUS_SUCCESS;
2714 }
2715 
2718  NTSTATUS Status;
2719 
2720  // FIXME - avoid "bootloader area"??
2721 
2722  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2723  dmdsa.Action = DeviceDsmAction_Trim;
2725  dmdsa.ParameterBlockOffset = 0;
2726  dmdsa.ParameterBlockLength = 0;
2727  dmdsa.DataSetRangesOffset = 0;
2728  dmdsa.DataSetRangesLength = 0;
2729 
2731  if (!NT_SUCCESS(Status))
2732  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status);
2733 }
2734 
2737  NTSTATUS Status;
2738  PFILE_OBJECT fileobj, mountmgrfo;
2740  HANDLE h;
2741  LIST_ENTRY* le;
2742  device* dev;
2743  DEV_ITEM* di;
2744  uint64_t dev_id, size;
2745  uint8_t* mb;
2746  uint64_t* stats;
2747  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2748  volume_child* vc;
2750  KEY searchkey;
2751  traverse_ptr tp;
2754  pdo_device_extension* pdode;
2755  const GUID* pnp_guid;
2757 
2758  pnp_name.Buffer = NULL;
2759 
2760  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2762 
2763  if (!Vcb->vde) {
2764  WARN("not allowing second device to be added to non-PNP device\n");
2765  return STATUS_NOT_SUPPORTED;
2766  }
2767 
2768  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2770 
2771 #if defined(_WIN64)
2772  if (IoIs32bitProcess(Irp)) {
2773  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2774  return STATUS_INVALID_PARAMETER;
2775 
2776  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2777  } else {
2778 #endif
2779  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2780  return STATUS_INVALID_PARAMETER;
2781 
2782  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2783 #if defined(_WIN64)
2784  }
2785 #endif
2786 
2787  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2788 
2789  if (!NT_SUCCESS(Status)) {
2790  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2791  return Status;
2792  }
2793 
2794  DeviceObject = fileobj->DeviceObject;
2795 
2796  Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid);
2797  if (!NT_SUCCESS(Status)) {
2798  ERR("get_device_pnp_name returned %08lx\n", Status);
2799  ObDereferenceObject(fileobj);
2800  return Status;
2801  }
2802 
2803  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2804  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2805  DeviceObject = DeviceObject->AttachedDevice;
2806 
2808  if (!NT_SUCCESS(Status)) {
2809  ERR("IOCTL_DISK_IS_WRITABLE returned %08lx\n", Status);
2810  ObDereferenceObject(fileobj);
2811  return Status;
2812  }
2813 
2815  if (!NT_SUCCESS(Status)) {
2816  ERR("is_device_part_of_mounted_btrfs_raid returned %08lx\n", Status);
2817  ObDereferenceObject(fileobj);
2818  return Status;
2819  }
2820 
2821  // if disk, check it has no partitions
2822  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2823  ULONG dlisize;
2824  DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
2825 
2826  dlisize = 0;
2827 
2828  do {
2829  dlisize += 1024;
2830 
2831  if (dli)
2832  ExFreePool(dli);
2833 
2834  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2835  if (!dli) {
2836  ERR("out of memory\n");
2838  goto end2;
2839  }
2840 
2842  } while (Status == STATUS_BUFFER_TOO_SMALL);
2843 
2844  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2845  ExFreePool(dli);
2846  ERR("not adding disk which has partitions\n");
2848  goto end2;
2849  }
2850 
2851  ExFreePool(dli);
2852  }
2853 
2855  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2856  if (NT_SUCCESS(Status)) {
2857  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2858  WARN("device was not disk\n");
2859  ObDereferenceObject(fileobj);
2860  return STATUS_INVALID_PARAMETER;
2861  }
2862  } else {
2863  sdn.DeviceNumber = 0xffffffff;
2864  sdn.PartitionNumber = 0xffffffff;
2865  }
2866 
2868  &gli, sizeof(gli), true, NULL);
2869  if (!NT_SUCCESS(Status)) {
2870  ERR("error reading length information: %08lx\n", Status);
2871  ObDereferenceObject(fileobj);
2872  return Status;
2873  }
2874 
2875  size = gli.Length.QuadPart;
2876 
2877  if (size < 0x100000) {
2878  ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2879  ObDereferenceObject(fileobj);
2880  return STATUS_INTERNAL_ERROR;
2881  }
2882 
2883  volume_removal(&pnp_name);
2884 
2885  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2886 
2887  if (Vcb->need_write)
2888  Status = do_write(Vcb, Irp);
2889  else
2891 
2892  free_trees(Vcb);
2893 
2894  if (!NT_SUCCESS(Status)) {
2895  ERR("do_write returned %08lx\n", Status);
2896  goto end;
2897  }
2898 
2900  if (!dev) {
2901  ERR("out of memory\n");
2903  goto end;
2904  }
2905 
2906  RtlZeroMemory(dev, sizeof(device));
2907 
2908  dev->devobj = DeviceObject;
2909  dev->fileobj = fileobj;
2910  dev->seeding = false;
2911  init_device(Vcb, dev, true);
2912 
2913  InitializeListHead(&dev->space);
2914 
2915  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2916  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2917  if (!NT_SUCCESS(Status)) {
2918  ERR("add_space_entry returned %08lx\n", Status);
2919  goto end;
2920  }
2921  }
2922 
2923  dev_id = 0;
2924 
2925  le = Vcb->devices.Flink;
2926  while (le != &Vcb->devices) {
2927  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2928 
2929  if (dev2->devitem.dev_id > dev_id)
2930  dev_id = dev2->devitem.dev_id;
2931 
2932  le = le->Flink;
2933  }
2934 
2935  dev_id++;
2936 
2937  dev->devitem.dev_id = dev_id;
2938  dev->devitem.num_bytes = size;
2939  dev->devitem.bytes_used = 0;
2940  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2941  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2942  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2943  dev->devitem.type = 0;
2944  dev->devitem.generation = 0;
2945  dev->devitem.start_offset = 0;
2946  dev->devitem.dev_group = 0;
2947  dev->devitem.seek_speed = 0;
2948  dev->devitem.bandwidth = 0;
2949  get_uuid(&dev->devitem.device_uuid);
2950  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2951 
2953  if (!di) {
2954  ERR("out of memory\n");
2955  goto end;
2956  }
2957 
2958  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2959 
2960  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2961  if (!NT_SUCCESS(Status)) {
2962  ERR("insert_tree_item returned %08lx\n", Status);
2963  ExFreePool(di);
2964  goto end;
2965  }
2966 
2967  // add stats entry to dev tree
2968  stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2969  if (!stats) {
2970  ERR("out of memory\n");
2972  goto end;
2973  }
2974 
2975  RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2976 
2977  searchkey.obj_id = 0;
2978  searchkey.obj_type = TYPE_DEV_STATS;
2979  searchkey.offset = di->dev_id;
2980 
2981  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2982  if (!NT_SUCCESS(Status)) {
2983  ERR("error - find_item returned %08lx\n", Status);
2984  ExFreePool(stats);
2985  goto end;
2986  }
2987 
2988  if (!keycmp(tp.item->key, searchkey)) {
2990  if (!NT_SUCCESS(Status)) {
2991  ERR("delete_tree_item returned %08lx\n", Status);
2992  ExFreePool(stats);
2993  goto end;
2994  }
2995  }
2996 
2997  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2998  if (!NT_SUCCESS(Status)) {
2999  ERR("insert_tree_item returned %08lx\n", Status);
3000  ExFreePool(stats);
3001  goto end;
3002  }
3003 
3004  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
3006 
3007  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3008  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
3009  if (!mb) {
3010  ERR("out of memory\n");
3012  goto end;
3013  }
3014 
3015  RtlZeroMemory(mb, 0x100000);
3016 
3017  Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3018  if (!NT_SUCCESS(Status)) {
3019  ERR("write_data_phys returned %08lx\n", Status);
3020  ExFreePool(mb);
3021  goto end;
3022  }
3023 
3024  ExFreePool(mb);
3025 
3026  vde = Vcb->vde;
3027  pdode = vde->pdode;
3028 
3030  if (!vc) {
3031  ERR("out of memory\n");
3033  goto end;
3034  }
3035 
3036  vc->uuid = dev->devitem.device_uuid;
3037  vc->devid = dev_id;
3038  vc->generation = Vcb->superblock.generation;
3039  vc->devobj = DeviceObject;
3040  vc->fileobj = fileobj;
3041  vc->notification_entry = NULL;
3042  vc->boot_volume = false;
3043 
3045  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3046  if (!NT_SUCCESS(Status))
3047  WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
3048 
3049  pnp_name2 = pnp_name;
3050 
3051  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3052  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3053  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3054  pnp_name2.Length -= 3 * sizeof(WCHAR);
3055  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3056  }
3057 
3058  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3059 
3060  if (pnp_name2.Length == 0)
3061  vc->pnp_name.Buffer = NULL;
3062  else {
3064  if (!vc->pnp_name.Buffer) {
3065  ERR("out of memory\n");
3067  goto end;
3068  }
3069 
3070  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3071  }
3072 
3073  vc->size = size;
3074  vc->seeding = false;
3075  vc->disk_num = sdn.DeviceNumber;
3076  vc->part_num = sdn.PartitionNumber;
3077  vc->had_drive_letter = false;
3078 
3080  InsertTailList(&pdode->children, &vc->list_entry);
3081  pdode->num_children++;
3082  pdode->children_loaded++;
3084 
3086  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3087  if (!NT_SUCCESS(Status))
3088  ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
3089  else {
3090  Status = remove_drive_letter(mountmgr, &pnp_name);
3092  WARN("remove_drive_letter returned %08lx\n", Status);
3093 
3095 
3096  ObDereferenceObject(mountmgrfo);
3097  }
3098 
3099  Vcb->superblock.num_devices++;
3100  Vcb->superblock.total_bytes += size;
3101  Vcb->devices_loaded++;
3102  InsertTailList(&Vcb->devices, &dev->list_entry);
3103 
3104  // FIXME - send notification that volume size has increased
3105 
3106  ObReferenceObject(DeviceObject); // for Vcb
3107 
3108  Status = do_write(Vcb, Irp);
3109  if (!NT_SUCCESS(Status))
3110  ERR("do_write returned %08lx\n", Status);
3111 
3112  ObReferenceObject(fileobj);
3113 
3114 end:
3115  free_trees(Vcb);
3116 
3117  ExReleaseResourceLite(&Vcb->tree_lock);
3118 
3119 end2:
3120  ObDereferenceObject(fileobj);
3121 
3122  if (pnp_name.Buffer)
3123  ExFreePool(pnp_name.Buffer);
3124 
3125  if (NT_SUCCESS(Status))
3127 
3128  return Status;
3129 }
3130 
3132  fcb* fcb;
3133  ccb* ccb;
3134 
3135  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3136 
3137  if (!FileObject)
3138  return STATUS_INVALID_PARAMETER;
3139 
3140  fcb = FileObject->FsContext;
3141  ccb = FileObject->FsContext2;
3142 
3143  if (!fcb)
3144  return STATUS_INVALID_PARAMETER;
3145 
3146  if (fcb != Vcb->volume_fcb)
3147  return STATUS_INVALID_PARAMETER;
3148 
3149  if (!ccb)
3150  return STATUS_INVALID_PARAMETER;
3151 
3152  ccb->allow_extended_dasd_io = true;
3153 
3154  return STATUS_SUCCESS;
3155 }
3156 
3158  if (length < sizeof(BTRFS_UUID))
3159  return STATUS_BUFFER_OVERFLOW;
3160 
3161  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3162 
3163  return STATUS_SUCCESS;
3164 }
3165 
3167  uint64_t devid;
3168  NTSTATUS Status;
3169  LIST_ENTRY* le;
3170 
3171  if (length < sizeof(uint64_t))
3172  return STATUS_INVALID_PARAMETER;
3173 
3174  if (Vcb->readonly)
3176 
3177  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3179 
3180  devid = *((uint64_t*)data);
3181 
3182  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3183 
3184  le = Vcb->devices.Flink;
3185 
3186  while (le != &Vcb->devices) {
3188 
3189  if (dev->devitem.dev_id == devid) {
3190  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3191  dev->stats_changed = true;
3192  Vcb->stats_changed = true;
3193  Vcb->need_write = true;
3195  goto end;
3196  }
3197 
3198  le = le->Flink;
3199  }
3200 
3201  WARN("device %I64x not found\n", devid);
3203 
3204 end:
3205  ExReleaseResourceLite(&Vcb->tree_lock);
3206 
3207  return Status;
3208 }
3209 
3212 
3213  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3214 
3215  // STUB
3216 
3217  if (!FileObject)
3218  return STATUS_INVALID_PARAMETER;
3219 
3221  return STATUS_INVALID_PARAMETER;
3222 
3223  fgiib->ChecksumAlgorithm = 0;
3224  fgiib->Reserved = 0;
3225  fgiib->Flags = 0;
3226  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3227  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3228 
3229  return STATUS_SUCCESS;
3230 }
3231 
3233  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3234 
3235  // STUB
3236 
3237  if (!FileObject)
3238  return STATUS_INVALID_PARAMETER;
3239 
3241  return STATUS_INVALID_PARAMETER;
3242 
3243  return STATUS_SUCCESS;
3244 }
3245 
3247  LIST_ENTRY* le;
3248 
3249  le = fcb->extents.Flink;
3250  while (le != &fcb->extents) {
3252 
3253  if (!ext->ignore)
3254  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3255 
3256  le = le->Flink;
3257  }
3258 
3259  return false;
3260 }
3261 
3264  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3265  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3266  NTSTATUS Status;
3267  PFILE_OBJECT sourcefo;
3268  uint64_t sourcelen, nbytes = 0;
3269  LIST_ENTRY rollback, *le, newexts;
3271  BTRFS_TIME now;
3272  bool make_inline;
3273 
3274  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3275  return STATUS_BUFFER_TOO_SMALL;
3276 
3277  if (Vcb->readonly)
3279 
3280  if (ded->ByteCount.QuadPart == 0)
3281  return STATUS_SUCCESS;
3282 
3283  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3284  return STATUS_INVALID_PARAMETER;
3285 
3287  return STATUS_ACCESS_DENIED;
3288 
3289  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3290  WARN("insufficient privileges\n");
3291  return STATUS_ACCESS_DENIED;
3292  }
3293 
3294  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3295  return STATUS_INVALID_PARAMETER;
3296 
3297  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3298  if (!NT_SUCCESS(Status)) {
3299  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3300  return Status;
3301  }
3302 
3303  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3304  WARN("source and destination are on different volumes\n");
3305  ObDereferenceObject(sourcefo);
3306  return STATUS_INVALID_PARAMETER;
3307  }
3308 
3309  sourcefcb = sourcefo->FsContext;
3310  sourceccb = sourcefo->FsContext2;
3311 
3312  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3313  ObDereferenceObject(sourcefo);
3314  return STATUS_INVALID_PARAMETER;
3315  }
3316 
3317  if (!sourcefcb->ads && !fcb->ads) {
3318  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3319  ObDereferenceObject(sourcefo);
3320  return STATUS_INVALID_PARAMETER;
3321  }
3322 
3323  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3324  ObDereferenceObject(sourcefo);
3325  return STATUS_INVALID_PARAMETER;
3326  }
3327  }
3328 
3329  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3330  WARN("insufficient privileges\n");
3331  ObDereferenceObject(sourcefo);
3332  return STATUS_ACCESS_DENIED;
3333  }
3334 
3335  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3336  ObDereferenceObject(sourcefo);
3337  return STATUS_INVALID_PARAMETER;
3338  }
3339 
3340  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3341 
3342  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3343  ObDereferenceObject(sourcefo);
3344  return STATUS_NOT_SUPPORTED;
3345  }
3346 
3347  if (fcb == sourcefcb &&
3350  WARN("source and destination are the same, and the ranges overlap\n");
3351  ObDereferenceObject(sourcefo);
3352  return STATUS_INVALID_PARAMETER;
3353  }
3354 
3355  // fail if nocsum flag set on one file but not the other
3356  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3357  ObDereferenceObject(sourcefo);
3358  return STATUS_INVALID_PARAMETER;
3359  }
3360 
3362  InitializeListHead(&newexts);
3363 
3364  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3365 
3366  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3367 
3368  if (fcb != sourcefcb)
3369  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3370 
3373  goto end;
3374  }
3375 
3376  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3378  goto end;
3379  }
3380 
3381  make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3382 
3383  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3384  uint8_t* data2;
3385  ULONG bytes_read, dataoff, datalen2;
3386 
3387  if (make_inline) {
3388  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3389  datalen2 = (ULONG)fcb->inode_item.st_size;
3390  } else if (fcb->ads) {
3391  dataoff = 0;
3392  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3393  } else {
3394  dataoff = ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1);
3395  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3396  }
3397 
3399  if (!data2) {
3400  ERR("out of memory\n");
3402  goto end;
3403  }
3404 
3405  if (dataoff > 0) {
3406  if (make_inline)
3407  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3408  else
3409  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3410 
3411  if (!NT_SUCCESS(Status)) {
3412  ERR("read_file returned %08lx\n", Status);
3413  ExFreePool(data2);
3414  goto end;
3415  }
3416  }
3417 
3418  if (sourcefcb->ads) {
3419  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3420  if (!NT_SUCCESS(Status)) {
3421  ERR("read_stream returned %08lx\n", Status);
3422  ExFreePool(data2);
3423  goto end;
3424  }
3425  } else {
3426  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3427  if (!NT_SUCCESS(Status)) {
3428  ERR("read_file returned %08lx\n", Status);
3429  ExFreePool(data2);
3430  goto end;
3431  }
3432  }
3433 
3434  if (dataoff + bytes_read < datalen2)
3435  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3436 
3437  if (fcb->ads)
3439  else if (make_inline) {
3440  uint16_t edsize;
3441  EXTENT_DATA* ed;
3442 
3443  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3444  if (!NT_SUCCESS(Status)) {
3445  ERR("excise_extents returned %08lx\n", Status);
3446  ExFreePool(data2);
3447  goto end;
3448  }
3449 
3450  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3451 
3452  ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3453  if (!ed) {
3454  ERR("out of memory\n");
3455  ExFreePool(data2);
3457  goto end;
3458  }
3459 
3460  ed->generation = Vcb->superblock.generation;
3465  ed->type = EXTENT_TYPE_INLINE;
3466 
3467  RtlCopyMemory(ed->data, data2, datalen2);
3468 
3469  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3470  if (!NT_SUCCESS(Status)) {
3471  ERR("add_extent_to_fcb returned %08lx\n", Status);
3472  ExFreePool(data2);
3473  goto end;
3474  }
3475 
3476  fcb->inode_item.st_blocks += datalen2;
3477  } else {
3478  uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1));
3479 
3480  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3481  if (!NT_SUCCESS(Status)) {
3482  ERR("do_write_file returned %08lx\n", Status);
3483  ExFreePool(data2);
3484  goto end;
3485  }
3486  }
3487 
3488  ExFreePool(data2);
3489  } else {
3490  LIST_ENTRY* lastextle;
3491 
3492  le = sourcefcb->extents.Flink;
3493  while (le != &sourcefcb->extents) {
3495 
3496  if (!ext->ignore) {
3497  if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3498  break;
3499 
3500  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3501  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3502  extent* ext2;
3503  EXTENT_DATA2 *ed2s, *ed2d;
3504  chunk* c;
3505 
3506  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3507 
3508  if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3509  le = le->Flink;
3510  continue;
3511  }
3512 
3514  if (!ext2) {
3515  ERR("out of memory\n");
3517  goto end;
3518  }
3519 
3520  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3521  ext2->offset = ded->TargetFileOffset.QuadPart;
3522  else
3523  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3524 
3525  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3526  ext2->unique = false;
3527  ext2->ignore = false;
3528  ext2->inserted = true;
3529 
3530  ext2->extent_data.generation = Vcb->superblock.generation;
3531  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3532  ext2->extent_data.compression = ext->extent_data.compression;
3533  ext2->extent_data.encryption = ext->extent_data.encryption;
3534  ext2->extent_data.encoding = ext->extent_data.encoding;
3535  ext2->extent_data.type = ext->extent_data.type;
3536 
3537  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3538 
3539  ed2d->address = ed2s->address;
3540  ed2d->size = ed2s->size;
3541 
3542  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3543  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3544  ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3545  } else {
3546  ed2d->offset = ed2s->offset;
3547  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3548  }
3549 
3550  if (ext->csum) {
3551  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3552  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)((ed2d->num_bytes * Vcb->csum_size) >> Vcb->sector_shift), ALLOC_TAG);
3553  if (!ext2->csum) {
3554  ERR("out of memory\n");
3556  ExFreePool(ext2);
3557  goto end;
3558  }
3559 
3560  RtlCopyMemory(ext2->csum, (uint8_t*)ext->csum + (((ed2d->offset - ed2s->offset) * Vcb->csum_size) >> Vcb->sector_shift),
3561  (ULONG)((ed2d->num_bytes * Vcb->csum_size) >> Vcb->sector_shift));
3562  } else {
3563  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)((ed2d->size * Vcb->csum_size) >> Vcb->sector_shift), ALLOC_TAG);
3564  if (!ext2->csum) {
3565  ERR("out of memory\n");
3567  ExFreePool(ext2);
3568  goto end;
3569  }
3570 
3571  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)((ed2s->size * Vcb->csum_size) >> Vcb->sector_shift));
3572  }
3573  } else
3574  ext2->csum = NULL;
3575 
3576  InsertTailList(&newexts, &ext2->list_entry);
3577 
3578  c = get_chunk_from_address(Vcb, ed2s->address);
3579  if (!c) {
3580  ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3582  goto end;
3583  }
3584 
3585  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3586  1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3587  if (!NT_SUCCESS(Status)) {
3588  ERR("update_changed_extent_ref returned %08lx\n", Status);
3589  goto end;
3590  }
3591 
3592  nbytes += ed2d->num_bytes;
3593  }
3594  }
3595 
3596  le = le->Flink;
3597  }
3598 
3600  if (!NT_SUCCESS(Status)) {
3601  ERR("excise_extents returned %08lx\n", Status);
3602 
3603  while (!IsListEmpty(&newexts)) {
3605  ExFreePool(ext);
3606  }
3607 
3608  goto end;
3609  }
3610 
3611  // clear unique flags in source fcb
3612  le = sourcefcb->extents.Flink;
3613  while (le != &sourcefcb->extents) {
3615 
3616  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3617  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3618  LIST_ENTRY* le2;
3619 
3620  le2 = newexts.Flink;
3621  while (le2 != &newexts) {
3623 
3624  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3625  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3626 
3627  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3628  ext->unique = false;
3629  break;
3630  }
3631  }
3632 
3633  le2 = le2->Flink;
3634  }
3635  }
3636 
3637  le = le->Flink;
3638  }
3639 
3640  lastextle = &fcb->extents;
3641  while (!IsListEmpty(&newexts)) {
3643 
3644  add_extent(fcb, lastextle, ext);
3645  lastextle = &ext->list_entry;
3646  }
3647  }
3648 
3651 
3652  if (fcb->ads) {
3653  ccb->fileref->parent->fcb->inode_item.sequence++;
3654 
3655  if (!ccb->user_set_change_time)
3656  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3657 
3658  ccb->fileref->parent->fcb->inode_item_changed = true;
3659  mark_fcb_dirty(ccb->fileref->parent->fcb);
3660  } else {
3661  fcb->inode_item.st_blocks += nbytes;
3662  fcb->inode_item.sequence++;
3663 
3664  if (!ccb->user_set_change_time)
3666 
3667  if (!ccb->user_set_write_time) {
3670  }
3671 
3672  fcb->inode_item_changed = true;
3673  fcb->extents_changed = true;
3674  }
3675 
3677 
3678  if (FileObject->SectionObjectPointer->DataSectionObject)
3679  CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3680 
3682 
3683 end:
3684  ObDereferenceObject(sourcefo);
3685 
3686  if (NT_SUCCESS(Status))
3688  else
3690 
3691  if (fcb != sourcefcb)
3692  ExReleaseResourceLite(sourcefcb->Header.Resource);
3693 
3694  ExReleaseResourceLite(fcb->Header.Resource);
3695 
3696  ExReleaseResourceLite(&Vcb->tree_lock);
3697 
3698  return Status;
3699 }
3700 
3703  KEY searchkey;
3704  traverse_ptr tp;
3705  NTSTATUS Status;
3706  uint8_t c = hash >> 24;
3707 
3708  if (subvol->fcbs_ptrs[c]) {
3709  LIST_ENTRY* le = subvol->fcbs_ptrs[c];
3710 
3711  while (le != &subvol->fcbs) {
3712  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3713 
3714  if (fcb2->inode == inode)
3715  return STATUS_SUCCESS;
3716  else if (fcb2->hash > hash)
3717  break;
3718 
3719  le = le->Flink;
3720  }
3721  }
3722 
3723  searchkey.obj_id = inode;
3724  searchkey.obj_type = TYPE_INODE_ITEM;
3725  searchkey.offset = 0xffffffffffffffff;
3726 
3727  Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
3728  if (!NT_SUCCESS(Status)) {
3729  ERR("find_item returned %08lx\n", Status);
3730  return Status;
3731  }
3732 
3733  if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
3734  return STATUS_SUCCESS;
3735 
3736  return STATUS_NOT_FOUND;
3737 }
3738 
3740  NTSTATUS Status;
3741  btrfs_mknod* bmn;
3742  fcb *parfcb, *fcb;
3743  ccb* parccb;
3744  file_ref *parfileref, *fileref;
3746  root* subvol;
3747  uint64_t inode;
3748  dir_child* dc;
3750  BTRFS_TIME now;
3751  ANSI_STRING utf8;
3752  ULONG len, i;
3753  SECURITY_SUBJECT_CONTEXT subjcont;
3754  PSID owner;
3755  BOOLEAN defaulted;
3756 
3757  TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
3758 
3759  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3760  return STATUS_INVALID_PARAMETER;
3761 
3762  if (Vcb->readonly)
3764 
3765  parfcb = FileObject->FsContext;
3766 
3767  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3768  WARN("trying to create file in something other than a directory\n");
3769  return STATUS_INVALID_PARAMETER;
3770  }
3771 
3772  if (is_subvol_readonly(parfcb->subvol, Irp))
3773  return STATUS_ACCESS_DENIED;
3774 
3775  parccb = FileObject->FsContext2;
3776  parfileref = parccb->fileref;
3777 
3778  if (!parfileref)
3779  return STATUS_INVALID_PARAMETER;
3780 
3781  if (datalen < sizeof(btrfs_mknod))
3782  return STATUS_INVALID_PARAMETER;
3783 
3784  bmn = (btrfs_mknod*)data;
3785 
3786  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3787  return STATUS_INVALID_PARAMETER;
3788 
3789  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3790  return STATUS_INVALID_PARAMETER;
3791 
3792  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3793  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3794  WARN("insufficient privileges\n");
3795  return STATUS_ACCESS_DENIED;
3796  }
3797 
3798  if (bmn->inode != 0) {
3799  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3801  }
3802 
3803  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3804  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3806  }
3807 
3808  // don't allow files called . or ..
3809  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3811 
3812  Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3813  if (!NT_SUCCESS(Status)) {
3814  ERR("utf16_to_utf8 returned %08lx\n", Status);
3815  return Status;
3816  }
3817 
3818  if (len == 0) {
3819  ERR("utf16_to_utf8 returned a length of 0\n");
3820  return STATUS_INTERNAL_ERROR;
3821  }
3822 
3823  if (len > 0xffff) {
3824  ERR("len was too long (%lx)\n", len);
3825  return STATUS_INVALID_PARAMETER;
3826  }
3827 
3828  utf8.MaximumLength = utf8.Length = (USHORT)len;
3830 
3831  if (!utf8.Buffer) {
3832  ERR("out of memory\n");
3834  }
3835 
3836  Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3837  if (!NT_SUCCESS(Status)) {
3838  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
3839  ExFreePool(utf8.Buffer);
3840  return Status;
3841  }
3842 
3843  name.Length = name.MaximumLength = bmn->namelen;
3844  name.Buffer = bmn->name;
3845 
3846  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3848  ERR("find_file_in_dir returned %08lx\n", Status);
3849  goto end;
3850  }
3851 
3852  if (NT_SUCCESS(Status)) {
3853  WARN("filename already exists\n");
3855  goto end;
3856  }
3857 
3860 
3862  if (!fcb) {
3863  ERR("out of memory\n");
3865  goto end;
3866  }
3867 
3868  fcb->Vcb = Vcb;
3869 
3870  fcb->inode_item.generation = Vcb->superblock.generation;
3871  fcb->inode_item.transid = Vcb->superblock.generation;
3872  fcb->inode_item.st_size = 0;
3873  fcb->inode_item.st_blocks = 0;
3874  fcb->inode_item.block_group = 0;
3875  fcb->inode_item.st_nlink = 1;
3879 
3880  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3881  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3882  else
3883  fcb->inode_item.st_rdev = 0;
3884 
3885  fcb->inode_item.flags = 0;
3886  fcb->inode_item.sequence = 1;
3890  fcb->inode_item.otime = now;
3891 
3892  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3894  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3896  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3898  else if (bmn->type == BTRFS_TYPE_FIFO)
3900  else if (bmn->type == BTRFS_TYPE_SOCKET)
3902  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3904  else
3906 
3907  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3908  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3909 
3910  // inherit nodatacow flag from parent directory
3911  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3913 
3914  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3916  }
3917 
3918  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3920 
3923 
3924  fcb->inode_item_changed = true;
3925 
3926  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3927  fcb->Header.AllocationSize.QuadPart = 0;
3928  fcb->Header.FileSize.QuadPart = 0;
3929  fcb->Header.ValidDataLength.QuadPart = 0;
3930 
3931  fcb->atts = 0;
3932 
3933  if (bmn->name[0] == '.')
3935 
3936  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3938 
3939  fcb->atts_changed = false;
3940 
3941  InterlockedIncrement(&parfcb->refcount);
3942  fcb->subvol = parfcb->subvol;
3943 
3944  SeCaptureSubjectContext(&subjcont);
3945 
3946  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3948 
3949  if (!NT_SUCCESS(Status)) {
3950  ERR("SeAssignSecurityEx returned %08lx\n", Status);
3951  reap_fcb(fcb);
3952  goto end;
3953  }
3954 
3955  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3956  if (!NT_SUCCESS(Status)) {
3957  WARN("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
3958  fcb->sd_dirty = true;
3959  } else {
3960  fcb->inode_item.st_uid = sid_to_uid(owner);
3962  }
3963 
3964  find_gid(fcb, parfcb, &subjcont);
3965 
3966  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3967  acquire_fcb_lock_exclusive(Vcb);
3968 
3969  if (bmn->inode == 0) {
3970  fcb->inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3971  fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t));
3972  } else {
3973  if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3974  fcb->inode = parfcb->subvol->lastinode = bmn->inode;
3975  fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t));
3976  } else {
3977  uint32_t hash = calc_crc32c(0xffffffff, (uint8_t*)&bmn->inode, sizeof(uint64_t));
3978 
3980  if (NT_SUCCESS(Status)) { // STATUS_SUCCESS means inode found
3981  release_fcb_lock(Vcb);
3982  ExReleaseResourceLite(&Vcb->fileref_lock);
3983 
3984  WARN("inode collision\n");
3986  goto end;
3987  } else if (Status != STATUS_NOT_FOUND) {
3988  ERR("check_inode_used returned %08lx\n", Status);
3989 
3990  release_fcb_lock(Vcb);
3991  ExReleaseResourceLite(&Vcb->fileref_lock);
3992  goto end;
3993  }
3994 
3995  fcb->inode = bmn->inode;
3996  fcb->hash = hash;
3997  }
3998  }
3999 
4000  fcb->inode = inode;
4001  fcb->type = bmn->type;
4002 
4004  if (!fileref) {
4005  release_fcb_lock(Vcb);
4006  ExReleaseResourceLite(&Vcb->fileref_lock);