ReactOS  0.4.14-dev-554-g2f8d847
fsctl.c
Go to the documentation of this file.
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "btrfs_drv.h"
19 #include "btrfsioctl.h"
20 #include <ntddstor.h>
21 #include <ntdddisk.h>
22 #ifndef __REACTOS__
23 #include <sys/stat.h>
24 #endif
25 
26 #ifndef FSCTL_CSV_CONTROL
27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
28 #endif
29 
30 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE
31 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS)
32 #endif
33 
34 #define DOTDOT ".."
35 
36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
37 
38 #ifndef _MSC_VER // not in mingw yet
39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
40 #endif
41 
42 #define SEF_SACL_AUTO_INHERIT 0x02
43 
44 extern LIST_ENTRY VcbList;
46 extern PDRIVER_OBJECT drvobj;
49 
51 
53  btrfs_get_file_ids* bgfi;
54  fcb* fcb;
55 
56  if (length < sizeof(btrfs_get_file_ids))
58 
59  if (!FileObject)
61 
62  fcb = FileObject->FsContext;
63 
64  if (!fcb)
66 
67  bgfi = data;
68 
69  bgfi->subvol = fcb->subvol->id;
70  bgfi->inode = fcb->inode;
71  bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
72 
73  return STATUS_SUCCESS;
74 }
75 
76 static void get_uuid(BTRFS_UUID* uuid) {
77  LARGE_INTEGER seed;
78  uint8_t i;
79 
81 
82  for (i = 0; i < 16; i+=2) {
83  ULONG rand = RtlRandomEx(&seed.LowPart);
84 
85  uuid->uuid[i] = (rand & 0xff00) >> 8;
86  uuid->uuid[i+1] = rand & 0xff;
87  }
88 }
89 
91  uint8_t* buf;
94  LIST_ENTRY* le;
95  tree t;
96  tree_header* th;
97  chunk* c;
98 
99  buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
100  if (!buf) {
101  ERR("out of memory\n");
103  }
104 
105  wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
106  wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
107 
108  Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, true, buf, NULL, NULL, Irp, 0, false, NormalPagePriority);
109  if (!NT_SUCCESS(Status)) {
110  ERR("read_data returned %08x\n", Status);
111  goto end;
112  }
113 
114  th = (tree_header*)buf;
115 
116  RtlZeroMemory(&t, sizeof(tree));
117  t.root = subvol;
118  t.header.level = th->level;
119  t.header.tree_id = t.root->id;
120 
122  if (!NT_SUCCESS(Status)) {
123  ERR("get_tree_new_address returned %08x\n", Status);
124  goto end;
125  }
126 
127  if (!t.has_new_address) {
128  ERR("tree new address not set\n");
130  goto end;
131  }
132 
133  c = get_chunk_from_address(Vcb, t.new_address);
134 
135  if (c)
136  c->used += Vcb->superblock.node_size;
137  else {
138  ERR("could not find chunk for address %I64x\n", t.new_address);
140  goto end;
141  }
142 
143  th->address = t.new_address;
144  th->tree_id = subvol->id;
145  th->generation = Vcb->superblock.generation;
146  th->fs_uuid = Vcb->superblock.uuid;
147 
148  if (th->level == 0) {
149  uint32_t i;
150  leaf_node* ln = (leaf_node*)&th[1];
151 
152  for (i = 0; i < th->num_items; i++) {
153  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)) {
154  EXTENT_DATA* ed = (EXTENT_DATA*)(((uint8_t*)&th[1]) + ln[i].offset);
155 
156  if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
157  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
158 
159  if (ed2->size != 0) { // not sparse
161 
162  if (!NT_SUCCESS(Status)) {
163  ERR("increase_extent_refcount_data returned %08x\n", Status);
164  goto end;
165  }
166  }
167  }
168  }
169  }
170  } else {
171  uint32_t i;
172  internal_node* in = (internal_node*)&th[1];
173 
174  for (i = 0; i < th->num_items; i++) {
175  TREE_BLOCK_REF tbr;
176 
177  tbr.offset = subvol->id;
178 
179  Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp);
180  if (!NT_SUCCESS(Status)) {
181  ERR("increase_extent_refcount returned %08x\n", Status);
182  goto end;
183  }
184  }
185  }
186 
187  *((uint32_t*)buf) = ~calc_crc32c(0xffffffff, (uint8_t*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
188 
191  wtc.stripes_left = 0;
192 
193  Status = write_data(Vcb, t.new_address, buf, Vcb->superblock.node_size, &wtc, NULL, NULL, false, 0, NormalPagePriority);
194  if (!NT_SUCCESS(Status)) {
195  ERR("write_data returned %08x\n", Status);
196  goto end;
197  }
198 
199  if (wtc.stripes.Flink != &wtc.stripes) {
200  bool need_wait = false;
201 
202  // launch writes and wait
203  le = wtc.stripes.Flink;
204  while (le != &wtc.stripes) {
206 
207  if (stripe->status != WriteDataStatus_Ignore) {
208  need_wait = true;
210  }
211 
212  le = le->Flink;
213  }
214 
215  if (need_wait)
217 
218  le = wtc.stripes.Flink;
219  while (le != &wtc.stripes) {
221 
222  if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
223  Status = stripe->iosb.Status;
225  break;
226  }
227 
228  le = le->Flink;
229  }
230 
232  buf = NULL;
233  }
234 
235  if (NT_SUCCESS(Status))
236  *newaddr = t.new_address;
237 
238 end:
239 
240  if (buf)
241  ExFreePool(buf);
242 
243  return Status;
244 }
245 
246 void flush_subvol_fcbs(root* subvol) {
247  LIST_ENTRY* le = subvol->fcbs.Flink;
248 
249  if (IsListEmpty(&subvol->fcbs))
250  return;
251 
252  while (le != &subvol->fcbs) {
253  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
255 
256  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
257  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
258 
259  le = le->Flink;
260  }
261 }
262 
265  uint64_t id;
267  root *r, *subvol = subvol_fcb->subvol;
268  KEY searchkey;
270  uint64_t address, *root_num;
272  BTRFS_TIME now;
273  fcb* fcb = parent->FsContext;
274  ccb* ccb = parent->FsContext2;
275  LIST_ENTRY* le;
276  file_ref *fileref, *fr;
277  dir_child* dc = NULL;
278 
279  if (!ccb) {
280  ERR("error - ccb was NULL\n");
281  return STATUS_INTERNAL_ERROR;
282  }
283 
284  if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
285  WARN("insufficient privileges\n");
286  return STATUS_ACCESS_DENIED;
287  }
288 
289  fileref = ccb->fileref;
290 
291  if (fileref->fcb == Vcb->dummy_fcb)
292  return STATUS_ACCESS_DENIED;
293 
294  // flush open files on this subvol
295 
297 
298  // flush metadata
299 
300  if (Vcb->need_write)
301  Status = do_write(Vcb, Irp);
302  else
304 
305  free_trees(Vcb);
306 
307  if (!NT_SUCCESS(Status)) {
308  ERR("do_write returned %08x\n", Status);
309  return Status;
310  }
311 
313 
314  // create new root
315 
316  id = InterlockedIncrement64(&Vcb->root_root->lastinode);
317  Status = create_root(Vcb, id, &r, true, Vcb->superblock.generation, Irp);
318 
319  if (!NT_SUCCESS(Status)) {
320  ERR("create_root returned %08x\n", Status);
321  goto end;
322  }
323 
324  r->lastinode = subvol->lastinode;
325 
326  if (!Vcb->uuid_root) {
327  root* uuid_root;
328 
329  TRACE("uuid root doesn't exist, creating it\n");
330 
331  Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
332 
333  if (!NT_SUCCESS(Status)) {
334  ERR("create_root returned %08x\n", Status);
335  goto end;
336  }
337 
338  Vcb->uuid_root = uuid_root;
339  }
340 
341  root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
342  if (!root_num) {
343  ERR("out of memory\n");
345  goto end;
346  }
347 
348  tp.tree = NULL;
349 
350  do {
351  get_uuid(&r->root_item.uuid);
352 
353  RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
354  searchkey.obj_type = TYPE_SUBVOL_UUID;
355  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
356 
357  Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
358  } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
359 
360  *root_num = r->id;
361 
362  Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
363  if (!NT_SUCCESS(Status)) {
364  ERR("insert_tree_item returned %08x\n", Status);
365  ExFreePool(root_num);
366  goto end;
367  }
368 
369  searchkey.obj_id = r->id;
370  searchkey.obj_type = TYPE_ROOT_ITEM;
371  searchkey.offset = 0xffffffffffffffff;
372 
373  Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
374  if (!NT_SUCCESS(Status)) {
375  ERR("error - find_item returned %08x\n", Status);
376  goto end;
377  }
378 
379  Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, &address, Irp, &rollback);
380  if (!NT_SUCCESS(Status)) {
381  ERR("snapshot_tree_copy returned %08x\n", Status);
382  goto end;
383  }
384 
387 
388  r->root_item.inode.generation = 1;
389  r->root_item.inode.st_size = 3;
390  r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks;
391  r->root_item.inode.st_nlink = 1;
392  r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
393  r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean
394  r->root_item.generation = Vcb->superblock.generation;
395  r->root_item.objid = subvol->root_item.objid;
396  r->root_item.block_number = address;
397  r->root_item.bytes_used = subvol->root_item.bytes_used;
398  r->root_item.last_snapshot_generation = Vcb->superblock.generation;
399  r->root_item.root_level = subvol->root_item.root_level;
400  r->root_item.generation2 = Vcb->superblock.generation;
401  r->root_item.parent_uuid = subvol->root_item.uuid;
402  r->root_item.ctransid = subvol->root_item.ctransid;
403  r->root_item.otransid = Vcb->superblock.generation;
404  r->root_item.ctime = subvol->root_item.ctime;
405  r->root_item.otime = now;
406 
407  if (readonly)
408  r->root_item.flags |= BTRFS_SUBVOL_READONLY;
409 
410  r->treeholder.address = address;
411 
412  // FIXME - do we need to copy over the send and receive fields too?
413 
414  if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
415  ERR("error - could not find ROOT_ITEM for subvol %I64x\n", r->id);
417  goto end;
418  }
419 
420  RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM));
421 
422  // update ROOT_ITEM of original subvol
423 
424  subvol->root_item.last_snapshot_generation = Vcb->superblock.generation;
425 
427 
428  // create fileref for entry in other subvolume
429 
430  fr = create_fileref(Vcb);
431  if (!fr) {
432  ERR("out of memory\n");
434  goto end;
435  }
436 
437  Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, false, fcb, &fr->fcb, PagedPool, Irp);
438  if (!NT_SUCCESS(Status)) {
439  ERR("open_fcb returned %08x\n", Status);
440  free_fileref(fr);
441  goto end;
442  }
443 
444  fr->parent = fileref;
445 
446  Status = add_dir_child(fileref->fcb, r->id, true, utf8, name, BTRFS_TYPE_DIRECTORY, &dc);
447  if (!NT_SUCCESS(Status))
448  WARN("add_dir_child returned %08x\n", Status);
449 
450  fr->dc = dc;
451  dc->fileref = fr;
452 
453  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
454  InsertTailList(&fileref->children, &fr->list_entry);
455  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
456 
458 
459  fr->created = true;
460  mark_fileref_dirty(fr);
461 
462  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
463  fr->fcb->fileref = fr;
464 
465  fr->fcb->subvol->parent = fileref->fcb->subvol->id;
466 
467  free_fileref(fr);
468 
469  // change fcb's INODE_ITEM
470 
471  fcb->inode_item.transid = Vcb->superblock.generation;
473  fcb->inode_item.st_size += utf8->Length * 2;
474 
477 
478  if (!ccb->user_set_write_time)
480 
481  fcb->inode_item_changed = true;
483 
484  fcb->subvol->root_item.ctime = now;
485  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
486 
489 
490  le = subvol->fcbs.Flink;
491  while (le != &subvol->fcbs) {
492  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
493  LIST_ENTRY* le2 = fcb2->extents.Flink;
494 
495  while (le2 != &fcb2->extents) {
497 
498  if (!ext->ignore)
499  ext->unique = false;
500 
501  le2 = le2->Flink;
502  }
503 
504  le = le->Flink;
505  }
506 
507  Status = do_write(Vcb, Irp);
508 
509  free_trees(Vcb);
510 
511  if (!NT_SUCCESS(Status))
512  ERR("do_write returned %08x\n", Status);
513 
514 end:
515  if (NT_SUCCESS(Status))
517  else
519 
520  return Status;
521 }
522 
524  PFILE_OBJECT subvol_obj;
527  fcb* subvol_fcb;
528  HANDLE subvolh;
529  bool readonly, posix;
530  ANSI_STRING utf8;
531  UNICODE_STRING nameus;
532  ULONG len;
533  fcb* fcb;
534  ccb* ccb;
535  file_ref *fileref, *fr2;
536 
537 #if defined(_WIN64)
538  if (IoIs32bitProcess(Irp)) {
539  btrfs_create_snapshot32* bcs32 = data;
540 
543 
546 
547  subvolh = Handle32ToHandle(bcs32->subvol);
548 
549  nameus.Buffer = bcs32->name;
550  nameus.Length = nameus.MaximumLength = bcs32->namelen;
551 
552  readonly = bcs32->readonly;
553  posix = bcs32->posix;
554  } else {
555 #endif
558 
561 
562  subvolh = bcs->subvol;
563 
564  nameus.Buffer = bcs->name;
565  nameus.Length = nameus.MaximumLength = bcs->namelen;
566 
567  readonly = bcs->readonly;
568  posix = bcs->posix;
569 #if defined(_WIN64)
570  }
571 #endif
572 
573  if (!subvolh)
575 
576  if (!FileObject || !FileObject->FsContext)
578 
579  fcb = FileObject->FsContext;
580  ccb = FileObject->FsContext2;
581 
582  if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY)
584 
585  fileref = ccb->fileref;
586 
587  if (!fileref) {
588  ERR("fileref was NULL\n");
590  }
591 
592  if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
593  WARN("insufficient privileges\n");
594  return STATUS_ACCESS_DENIED;
595  }
596 
597  if (Vcb->readonly)
599 
601  return STATUS_ACCESS_DENIED;
602 
603  if (!is_file_name_valid(&nameus, posix, false))
605 
606  utf8.Buffer = NULL;
607 
608  Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length);
609  if (!NT_SUCCESS(Status)) {
610  ERR("utf16_to_utf8 failed with error %08x\n", Status);
611  return Status;
612  }
613 
614  if (len == 0) {
615  ERR("utf16_to_utf8 returned a length of 0\n");
616  return STATUS_INTERNAL_ERROR;
617  }
618 
619  if (len > 0xffff) {
620  ERR("len was too long\n");
622  }
623 
624  utf8.MaximumLength = utf8.Length = (USHORT)len;
626 
627  if (!utf8.Buffer) {
628  ERR("out of memory\n");
630  }
631 
632  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
633  if (!NT_SUCCESS(Status)) {
634  ERR("utf16_to_utf8 failed with error %08x\n", Status);
635  goto end2;
636  }
637 
638  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
639 
640  // no need for fcb_lock as we have tree_lock exclusively
641  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || posix, Irp);
642 
643  if (NT_SUCCESS(Status)) {
644  if (!fr2->deleted) {
645  WARN("file already exists\n");
646  free_fileref(fr2);
648  goto end3;
649  } else
650  free_fileref(fr2);
652  ERR("open_fileref returned %08x\n", Status);
653  goto end3;
654  }
655 
656  Status = ObReferenceObjectByHandle(subvolh, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&subvol_obj, NULL);
657  if (!NT_SUCCESS(Status)) {
658  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
659  goto end3;
660  }
661 
662  if (subvol_obj->DeviceObject != FileObject->DeviceObject) {
664  goto end;
665  }
666 
667  subvol_fcb = subvol_obj->FsContext;
668  if (!subvol_fcb) {
670  goto end;
671  }
672 
673  if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) {
674  WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid);
676  goto end;
677  }
678 
679  ccb = subvol_obj->FsContext2;
680 
681  if (!ccb) {
683  goto end;
684  }
685 
686  if (!(ccb->access & FILE_TRAVERSE)) {
687  WARN("insufficient privileges\n");
689  goto end;
690  }
691 
692  if (fcb == Vcb->dummy_fcb) {
694  goto end;
695  }
696 
697  // clear unique flag on extents of open files in subvol
698  if (!IsListEmpty(&subvol_fcb->subvol->fcbs)) {
699  LIST_ENTRY* le = subvol_fcb->subvol->fcbs.Flink;
700 
701  while (le != &subvol_fcb->subvol->fcbs) {
702  struct _fcb* openfcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
703  LIST_ENTRY* le2;
704 
705  le2 = openfcb->extents.Flink;
706 
707  while (le2 != &openfcb->extents) {
709 
710  ext->unique = false;
711 
712  le2 = le2->Flink;
713  }
714 
715  le = le->Flink;
716  }
717  }
718 
719  Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, readonly, Irp);
720 
721  if (NT_SUCCESS(Status)) {
722  file_ref* fr;
723 
724  Status = open_fileref(Vcb, &fr, &nameus, fileref, false, NULL, NULL, PagedPool, false, Irp);
725 
726  if (!NT_SUCCESS(Status)) {
727  ERR("open_fileref returned %08x\n", Status);
729  } else {
731  free_fileref(fr);
732  }
733  }
734 
735 end:
736  ObDereferenceObject(subvol_obj);
737 
738 end3:
739  ExReleaseResourceLite(&Vcb->tree_lock);
740 
741 end2:
742  ExFreePool(utf8.Buffer);
743 
744  return Status;
745 }
746 
748  btrfs_create_subvol* bcs;
749  fcb *fcb, *rootfcb = NULL;
750  ccb* ccb;
751  file_ref* fileref;
753  uint64_t id;
754  root* r = NULL;
756  BTRFS_TIME now;
757  ULONG len;
758  uint16_t irsize;
759  UNICODE_STRING nameus;
760  ANSI_STRING utf8;
761  INODE_REF* ir;
762  KEY searchkey;
764  SECURITY_SUBJECT_CONTEXT subjcont;
765  PSID owner;
766  BOOLEAN defaulted;
767  uint64_t* root_num;
768  file_ref *fr = NULL, *fr2;
769  dir_child* dc = NULL;
770 
771  fcb = FileObject->FsContext;
772  if (!fcb) {
773  ERR("error - fcb was NULL\n");
774  return STATUS_INTERNAL_ERROR;
775  }
776 
777  ccb = FileObject->FsContext2;
778  if (!ccb) {
779  ERR("error - ccb was NULL\n");
780  return STATUS_INTERNAL_ERROR;
781  }
782 
783  fileref = ccb->fileref;
784 
785  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
786  ERR("parent FCB was not a directory\n");
787  return STATUS_NOT_A_DIRECTORY;
788  }
789 
790  if (!fileref) {
791  ERR("fileref was NULL\n");
793  }
794 
795  if (fileref->deleted || fcb->deleted) {
796  ERR("parent has been deleted\n");
797  return STATUS_FILE_DELETED;
798  }
799 
800  if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) {
801  WARN("insufficient privileges\n");
802  return STATUS_ACCESS_DENIED;
803  }
804 
805  if (Vcb->readonly)
807 
809  return STATUS_ACCESS_DENIED;
810 
811  if (fcb == Vcb->dummy_fcb)
812  return STATUS_ACCESS_DENIED;
813 
814  if (!data || datalen < sizeof(btrfs_create_subvol))
816 
817  bcs = (btrfs_create_subvol*)data;
818 
819  if (offsetof(btrfs_create_subvol, name[0]) + bcs->namelen > datalen)
821 
822  nameus.Length = nameus.MaximumLength = bcs->namelen;
823  nameus.Buffer = bcs->name;
824 
825  if (!is_file_name_valid(&nameus, bcs->posix, false))
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 %08x\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 %08x\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 %08x\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 %08x\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 %08x\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 %08x\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 = 0xffffffff80000000; // FIXME - find out what these mean
940 
941  if (bcs->readonly)
942  r->root_item.flags |= BTRFS_SUBVOL_READONLY;
943 
944  r->root_item.objid = SUBVOL_ROOT_INODE;
945  r->root_item.bytes_used = Vcb->superblock.node_size;
946  r->root_item.ctransid = Vcb->superblock.generation;
947  r->root_item.otransid = Vcb->superblock.generation;
948  r->root_item.ctime = now;
949  r->root_item.otime = now;
950 
951  // add .. inode to new subvol
952 
953  rootfcb = create_fcb(Vcb, PagedPool);
954  if (!rootfcb) {
955  ERR("out of memory\n");
957  goto end;
958  }
959 
960  rootfcb->Vcb = Vcb;
961 
962  rootfcb->subvol = r;
963  rootfcb->inode = SUBVOL_ROOT_INODE;
964  rootfcb->type = BTRFS_TYPE_DIRECTORY;
965 
966  rootfcb->inode_item.generation = Vcb->superblock.generation;
967  rootfcb->inode_item.transid = Vcb->superblock.generation;
968  rootfcb->inode_item.st_nlink = 1;
969  rootfcb->inode_item.st_mode = __S_IFDIR | inherit_mode(fileref->fcb, true);
970  rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
971  rootfcb->inode_item.st_gid = GID_NOBODY;
972 
973  rootfcb->atts = get_file_attributes(Vcb, rootfcb->subvol, rootfcb->inode, rootfcb->type, false, true, Irp);
974 
975  if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
976  rootfcb->atts |= FILE_ATTRIBUTE_READONLY;
977 
978  SeCaptureSubjectContext(&subjcont);
979 
980  Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
981 
982  if (!NT_SUCCESS(Status)) {
983  ERR("SeAssignSecurity returned %08x\n", Status);
984  goto end;
985  }
986 
987  if (!rootfcb->sd) {
988  ERR("SeAssignSecurity returned NULL security descriptor\n");
990  goto end;
991  }
992 
993  Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted);
994  if (!NT_SUCCESS(Status)) {
995  ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
996  rootfcb->inode_item.st_uid = UID_NOBODY;
997  rootfcb->sd_dirty = true;
998  } else {
999  rootfcb->inode_item.st_uid = sid_to_uid(owner);
1000  rootfcb->sd_dirty = rootfcb->inode_item.st_uid == UID_NOBODY;
1001  }
1002 
1003  find_gid(rootfcb, fileref->fcb, &subjcont);
1004 
1005  rootfcb->inode_item_changed = true;
1006 
1007  acquire_fcb_lock_exclusive(Vcb);
1008  InsertTailList(&r->fcbs, &rootfcb->list_entry);
1009  InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
1010  r->fcbs_version++;
1011  release_fcb_lock(Vcb);
1012 
1013  rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1014  rootfcb->Header.AllocationSize.QuadPart = 0;
1015  rootfcb->Header.FileSize.QuadPart = 0;
1016  rootfcb->Header.ValidDataLength.QuadPart = 0;
1017 
1018  rootfcb->created = true;
1019 
1020  if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1022 
1023  rootfcb->prop_compression = fileref->fcb->prop_compression;
1025 
1026  r->lastinode = rootfcb->inode;
1027 
1028  // add INODE_REF
1029 
1030  irsize = (uint16_t)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1031  ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1032  if (!ir) {
1033  ERR("out of memory\n");
1035  goto end;
1036  }
1037 
1038  ir->index = 0;
1039  ir->n = sizeof(DOTDOT) - 1;
1040  RtlCopyMemory(ir->name, DOTDOT, ir->n);
1041 
1042  Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1043  if (!NT_SUCCESS(Status)) {
1044  ERR("insert_tree_item returned %08x\n", Status);
1045  ExFreePool(ir);
1046  goto end;
1047  }
1048 
1049  // create fileref for entry in other subvolume
1050 
1051  fr = create_fileref(Vcb);
1052  if (!fr) {
1053  ERR("out of memory\n");
1054 
1055  reap_fcb(rootfcb);
1056 
1058  goto end;
1059  }
1060 
1061  fr->fcb = rootfcb;
1062 
1063  mark_fcb_dirty(rootfcb);
1064 
1065  fr->parent = fileref;
1066 
1067  Status = add_dir_child(fileref->fcb, r->id, true, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1068  if (!NT_SUCCESS(Status))
1069  WARN("add_dir_child returned %08x\n", Status);
1070 
1071  fr->dc = dc;
1072  dc->fileref = fr;
1073 
1075  if (!fr->fcb->hash_ptrs) {
1076  ERR("out of memory\n");
1077  free_fileref(fr);
1079  goto end;
1080  }
1081 
1082  RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1083 
1085  if (!fr->fcb->hash_ptrs_uc) {
1086  ERR("out of memory\n");
1087  free_fileref(fr);
1089  goto end;
1090  }
1091 
1092  RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1093 
1094  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true);
1095  InsertTailList(&fileref->children, &fr->list_entry);
1096  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1097 
1099 
1100  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1101  fr->fcb->fileref = fr;
1102 
1103  fr->created = true;
1104  mark_fileref_dirty(fr);
1105 
1106  // change fcb->subvol's ROOT_ITEM
1107 
1108  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1109  fcb->subvol->root_item.ctime = now;
1110 
1111  // change fcb's INODE_ITEM
1112 
1113  fcb->inode_item.transid = Vcb->superblock.generation;
1114  fcb->inode_item.st_size += utf8.Length * 2;
1115  fcb->inode_item.sequence++;
1116 
1117  if (!ccb->user_set_change_time)
1119 
1120  if (!ccb->user_set_write_time)
1122 
1123  fcb->inode_item_changed = true;
1125 
1126  fr->fcb->subvol->parent = fcb->subvol->id;
1127 
1129 
1130 end:
1131  if (!NT_SUCCESS(Status)) {
1132  if (fr) {
1133  fr->deleted = true;
1134  mark_fileref_dirty(fr);
1135  } else if (rootfcb) {
1136  rootfcb->deleted = true;
1137  mark_fcb_dirty(rootfcb);
1138  }
1139 
1140  if (r) {
1141  RemoveEntryList(&r->list_entry);
1142  InsertTailList(&Vcb->drop_roots, &r->list_entry);
1143  }
1144  }
1145 
1146  ExReleaseResourceLite(&Vcb->tree_lock);
1147 
1148  if (NT_SUCCESS(Status)) {
1151  }
1152 
1153 end2:
1154  if (fr)
1155  free_fileref(fr);
1156 
1157  return Status;
1158 }
1159 
1161  btrfs_inode_info* bii = data;
1162  fcb* fcb;
1163  ccb* ccb;
1164  bool old_style;
1165 
1166  if (length < offsetof(btrfs_inode_info, disk_size_zstd))
1167  return STATUS_BUFFER_OVERFLOW;
1168 
1169  if (!FileObject)
1170  return STATUS_INVALID_PARAMETER;
1171 
1172  fcb = FileObject->FsContext;
1173 
1174  if (!fcb)
1175  return STATUS_INVALID_PARAMETER;
1176 
1177  ccb = FileObject->FsContext2;
1178 
1179  if (!ccb)
1180  return STATUS_INVALID_PARAMETER;
1181 
1182  if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1183  WARN("insufficient privileges\n");
1184  return STATUS_ACCESS_DENIED;
1185  }
1186 
1187  if (fcb->ads)
1188  fcb = ccb->fileref->parent->fcb;
1189 
1190  old_style = length < offsetof(btrfs_inode_info, sparse_size) + sizeof(((btrfs_inode_info*)NULL)->sparse_size);
1191 
1192  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
1193 
1194  bii->subvol = fcb->subvol->id;
1195  bii->inode = fcb->inode;
1196  bii->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false;
1197  bii->type = fcb->type;
1198  bii->st_uid = fcb->inode_item.st_uid;
1199  bii->st_gid = fcb->inode_item.st_gid;
1200  bii->st_mode = fcb->inode_item.st_mode;
1201 
1202  if (fcb->inode_item.st_rdev == 0)
1203  bii->st_rdev = 0;
1204  else
1205  bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1206 
1207  bii->flags = fcb->inode_item.flags;
1208 
1209  bii->inline_length = 0;
1210  bii->disk_size_uncompressed = 0;
1211  bii->disk_size_zlib = 0;
1212  bii->disk_size_lzo = 0;
1213 
1214  if (!old_style) {
1215  bii->disk_size_zstd = 0;
1216  bii->sparse_size = 0;
1217  }
1218 
1219  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1220  uint64_t last_end = 0;
1221  LIST_ENTRY* le;
1222  bool extents_inline = false;
1223 
1224  le = fcb->extents.Flink;
1225  while (le != &fcb->extents) {
1227 
1228  if (!ext->ignore) {
1229  if (!old_style && ext->offset > last_end)
1230  bii->sparse_size += ext->offset - last_end;
1231 
1232  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1233  bii->inline_length += ext->datalen - (uint16_t)offsetof(EXTENT_DATA, data[0]);
1234  last_end = ext->offset + ext->extent_data.decoded_size;
1235  extents_inline = true;
1236  } else {
1237  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1238 
1239  // FIXME - compressed extents with a hole in them are counted more than once
1240  if (ed2->size != 0) {
1241  switch (ext->extent_data.compression) {
1244  break;
1245 
1247  bii->disk_size_zlib += ed2->size;
1248  break;
1249 
1250  case BTRFS_COMPRESSION_LZO:
1251  bii->disk_size_lzo += ed2->size;
1252  break;
1253 
1255  if (!old_style)
1256  bii->disk_size_zstd += ed2->size;
1257  break;
1258  }
1259  }
1260 
1261  last_end = ext->offset + ed2->num_bytes;
1262  }
1263  }
1264 
1265  le = le->Flink;
1266  }
1267 
1268  if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1269  bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1270 
1271  if (length >= offsetof(btrfs_inode_info, num_extents) + sizeof(((btrfs_inode_info*)NULL)->num_extents)) {
1272  EXTENT_DATA2* last_ed2 = NULL;
1273 
1274  le = fcb->extents.Flink;
1275 
1276  bii->num_extents = 0;
1277 
1278  while (le != &fcb->extents) {
1280 
1281  if (!ext->ignore && ext->extent_data.type != EXTENT_TYPE_INLINE) {
1282  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1283 
1284  if (ed2->size != 0) {
1285  if (!last_ed2 || ed2->offset != last_ed2->offset + last_ed2->num_bytes)
1286  bii->num_extents++;
1287 
1288  last_ed2 = ed2;
1289  } else
1290  last_ed2 = NULL;
1291  }
1292 
1293  le = le->Flink;
1294  }
1295  }
1296  }
1297 
1298  switch (fcb->prop_compression) {
1299  case PropCompression_Zlib:
1301  break;
1302 
1303  case PropCompression_LZO:
1305  break;
1306 
1307  case PropCompression_ZSTD:
1309  break;
1310 
1311  default:
1313  break;
1314  }
1315 
1316  ExReleaseResourceLite(fcb->Header.Resource);
1317 
1318  return STATUS_SUCCESS;
1319 }
1320 
1322  btrfs_set_inode_info* bsii = data;
1323  NTSTATUS Status;
1324  fcb* fcb;
1325  ccb* ccb;
1326 
1327  if (length < sizeof(btrfs_set_inode_info))
1328  return STATUS_INVALID_PARAMETER;
1329 
1330  if (!FileObject)
1331  return STATUS_INVALID_PARAMETER;
1332 
1333  fcb = FileObject->FsContext;
1334 
1335  if (!fcb)
1336  return STATUS_INVALID_PARAMETER;
1337 
1338  ccb = FileObject->FsContext2;
1339 
1340  if (!ccb)
1341  return STATUS_INVALID_PARAMETER;
1342 
1343  if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1344  WARN("insufficient privileges\n");
1345  return STATUS_ACCESS_DENIED;
1346  }
1347 
1348  if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1349  WARN("insufficient privileges\n");
1350  return STATUS_ACCESS_DENIED;
1351  }
1352 
1354  return STATUS_INVALID_PARAMETER;
1355 
1356  if (fcb->ads)
1357  fcb = ccb->fileref->parent->fcb;
1358 
1359  if (is_subvol_readonly(fcb->subvol, Irp)) {
1360  WARN("trying to change inode on readonly subvolume\n");
1361  return STATUS_ACCESS_DENIED;
1362  }
1363 
1364  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1365 
1366  if (bsii->flags_changed) {
1367  if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1369  WARN("trying to change nocow flag on non-empty file\n");
1371  goto end;
1372  }
1373 
1374  fcb->inode_item.flags = bsii->flags;
1375 
1378  else
1380  }
1381 
1382  if (bsii->mode_changed) {
1384  S_ISGID | S_ISVTX;
1385 
1386  if (ccb->access & WRITE_OWNER)
1387  allowed |= S_ISUID;
1388 
1389  fcb->inode_item.st_mode &= ~allowed;
1390  fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1391  }
1392 
1393  if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1394  fcb->inode_item.st_uid = bsii->st_uid;
1395 
1396  fcb->sd_dirty = true;
1397  fcb->sd_deleted = false;
1398  }
1399 
1400  if (bsii->gid_changed)
1401  fcb->inode_item.st_gid = bsii->st_gid;
1402 
1403  if (bsii->compression_type_changed) {
1404  switch (bsii->compression_type) {
1405  case BTRFS_COMPRESSION_ANY:
1407  break;
1408 
1411  break;
1412 
1413  case BTRFS_COMPRESSION_LZO:
1415  break;
1416 
1419  break;
1420  }
1421 
1422  fcb->prop_compression_changed = true;
1423  }
1424 
1425  if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1426  fcb->inode_item_changed = true;
1428  }
1429 
1431 
1432 end:
1433  ExReleaseResourceLite(fcb->Header.Resource);
1434 
1435  return Status;
1436 }
1437 
1439  btrfs_device* dev = NULL;
1440  NTSTATUS Status;
1441  LIST_ENTRY* le;
1442 
1443  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1444 
1445  le = Vcb->devices.Flink;
1446  while (le != &Vcb->devices) {
1447  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1448  ULONG structlen;
1449 
1450  if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1452  goto end;
1453  }
1454 
1455  if (!dev)
1456  dev = data;
1457  else {
1458  dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1459  dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry);
1460  }
1461 
1462  structlen = length - offsetof(btrfs_device, namelen);
1463 
1464  if (dev2->devobj) {
1465  Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL);
1466  if (!NT_SUCCESS(Status))
1467  goto end;
1468 
1469  dev->missing = false;
1470  } else {
1471  dev->namelen = 0;
1472  dev->missing = true;
1473  }
1474 
1475  dev->next_entry = 0;
1476  dev->dev_id = dev2->devitem.dev_id;
1477  dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false;
1478  dev->device_number = dev2->disk_num;
1479  dev->partition_number = dev2->part_num;
1480  dev->size = dev2->devitem.num_bytes;
1481 
1482  if (dev2->devobj) {
1484 
1485  Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL);
1486  if (!NT_SUCCESS(Status))
1487  goto end;
1488 
1489  dev->max_size = gli.Length.QuadPart;
1490  } else
1491  dev->max_size = dev->size;
1492 
1493  RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5);
1494 
1495  length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1496 
1497  le = le->Flink;
1498  }
1499 
1500 end:
1501  ExReleaseResourceLite(&Vcb->tree_lock);
1502 
1503  return Status;
1504 }
1505 
1508  btrfs_usage* lastbue = NULL;
1509  NTSTATUS Status;
1510  LIST_ENTRY* le;
1511 
1512  if (length < sizeof(btrfs_usage))
1513  return STATUS_BUFFER_OVERFLOW;
1514 
1515  if (!Vcb->chunk_usage_found) {
1516  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1517 
1518  if (!Vcb->chunk_usage_found)
1520  else
1522 
1523  ExReleaseResourceLite(&Vcb->tree_lock);
1524 
1525  if (!NT_SUCCESS(Status)) {
1526  ERR("find_chunk_usage returned %08x\n", Status);
1527  return Status;
1528  }
1529  }
1530 
1532 
1533  ExAcquireResourceSharedLite(&Vcb->chunk_lock, true);
1534 
1535  le = Vcb->chunks.Flink;
1536  while (le != &Vcb->chunks) {
1537  bool addnew = false;
1538 
1540 
1541  if (!lastbue) // first entry
1542  addnew = true;
1543  else {
1544  btrfs_usage* bue = usage;
1545 
1546  addnew = true;
1547 
1548  while (true) {
1549  if (bue->type == c->chunk_item->type) {
1550  addnew = false;
1551  break;
1552  }
1553 
1554  if (bue->next_entry == 0)
1555  break;
1556  else
1557  bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry);
1558  }
1559  }
1560 
1561  if (addnew) {
1562  btrfs_usage* bue;
1563  LIST_ENTRY* le2;
1564  uint64_t factor;
1565 
1566  if (!lastbue) {
1567  bue = usage;
1568  } else {
1569  if (length < offsetof(btrfs_usage, devices)) {
1571  goto end;
1572  }
1573 
1575 
1576  lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1577 
1578  bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry);
1579  }
1580 
1581  bue->next_entry = 0;
1582  bue->type = c->chunk_item->type;
1583  bue->size = 0;
1584  bue->used = 0;
1585  bue->num_devices = 0;
1586 
1587  if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1588  factor = c->chunk_item->num_stripes;
1589  else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1590  factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1591  else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1592  factor = c->chunk_item->num_stripes - 1;
1593  else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1594  factor = c->chunk_item->num_stripes - 2;
1595  else
1596  factor = 1;
1597 
1598  le2 = le;
1599  while (le2 != &Vcb->chunks) {
1600  chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1601 
1602  if (c2->chunk_item->type == c->chunk_item->type) {
1603  uint16_t i;
1605  uint64_t stripesize;
1606 
1607  bue->size += c2->chunk_item->size;
1608  bue->used += c2->used;
1609 
1610  stripesize = c2->chunk_item->size / factor;
1611 
1612  for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1613  uint64_t j;
1614  bool found = false;
1615 
1616  for (j = 0; j < bue->num_devices; j++) {
1617  if (bue->devices[j].dev_id == cis[i].dev_id) {
1618  bue->devices[j].alloc += stripesize;
1619  found = true;
1620  break;
1621  }
1622  }
1623 
1624  if (!found) {
1625  if (length < sizeof(btrfs_usage_device)) {
1627  goto end;
1628  }
1629 
1630  length -= sizeof(btrfs_usage_device);
1631 
1632  bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1633  bue->devices[bue->num_devices].alloc = stripesize;
1634  bue->num_devices++;
1635  }
1636  }
1637  }
1638 
1639  le2 = le2->Flink;
1640  }
1641 
1642  lastbue = bue;
1643  }
1644 
1645  le = le->Flink;
1646  }
1647 
1649 
1650 end:
1651  ExReleaseResourceLite(&Vcb->chunk_lock);
1652 
1653  return Status;
1654 }
1655 
1657  NTSTATUS Status;
1658  ULONG cc;
1660  bool verify = false;
1661  LIST_ENTRY* le;
1662 
1663  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1664 
1665  le = Vcb->devices.Flink;
1666  while (le != &Vcb->devices) {
1668 
1669  if (dev->devobj && dev->removable) {
1670  Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb);
1671 
1672  if (iosb.Information != sizeof(ULONG))
1673  cc = 0;
1674 
1675  if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1676  dev->devobj->Flags |= DO_VERIFY_VOLUME;
1677  verify = true;
1678  }
1679 
1680  if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1681  dev->change_count = cc;
1682 
1683  if (!NT_SUCCESS(Status) || verify) {
1685  ExReleaseResourceLite(&Vcb->tree_lock);
1686 
1687  return verify ? STATUS_VERIFY_REQUIRED : Status;
1688  }
1689  }
1690 
1691  le = le->Flink;
1692  }
1693 
1694  ExReleaseResourceLite(&Vcb->tree_lock);
1695 
1696  return STATUS_SUCCESS;
1697 }
1698 
1699 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1700  FILESYSTEM_STATISTICS* fss;
1701 
1702  WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1703 
1704  // This is hideously wrong, but at least it stops SMB from breaking
1705 
1706  if (buflen < sizeof(FILESYSTEM_STATISTICS))
1707  return STATUS_BUFFER_TOO_SMALL;
1708 
1709  fss = buffer;
1710  RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1711 
1712  fss->Version = 1;
1715 
1716  *retlen = sizeof(FILESYSTEM_STATISTICS);
1717 
1718  return STATUS_SUCCESS;
1719 }
1720 
1722  FILE_SET_SPARSE_BUFFER* fssb = data;
1723  NTSTATUS Status;
1724  bool set;
1725  fcb* fcb;
1726  ccb* ccb = FileObject->FsContext2;
1727  file_ref* fileref = ccb ? ccb->fileref : NULL;
1728 
1729  if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1730  return STATUS_INVALID_PARAMETER;
1731 
1732  if (!FileObject) {
1733  ERR("FileObject was NULL\n");
1734  return STATUS_INVALID_PARAMETER;
1735  }
1736 
1737  fcb = FileObject->FsContext;
1738 
1739  if (!fcb) {
1740  ERR("FCB was NULL\n");
1741  return STATUS_INVALID_PARAMETER;
1742  }
1743 
1744  if (!ccb) {
1745  ERR("CCB was NULL\n");
1746  return STATUS_INVALID_PARAMETER;
1747  }
1748 
1749  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1750  WARN("insufficient privileges\n");
1751  return STATUS_ACCESS_DENIED;
1752  }
1753 
1754  if (!fileref) {
1755  ERR("no fileref\n");
1756  return STATUS_INVALID_PARAMETER;
1757  }
1758 
1759  if (fcb->ads) {
1760  fileref = fileref->parent;
1761  fcb = fileref->fcb;
1762  }
1763 
1764  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1765  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1766 
1767  if (fcb->type != BTRFS_TYPE_FILE) {
1768  WARN("FileObject did not point to a file\n");
1770  goto end;
1771  }
1772 
1773  if (fssb)
1774  set = fssb->SetSparse;
1775  else
1776  set = true;
1777 
1778  if (set) {
1780  fcb->atts_changed = true;
1781  } else {
1782  ULONG defda;
1783 
1785  fcb->atts_changed = true;
1786 
1788  fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
1789 
1790  fcb->atts_deleted = defda == fcb->atts;
1791  }
1792 
1795 
1797 
1798 end:
1799  ExReleaseResourceLite(fcb->Header.Resource);
1800  ExReleaseResourceLite(&Vcb->tree_lock);
1801 
1802  return Status;
1803 }
1804 
1806  NTSTATUS Status;
1807  bool make_inline, compress;
1808  uint64_t start_data, end_data;
1809  ULONG buf_head;
1810  uint8_t* data;
1811 
1812  make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1813 
1814  if (!make_inline)
1816 
1817  if (make_inline) {
1818  start_data = 0;
1819  end_data = fcb->inode_item.st_size;
1820  buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1821  } else if (compress) {
1824  sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1825  buf_head = 0;
1826  } else {
1827  start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1);
1828  end_data = sector_align(start + length, Vcb->superblock.sector_size);
1829  buf_head = 0;
1830  }
1831 
1832  data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1833  if (!data) {
1834  ERR("out of memory\n");
1836  }
1837 
1838  RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1839 
1840  if (start > start_data || start + length < end_data) {
1841  Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1842 
1843  if (!NT_SUCCESS(Status)) {
1844  ERR("read_file returned %08x\n", Status);
1845  ExFreePool(data);
1846  return Status;
1847  }
1848  }
1849 
1850  RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1851 
1852  if (make_inline) {
1853  uint16_t edsize;
1855 
1856  Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1857  if (!NT_SUCCESS(Status)) {
1858  ERR("excise_extents returned %08x\n", Status);
1859  ExFreePool(data);
1860  return Status;
1861  }
1862 
1863  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data);
1864 
1865  ed->generation = Vcb->superblock.generation;
1866  ed->decoded_size = end_data;
1871 
1872  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback);
1873  if (!NT_SUCCESS(Status)) {
1874  ERR("add_extent_to_fcb returned %08x\n", Status);
1875  ExFreePool(data);
1876  return Status;
1877  }
1878 
1879  ExFreePool(data);
1880 
1881  fcb->inode_item.st_blocks += end_data;
1882  } else if (compress) {
1884 
1885  ExFreePool(data);
1886 
1887  if (!NT_SUCCESS(Status)) {
1888  ERR("write_compressed returned %08x\n", Status);
1889  return Status;
1890  }
1891  } else {
1892  Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback);
1893 
1894  ExFreePool(data);
1895 
1896  if (!NT_SUCCESS(Status)) {
1897  ERR("do_write_file returned %08x\n", Status);
1898  return Status;
1899  }
1900  }
1901 
1902  return STATUS_SUCCESS;
1903 }
1904 
1906  FILE_ZERO_DATA_INFORMATION* fzdi = data;
1907  NTSTATUS Status;
1908  fcb* fcb;
1909  ccb* ccb;
1910  file_ref* fileref;
1911  LIST_ENTRY rollback, *le;
1913  BTRFS_TIME now;
1914  uint64_t start, end;
1915  extent* ext;
1917 
1918  if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1919  return STATUS_INVALID_PARAMETER;
1920 
1921  if (!FileObject) {
1922  ERR("FileObject was NULL\n");
1923  return STATUS_INVALID_PARAMETER;
1924  }
1925 
1926  if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1927  WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1928  return STATUS_INVALID_PARAMETER;
1929  }
1930 
1931  fcb = FileObject->FsContext;
1932 
1933  if (!fcb) {
1934  ERR("FCB was NULL\n");
1935  return STATUS_INVALID_PARAMETER;
1936  }
1937 
1938  ccb = FileObject->FsContext2;
1939 
1940  if (!ccb) {
1941  ERR("ccb was NULL\n");
1942  return STATUS_INVALID_PARAMETER;
1943  }
1944 
1945  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1946  WARN("insufficient privileges\n");
1947  return STATUS_ACCESS_DENIED;
1948  }
1949 
1950  fileref = ccb->fileref;
1951 
1952  if (!fileref) {
1953  ERR("fileref was NULL\n");
1954  return STATUS_INVALID_PARAMETER;
1955  }
1956 
1958 
1959  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1960  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
1961 
1962  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
1963 
1964  if (fcb->type != BTRFS_TYPE_FILE) {
1965  WARN("FileObject did not point to a file\n");
1967  goto end;
1968  }
1969 
1970  if (fcb->ads) {
1971  ERR("FileObject is stream\n");
1973  goto end;
1974  }
1975 
1976  if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1978  goto end;
1979  }
1980 
1981  ext = NULL;
1982  le = fcb->extents.Flink;
1983  while (le != &fcb->extents) {
1985 
1986  if (!ext2->ignore) {
1987  ext = ext2;
1988  break;
1989  }
1990 
1991  le = le->Flink;
1992  }
1993 
1994  if (!ext) {
1996  goto end;
1997  }
1998 
1999  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
2000  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2001  if (!NT_SUCCESS(Status)) {
2002  ERR("zero_data returned %08x\n", Status);
2003  goto end;
2004  }
2005  } else {
2006  start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
2007 
2008  if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
2009  end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
2010  else
2011  end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
2012 
2013  if (end <= start) {
2014  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
2015  if (!NT_SUCCESS(Status)) {
2016  ERR("zero_data returned %08x\n", Status);
2017  goto end;
2018  }
2019  } else {
2020  if (start > (uint64_t)fzdi->FileOffset.QuadPart) {
2021  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
2022  if (!NT_SUCCESS(Status)) {
2023  ERR("zero_data returned %08x\n", Status);
2024  goto end;
2025  }
2026  }
2027 
2028  if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) {
2029  Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2030  if (!NT_SUCCESS(Status)) {
2031  ERR("zero_data returned %08x\n", Status);
2032  goto end;
2033  }
2034  }
2035 
2036  if (end > start) {
2038  if (!NT_SUCCESS(Status)) {
2039  ERR("excise_extents returned %08x\n", Status);
2040  goto end;
2041  }
2042  }
2043  }
2044  }
2045 
2046  CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false);
2047 
2050 
2051  fcb->inode_item.transid = Vcb->superblock.generation;
2052  fcb->inode_item.sequence++;
2053 
2054  if (!ccb->user_set_change_time)
2056 
2057  if (!ccb->user_set_write_time)
2059 
2060  fcb->extents_changed = true;
2061  fcb->inode_item_changed = true;
2063 
2065 
2066  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2067  fcb->subvol->root_item.ctime = now;
2068 
2070 
2071 end:
2072  if (!NT_SUCCESS(Status))
2074  else
2076 
2077  ExReleaseResourceLite(fcb->Header.Resource);
2078  ExReleaseResourceLite(&Vcb->tree_lock);
2079 
2080  return Status;
2081 }
2082 
2083 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2084  NTSTATUS Status;
2085  fcb* fcb;
2086  LIST_ENTRY* le;
2087  FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2088  ULONG i = 0;
2089  uint64_t last_start, last_end;
2090 
2091  TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2092 
2093  if (!FileObject) {
2094  ERR("FileObject was NULL\n");
2095  return STATUS_INVALID_PARAMETER;
2096  }
2097 
2098  if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2099  return STATUS_INVALID_PARAMETER;
2100 
2101  fcb = FileObject->FsContext;
2102 
2103  if (!fcb) {
2104  ERR("FCB was NULL\n");
2105  return STATUS_INVALID_PARAMETER;
2106  }
2107 
2108  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2109 
2110  // If file is not marked as sparse, claim the whole thing as an allocated range
2111 
2112  if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2113  if (fcb->inode_item.st_size == 0)
2115  else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2117  else {
2118  ranges[i].FileOffset.QuadPart = 0;
2119  ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2120  i++;
2122  }
2123 
2124  goto end;
2125 
2126  }
2127 
2128  le = fcb->extents.Flink;
2129 
2130  last_start = 0;
2131  last_end = 0;
2132 
2133  while (le != &fcb->extents) {
2135 
2136  if (!ext->ignore) {
2137  EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2138  uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2139 
2140  if (ext->offset > last_end) { // first extent after a hole
2141  if (last_end > last_start) {
2142  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2143  ranges[i].FileOffset.QuadPart = last_start;
2144  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2145  i++;
2146  } else {
2148  goto end;
2149  }
2150  }
2151 
2152  last_start = ext->offset;
2153  }
2154 
2155  last_end = ext->offset + len;
2156  }
2157 
2158  le = le->Flink;
2159  }
2160 
2161  if (last_end > last_start) {
2162  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2163  ranges[i].FileOffset.QuadPart = last_start;
2164  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2165  i++;
2166  } else {
2168  goto end;
2169  }
2170  }
2171 
2173 
2174 end:
2175  *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2176 
2177  ExReleaseResourceLite(fcb->Header.Resource);
2178 
2179  return Status;
2180 }
2181 
2182 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2183  fcb* fcb;
2184 
2185  TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen);
2186 
2187  if (!FileObject) {
2188  ERR("FileObject was NULL\n");
2189  return STATUS_INVALID_PARAMETER;
2190  }
2191 
2192  if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2193  return STATUS_INVALID_PARAMETER;
2194 
2195  fcb = FileObject->FsContext;
2196 
2197  if (!fcb) {
2198  ERR("FCB was NULL\n");
2199  return STATUS_INVALID_PARAMETER;
2200  }
2201 
2202  ExAcquireResourceSharedLite(fcb->Header.Resource, true);
2203 
2204  RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t));
2205  RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
2206 
2207  ExReleaseResourceLite(fcb->Header.Resource);
2208 
2209  RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2210 
2211  *retlen = sizeof(FILE_OBJECTID_BUFFER);
2212 
2213  return STATUS_SUCCESS;
2214 }
2215 
2217  LIST_ENTRY* le;
2218 
2219  le = Vcb->all_fcbs.Flink;
2220  while (le != &Vcb->all_fcbs) {
2221  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2223 
2224  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2225  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2226 
2227  le = le->Flink;
2228  }
2229 }
2230 
2233  NTSTATUS Status;
2234  KIRQL irql;
2235  bool lock_paused_balance = false;
2236 
2237  TRACE("FSCTL_LOCK_VOLUME\n");
2238 
2239  if (Vcb->scrub.thread) {
2240  WARN("cannot lock while scrub running\n");
2241  return STATUS_DEVICE_NOT_READY;
2242  }
2243 
2244  if (Vcb->balance.thread) {
2245  WARN("cannot lock while balance running\n");
2246  return STATUS_DEVICE_NOT_READY;
2247  }
2248 
2249  TRACE("locking volume\n");
2250 
2252 
2253  if (Vcb->locked)
2254  return STATUS_SUCCESS;
2255 
2256  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2257 
2258  if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2260  ExReleaseResourceLite(&Vcb->fileref_lock);
2261  goto end;
2262  }
2263 
2264  ExReleaseResourceLite(&Vcb->fileref_lock);
2265 
2266  if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2267  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2268  KeClearEvent(&Vcb->balance.event);
2269  ExReleaseResourceLite(&Vcb->tree_lock);
2270 
2271  lock_paused_balance = true;
2272  }
2273 
2274  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2275 
2277 
2278  if (Vcb->need_write && !Vcb->readonly)
2279  Status = do_write(Vcb, Irp);
2280  else
2282 
2283  free_trees(Vcb);
2284 
2285  ExReleaseResourceLite(&Vcb->tree_lock);
2286 
2287  if (!NT_SUCCESS(Status)) {
2288  ERR("do_write returned %08x\n", Status);
2289  goto end;
2290  }
2291 
2293 
2294  if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2295  Vcb->Vpb->Flags |= VPB_LOCKED;
2296  Vcb->locked = true;
2297  Vcb->locked_fileobj = IrpSp->FileObject;
2298  Vcb->lock_paused_balance = lock_paused_balance;
2299  } else {
2302 
2303  if (lock_paused_balance)
2304  KeSetEvent(&Vcb->balance.event, 0, false);
2305 
2306  goto end;
2307  }
2308 
2310 
2312 
2313 end:
2314  if (!NT_SUCCESS(Status))
2316 
2317  return Status;
2318 }
2319 
2321  KIRQL irql;
2322 
2324 
2325  Vcb->locked = false;
2326  Vcb->Vpb->Flags &= ~VPB_LOCKED;
2327  Vcb->locked_fileobj = NULL;
2328 
2330 
2331  if (Vcb->lock_paused_balance)
2332  KeSetEvent(&Vcb->balance.event, 0, false);
2333 }
2334 
2337 
2338  TRACE("FSCTL_UNLOCK_VOLUME\n");
2339 
2340  if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2341  return STATUS_NOT_LOCKED;
2342 
2343  TRACE("unlocking volume\n");
2344 
2346 
2348 
2349  return STATUS_SUCCESS;
2350 }
2351 
2354  LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2355  NTSTATUS Status;
2356  HANDLE h;
2357  PFILE_OBJECT fileobj;
2358  PDEVICE_OBJECT devobj;
2359  LIST_ENTRY* le;
2360 
2361  TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2362 
2363  if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2365 
2366 #if defined(_WIN64)
2367  if (IoIs32bitProcess(Irp)) {
2368  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2369  return STATUS_INVALID_PARAMETER;
2370 
2371  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2372  } else {
2373 #endif
2374  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2375  return STATUS_INVALID_PARAMETER;
2376 
2377  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2378 #if defined(_WIN64)
2379  }
2380 #endif
2381 
2382  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2383 
2384  if (!NT_SUCCESS(Status)) {
2385  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2386  return Status;
2387  }
2388 
2389  devobj = fileobj->DeviceObject;
2390 
2392 
2393  le = VcbList.Flink;
2394 
2395  while (le != &VcbList) {
2397 
2398  if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2399  if (Vcb->Vpb == devobj->Vpb) {
2400  KIRQL irql;
2401  PVPB newvpb;
2402  bool free_newvpb = false;
2403 
2404  newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2405  if (!newvpb) {
2406  ERR("out of memory\n");
2408  goto end;
2409  }
2410 
2411  RtlZeroMemory(newvpb, sizeof(VPB));
2412 
2413  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2414 
2415  Vcb->removing = true;
2416 
2417  ExReleaseResourceLite(&Vcb->tree_lock);
2418 
2420 
2421  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2422 
2424 
2425  if (Vcb->need_write && !Vcb->readonly)
2426  Status = do_write(Vcb, Irp);
2427  else
2429 
2430  free_trees(Vcb);
2431 
2432  if (!NT_SUCCESS(Status)) {
2433  ERR("do_write returned %08x\n", Status);
2434  ExReleaseResourceLite(&Vcb->tree_lock);
2435  ExFreePool(newvpb);
2436  goto end;
2437  }
2438 
2440 
2441  ExReleaseResourceLite(&Vcb->tree_lock);
2442 
2444 
2445  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2446  newvpb->Type = IO_TYPE_VPB;
2447  newvpb->Size = sizeof(VPB);
2448  newvpb->RealDevice = devobj;
2449  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2450 
2451  devobj->Vpb = newvpb;
2452  } else
2453  free_newvpb = true;
2454 
2456 
2457  if (free_newvpb)
2458  ExFreePool(newvpb);
2459 
2460  if (Vcb->open_files == 0)
2461  uninit(Vcb);
2462  }
2463 
2464  break;
2465  }
2466 
2467  le = le->Flink;
2468  }
2469 
2471 
2472 end:
2474 
2475  ObDereferenceObject(fileobj);
2476 
2477  return Status;
2478 }
2479 
2482  ULONG* volstate;
2483 
2484  if (Irp->AssociatedIrp.SystemBuffer) {
2485  volstate = Irp->AssociatedIrp.SystemBuffer;
2486  } else if (Irp->MdlAddress != NULL) {
2487  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2488 
2489  if (!volstate)
2491  } else
2493 
2494  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2495  return STATUS_INVALID_PARAMETER;
2496 
2497  *volstate = 0;
2498 
2499  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2500  return STATUS_INVALID_PARAMETER;
2501 
2502  Irp->IoStatus.Information = sizeof(ULONG);
2503 
2504  return STATUS_SUCCESS;
2505 }
2506 
2510 
2511  TRACE("FSCTL_GET_COMPRESSION\n");
2512 
2513  if (Irp->AssociatedIrp.SystemBuffer) {
2514  compression = Irp->AssociatedIrp.SystemBuffer;
2515  } else if (Irp->MdlAddress != NULL) {
2517 
2518  if (!compression)
2520  } else
2522 
2523  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2524  return STATUS_INVALID_PARAMETER;
2525 
2527 
2528  Irp->IoStatus.Information = sizeof(USHORT);
2529 
2530  return STATUS_SUCCESS;
2531 }
2532 
2536 
2537  TRACE("FSCTL_SET_COMPRESSION\n");
2538 
2539  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2540  return STATUS_INVALID_PARAMETER;
2541 
2542  compression = Irp->AssociatedIrp.SystemBuffer;
2543 
2545  return STATUS_INVALID_PARAMETER;
2546 
2547  return STATUS_SUCCESS;
2548 }
2549 
2551  LIST_ENTRY* le;
2552  volume_device_extension* vde = Vcb->vde;
2553  pdo_device_extension* pdode = vde->pdode;
2554 
2555  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2556 
2558 
2559  le = pdode->children.Flink;
2560  while (le != &pdode->children) {
2562 
2563  vc->generation = Vcb->superblock.generation - 1;
2564 
2565  le = le->Flink;
2566  }
2567 
2569 
2570  ExReleaseResourceLite(&Vcb->tree_lock);
2571 }
2572 
2574  NTSTATUS Status;
2575  bool open_files;
2576 
2577  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2578 
2579  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2580  return STATUS_SUCCESS;
2581 
2582  if (!shutdown) {
2583  if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2584  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2585  return STATUS_ACCESS_DENIED;
2586  }
2587 
2589  if (!NT_SUCCESS(Status)) {
2590  WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
2591  }
2592  }
2593 
2594  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2595 
2596  if (!Vcb->locked) {
2598 
2599  if (Vcb->need_write && !Vcb->readonly) {
2600  Status = do_write(Vcb, Irp);
2601 
2602  if (!NT_SUCCESS(Status))
2603  ERR("do_write returned %08x\n", Status);
2604  }
2605  }
2606 
2607  free_trees(Vcb);
2608 
2609  Vcb->removing = true;
2610 
2611  open_files = Vcb->open_files > 0;
2612 
2613  if (Vcb->vde) {
2615  Vcb->vde->mounted_device = NULL;
2616  }
2617 
2618  ExReleaseResourceLite(&Vcb->tree_lock);
2619 
2620  if (!open_files)
2621  uninit(Vcb);
2622 
2623  return STATUS_SUCCESS;
2624 }
2625 
2627  NTSTATUS Status;
2628  ULONG to_read;
2629  superblock* sb;
2630  uint32_t crc32;
2631  BTRFS_UUID fsuuid, devuuid;
2632  LIST_ENTRY* le;
2633 
2634  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2635 
2637  if (!sb) {
2638  ERR("out of memory\n");
2640  }
2641 
2642  Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2643  if (!NT_SUCCESS(Status)) {
2644  ERR("sync_read_phys returned %08x\n", Status);
2645  ExFreePool(sb);
2646  return Status;
2647  }
2648 
2649  if (sb->magic != BTRFS_MAGIC) {
2650  TRACE("device is not Btrfs\n");
2651  ExFreePool(sb);
2652  return STATUS_SUCCESS;
2653  }
2654 
2655  crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2656 
2657  if (crc32 != *((uint32_t*)sb->checksum)) {
2658  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2659  ExFreePool(sb);
2660  return STATUS_SUCCESS;
2661  }
2662 
2663  fsuuid = sb->uuid;
2664  devuuid = sb->dev_item.device_uuid;
2665 
2666  ExFreePool(sb);
2667 
2669 
2670  le = VcbList.Flink;
2671 
2672  while (le != &VcbList) {
2674 
2675  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2676  LIST_ENTRY* le2;
2677 
2678  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2679 
2680  if (Vcb->superblock.num_devices > 1) {
2681  le2 = Vcb->devices.Flink;
2682  while (le2 != &Vcb->devices) {
2684 
2685  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2686  ExReleaseResourceLite(&Vcb->tree_lock);
2688  return STATUS_DEVICE_NOT_READY;
2689  }
2690 
2691  le2 = le2->Flink;
2692  }
2693  }
2694 
2695  ExReleaseResourceLite(&Vcb->tree_lock);
2697  return STATUS_SUCCESS;
2698  }
2699 
2700  le = le->Flink;
2701  }
2702 
2704 
2705  return STATUS_SUCCESS;
2706 }
2707 
2710  NTSTATUS Status;
2711 
2712  // FIXME - avoid "bootloader area"??
2713 
2714  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2715  dmdsa.Action = DeviceDsmAction_Trim;
2717  dmdsa.ParameterBlockOffset = 0;
2718  dmdsa.ParameterBlockLength = 0;
2719  dmdsa.DataSetRangesOffset = 0;
2720  dmdsa.DataSetRangesLength = 0;
2721 
2723  if (!NT_SUCCESS(Status))
2724  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
2725 }
2726 
2729  NTSTATUS Status;
2730  PFILE_OBJECT fileobj, mountmgrfo;
2732  HANDLE h;
2733  LIST_ENTRY* le;
2734  device* dev;
2735  DEV_ITEM* di;
2736  uint64_t dev_id, size;
2737  uint8_t* mb;
2738  uint64_t* stats;
2739  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2740  volume_child* vc;
2742  KEY searchkey;
2743  traverse_ptr tp;
2746  pdo_device_extension* pdode;
2747  const GUID* pnp_guid;
2749 
2750  pnp_name.Buffer = NULL;
2751 
2752  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2754 
2755  if (!Vcb->vde) {
2756  WARN("not allowing second device to be added to non-PNP device\n");
2757  return STATUS_NOT_SUPPORTED;
2758  }
2759 
2760  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2762 
2763 #if defined(_WIN64)
2764  if (IoIs32bitProcess(Irp)) {
2765  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2766  return STATUS_INVALID_PARAMETER;
2767 
2768  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2769  } else {
2770 #endif
2771  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2772  return STATUS_INVALID_PARAMETER;
2773 
2774  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2775 #if defined(_WIN64)
2776  }
2777 #endif
2778 
2779  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2780 
2781  if (!NT_SUCCESS(Status)) {
2782  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2783  return Status;
2784  }
2785 
2786  DeviceObject = fileobj->DeviceObject;
2787 
2789  if (!NT_SUCCESS(Status)) {
2790  ERR("get_device_pnp_name returned %08x\n", Status);
2791  ObDereferenceObject(fileobj);
2792  return Status;
2793  }
2794 
2795  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2796  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2797  DeviceObject = DeviceObject->AttachedDevice;
2798 
2800  if (!NT_SUCCESS(Status)) {
2801  ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
2802  ObDereferenceObject(fileobj);
2803  return Status;
2804  }
2805 
2807  if (!NT_SUCCESS(Status)) {
2808  ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
2809  ObDereferenceObject(fileobj);
2810  return Status;
2811  }
2812 
2813  // if disk, check it has no partitions
2814  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2815  ULONG dlisize;
2817 
2818  dlisize = 0;
2819 
2820  do {
2821  dlisize += 1024;
2822 
2823  if (dli)
2824  ExFreePool(dli);
2825 
2826  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2827  if (!dli) {
2828  ERR("out of memory\n");
2830  goto end2;
2831  }
2832 
2834  } while (Status == STATUS_BUFFER_TOO_SMALL);
2835 
2836  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2837  ExFreePool(dli);
2838  ERR("not adding disk which has partitions\n");
2840  goto end2;
2841  }
2842 
2843  ExFreePool(dli);
2844  }
2845 
2847  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2848  if (NT_SUCCESS(Status)) {
2849  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2850  WARN("device was not disk\n");
2851  ObDereferenceObject(fileobj);
2852  return STATUS_INVALID_PARAMETER;
2853  }
2854  } else {
2855  sdn.DeviceNumber = 0xffffffff;
2856  sdn.PartitionNumber = 0xffffffff;
2857  }
2858 
2860  &gli, sizeof(gli), true, NULL);
2861  if (!NT_SUCCESS(Status)) {
2862  ERR("error reading length information: %08x\n", Status);
2863  ObDereferenceObject(fileobj);
2864  return Status;
2865  }
2866 
2867  size = gli.Length.QuadPart;
2868 
2869  if (size < 0x100000) {
2870  ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2871  ObDereferenceObject(fileobj);
2872  return STATUS_INTERNAL_ERROR;
2873  }
2874 
2876 
2877  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2878 
2879  if (Vcb->need_write)
2880  Status = do_write(Vcb, Irp);
2881  else
2883 
2884  free_trees(Vcb);
2885 
2886  if (!NT_SUCCESS(Status)) {
2887  ERR("do_write returned %08x\n", Status);
2888  goto end;
2889  }
2890 
2892  if (!dev) {
2893  ERR("out of memory\n");
2895  goto end;
2896  }
2897 
2898  RtlZeroMemory(dev, sizeof(device));
2899 
2900  dev->devobj = DeviceObject;
2901  dev->fileobj = fileobj;
2902  dev->seeding = false;
2903  init_device(Vcb, dev, true);
2904 
2905  InitializeListHead(&dev->space);
2906 
2907  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2908  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2909  if (!NT_SUCCESS(Status)) {
2910  ERR("add_space_entry returned %08x\n", Status);
2911  goto end;
2912  }
2913  }
2914 
2915  dev_id = 0;
2916 
2917  le = Vcb->devices.Flink;
2918  while (le != &Vcb->devices) {
2919  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2920 
2921  if (dev2->devitem.dev_id > dev_id)
2922  dev_id = dev2->devitem.dev_id;
2923 
2924  le = le->Flink;
2925  }
2926 
2927  dev_id++;
2928 
2929  dev->devitem.dev_id = dev_id;
2930  dev->devitem.num_bytes = size;
2931  dev->devitem.bytes_used = 0;
2932  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2933  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2934  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2935  dev->devitem.type = 0;
2936  dev->devitem.generation = 0;
2937  dev->devitem.start_offset = 0;
2938  dev->devitem.dev_group = 0;
2939  dev->devitem.seek_speed = 0;
2940  dev->devitem.bandwidth = 0;
2941  get_uuid(&dev->devitem.device_uuid);
2942  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2943 
2945  if (!di) {
2946  ERR("out of memory\n");
2947  goto end;
2948  }
2949 
2950  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2951 
2952  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2953  if (!NT_SUCCESS(Status)) {
2954  ERR("insert_tree_item returned %08x\n", Status);
2955  ExFreePool(di);
2956  goto end;
2957  }
2958 
2959  // add stats entry to dev tree
2960  stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2961  if (!stats) {
2962  ERR("out of memory\n");
2964  goto end;
2965  }
2966 
2967  RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2968 
2969  searchkey.obj_id = 0;
2970  searchkey.obj_type = TYPE_DEV_STATS;
2971  searchkey.offset = di->dev_id;
2972 
2973  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2974  if (!NT_SUCCESS(Status)) {
2975  ERR("error - find_item returned %08x\n", Status);
2976  ExFreePool(stats);
2977  goto end;
2978  }
2979 
2980  if (!keycmp(tp.item->key, searchkey)) {
2982  if (!NT_SUCCESS(Status)) {
2983  ERR("delete_tree_item returned %08x\n", Status);
2984  ExFreePool(stats);
2985  goto end;
2986  }
2987  }
2988 
2989  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2990  if (!NT_SUCCESS(Status)) {
2991  ERR("insert_tree_item returned %08x\n", Status);
2992  ExFreePool(stats);
2993  goto end;
2994  }
2995 
2996  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
2998 
2999  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3000  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
3001  if (!mb) {
3002  ERR("out of memory\n");
3004  goto end;
3005  }
3006 
3007  RtlZeroMemory(mb, 0x100000);
3008 
3009  Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3010  if (!NT_SUCCESS(Status)) {
3011  ERR("write_data_phys returned %08x\n", Status);
3012  ExFreePool(mb);
3013  goto end;
3014  }
3015 
3016  ExFreePool(mb);
3017 
3018  vde = Vcb->vde;
3019  pdode = vde->pdode;
3020 
3022  if (!vc) {
3023  ERR("out of memory\n");
3025  goto end;
3026  }
3027 
3028  vc->uuid = dev->devitem.device_uuid;
3029  vc->devid = dev_id;
3030  vc->generation = Vcb->superblock.generation;
3031  vc->devobj = DeviceObject;
3032  vc->fileobj = fileobj;
3033  vc->notification_entry = NULL;
3034 
3036  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3037  if (!NT_SUCCESS(Status))
3038  WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
3039 
3040  pnp_name2 = pnp_name;
3041 
3042  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3043  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3044  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3045  pnp_name2.Length -= 3 * sizeof(WCHAR);
3046  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3047  }
3048 
3049  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3050 
3051  if (pnp_name2.Length == 0)
3052  vc->pnp_name.Buffer = NULL;
3053  else {
3055  if (!vc->pnp_name.Buffer) {
3056  ERR("out of memory\n");
3058  goto end;
3059  }
3060 
3061  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3062  }
3063 
3064  vc->size = size;
3065  vc->seeding = false;
3066  vc->disk_num = sdn.DeviceNumber;
3067  vc->part_num = sdn.PartitionNumber;
3068  vc->had_drive_letter = false;
3069 
3071  InsertTailList(&pdode->children, &vc->list_entry);
3072  pdode->num_children++;
3073  pdode->children_loaded++;
3075 
3077  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3078  if (!NT_SUCCESS(Status))
3079  ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
3080  else {
3083  WARN("remove_drive_letter returned %08x\n", Status);
3084 
3086 
3087  ObDereferenceObject(mountmgrfo);
3088  }
3089 
3090  Vcb->superblock.num_devices++;
3091  Vcb->superblock.total_bytes += size;
3092  Vcb->devices_loaded++;
3093  InsertTailList(&Vcb->devices, &dev->list_entry);
3094 
3095  // FIXME - send notification that volume size has increased
3096 
3097  ObReferenceObject(DeviceObject); // for Vcb
3098 
3099  Status = do_write(Vcb, Irp);
3100  if (!NT_SUCCESS(Status))
3101  ERR("do_write returned %08x\n", Status);
3102 
3103  ObReferenceObject(fileobj);
3104 
3105 end:
3106  free_trees(Vcb);
3107 
3108  ExReleaseResourceLite(&Vcb->tree_lock);
3109 
3110 end2:
3111  ObDereferenceObject(fileobj);
3112 
3113  if (pnp_name.Buffer)
3115 
3116  if (NT_SUCCESS(Status))
3118 
3119  return Status;
3120 }
3121 
3123  fcb* fcb;
3124  ccb* ccb;
3125 
3126  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3127 
3128  if (!FileObject)
3129  return STATUS_INVALID_PARAMETER;
3130 
3131  fcb = FileObject->FsContext;
3132  ccb = FileObject->FsContext2;
3133 
3134  if (!fcb)
3135  return STATUS_INVALID_PARAMETER;
3136 
3137  if (fcb != Vcb->volume_fcb)
3138  return STATUS_INVALID_PARAMETER;
3139 
3140  if (!ccb)
3141  return STATUS_INVALID_PARAMETER;
3142 
3143  ccb->allow_extended_dasd_io = true;
3144 
3145  return STATUS_SUCCESS;
3146 }
3147 
3149  if (length < sizeof(BTRFS_UUID))
3150  return STATUS_BUFFER_OVERFLOW;
3151 
3152  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3153 
3154  return STATUS_SUCCESS;
3155 }
3156 
3158  uint64_t devid;
3159  NTSTATUS Status;
3160  LIST_ENTRY* le;
3161 
3162  if (length < sizeof(uint64_t))
3163  return STATUS_INVALID_PARAMETER;
3164 
3165  if (Vcb->readonly)
3167 
3168  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3170 
3171  devid = *((uint64_t*)data);
3172 
3173  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3174 
3175  le = Vcb->devices.Flink;
3176 
3177  while (le != &Vcb->devices) {
3179 
3180  if (dev->devitem.dev_id == devid) {
3181  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3182  dev->stats_changed = true;
3183  Vcb->stats_changed = true;
3184  Vcb->need_write = true;
3186  goto end;
3187  }
3188 
3189  le = le->Flink;
3190  }
3191 
3192  WARN("device %I64x not found\n", devid);
3194 
3195 end:
3196  ExReleaseResourceLite(&Vcb->tree_lock);
3197 
3198  return Status;
3199 }
3200 
3203 
3204  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3205 
3206  // STUB
3207 
3208  if (!FileObject)
3209  return STATUS_INVALID_PARAMETER;
3210 
3212  return STATUS_INVALID_PARAMETER;
3213 
3214  fgiib->ChecksumAlgorithm = 0;
3215  fgiib->Reserved = 0;
3216  fgiib->Flags = 0;
3217  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3218  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3219 
3220  return STATUS_SUCCESS;
3221 }
3222 
3224  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3225 
3226  // STUB
3227 
3228  if (!FileObject)
3229  return STATUS_INVALID_PARAMETER;
3230 
3232  return STATUS_INVALID_PARAMETER;
3233 
3234  return STATUS_SUCCESS;
3235 }
3236 
3238  LIST_ENTRY* le;
3239 
3240  le = fcb->extents.Flink;
3241  while (le != &fcb->extents) {
3243 
3244  if (!ext->ignore)
3245  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3246 
3247  le = le->Flink;
3248  }
3249 
3250  return false;
3251 }
3252 
3255  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3256  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3257  NTSTATUS Status;
3258  PFILE_OBJECT sourcefo;
3259  uint64_t sourcelen, nbytes = 0;
3260  LIST_ENTRY rollback, *le, newexts;
3262  BTRFS_TIME now;
3263  bool make_inline;
3264 
3265  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3266  return STATUS_BUFFER_TOO_SMALL;
3267 
3268  if (Vcb->readonly)
3270 
3271  if (ded->ByteCount.QuadPart == 0)
3272  return STATUS_SUCCESS;
3273 
3274  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3275  return STATUS_INVALID_PARAMETER;
3276 
3278  return STATUS_ACCESS_DENIED;
3279 
3280  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3281  WARN("insufficient privileges\n");
3282  return STATUS_ACCESS_DENIED;
3283  }
3284 
3285  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3286  return STATUS_INVALID_PARAMETER;
3287 
3288  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3289  if (!NT_SUCCESS(Status)) {
3290  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3291  return Status;
3292  }
3293 
3294  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3295  WARN("source and destination are on different volumes\n");
3296  ObDereferenceObject(sourcefo);
3297  return STATUS_INVALID_PARAMETER;
3298  }
3299 
3300  sourcefcb = sourcefo->FsContext;
3301  sourceccb = sourcefo->FsContext2;
3302 
3303  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3304  ObDereferenceObject(sourcefo);
3305  return STATUS_INVALID_PARAMETER;
3306  }
3307 
3308  if (!sourcefcb->ads && !fcb->ads) {
3309  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3310  ObDereferenceObject(sourcefo);
3311  return STATUS_INVALID_PARAMETER;
3312  }
3313 
3314  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3315  ObDereferenceObject(sourcefo);
3316  return STATUS_INVALID_PARAMETER;
3317  }
3318  }
3319 
3320  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3321  WARN("insufficient privileges\n");
3322  ObDereferenceObject(sourcefo);
3323  return STATUS_ACCESS_DENIED;
3324  }
3325 
3326  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3327  ObDereferenceObject(sourcefo);
3328  return STATUS_INVALID_PARAMETER;
3329  }
3330 
3331  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3332 
3333  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3334  ObDereferenceObject(sourcefo);
3335  return STATUS_NOT_SUPPORTED;
3336  }
3337 
3338  if (fcb == sourcefcb &&
3341  WARN("source and destination are the same, and the ranges overlap\n");
3342  ObDereferenceObject(sourcefo);
3343  return STATUS_INVALID_PARAMETER;
3344  }
3345 
3346  // fail if nocsum flag set on one file but not the other
3347  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3348  ObDereferenceObject(sourcefo);
3349  return STATUS_INVALID_PARAMETER;
3350  }
3351 
3353  InitializeListHead(&newexts);
3354 
3355  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3356 
3357  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3358 
3359  if (fcb != sourcefcb)
3360  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3361 
3364  goto end;
3365  }
3366 
3367  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3369  goto end;
3370  }
3371 
3372  make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3373 
3374  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3375  uint8_t* data2;
3376  ULONG bytes_read, dataoff, datalen2;
3377 
3378  if (make_inline) {
3379  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3380  datalen2 = (ULONG)fcb->inode_item.st_size;
3381  } else if (fcb->ads) {
3382  dataoff = 0;
3383  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3384  } else {
3385  dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3386  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3387  }
3388 
3390  if (!data2) {
3391  ERR("out of memory\n");
3393  goto end;
3394  }
3395 
3396  if (dataoff > 0) {
3397  if (make_inline)
3398  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3399  else
3400  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3401 
3402  if (!NT_SUCCESS(Status)) {
3403  ERR("read_file returned %08x\n", Status);
3404  ExFreePool(data2);
3405  goto end;
3406  }
3407  }
3408 
3409  if (sourcefcb->ads) {
3410  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3411  if (!NT_SUCCESS(Status)) {
3412  ERR("read_stream returned %08x\n", Status);
3413  ExFreePool(data2);
3414  goto end;
3415  }
3416  } else {
3417  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3418  if (!NT_SUCCESS(Status)) {
3419  ERR("read_file returned %08x\n", Status);
3420  ExFreePool(data2);
3421  goto end;
3422  }
3423  }
3424 
3425  if (dataoff + bytes_read < datalen2)
3426  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3427 
3428  if (fcb->ads)
3430  else if (make_inline) {
3431  uint16_t edsize;
3432  EXTENT_DATA* ed;
3433 
3434  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3435  if (!NT_SUCCESS(Status)) {
3436  ERR("excise_extents returned %08x\n", Status);
3437  ExFreePool(data2);
3438  goto end;
3439  }
3440 
3441  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3442 
3444  if (!ed) {
3445  ERR("out of memory\n");
3446  ExFreePool(data2);
3448  goto end;
3449  }
3450 
3451  ed->generation = Vcb->superblock.generation;
3457 
3458  RtlCopyMemory(ed->data, data2, datalen2);
3459 
3460  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3461  if (!NT_SUCCESS(Status)) {
3462  ERR("add_extent_to_fcb returned %08x\n", Status);
3463  ExFreePool(data2);
3464  goto end;
3465  }
3466 
3467  fcb->inode_item.st_blocks += datalen2;
3468  } else {
3469  uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3470 
3471  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3472  if (!NT_SUCCESS(Status)) {
3473  ERR("do_write_file returned %08x\n", Status);
3474  ExFreePool(data2);
3475  goto end;
3476  }
3477  }
3478 
3479  ExFreePool(data2);
3480  } else {
3481  LIST_ENTRY* lastextle;
3482 
3483  le = sourcefcb->extents.Flink;
3484  while (le != &sourcefcb->extents) {
3486 
3487  if (!ext->ignore) {
3488  if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3489  break;
3490 
3491  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3492  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3493  extent* ext2;
3494  EXTENT_DATA2 *ed2s, *ed2d;
3495  chunk* c;
3496 
3497  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3498 
3499  if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3500  le = le->Flink;
3501  continue;
3502  }
3503 
3505  if (!ext2) {
3506  ERR("out of memory\n");
3508  goto end;
3509  }
3510 
3511  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3512  ext2->offset = ded->TargetFileOffset.QuadPart;
3513  else
3514  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3515 
3516  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3517  ext2->unique = false;
3518  ext2->ignore = false;
3519  ext2->inserted = true;
3520 
3521  ext2->extent_data.generation = Vcb->superblock.generation;
3522  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3523  ext2->extent_data.compression = ext->extent_data.compression;
3524  ext2->extent_data.encryption = ext->extent_data.encryption;
3525  ext2->extent_data.encoding = ext->extent_data.encoding;
3526  ext2->extent_data.type = ext->extent_data.type;
3527 
3528  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3529 
3530  ed2d->address = ed2s->address;
3531  ed2d->size = ed2s->size;
3532 
3533  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3534  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3535  ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3536  } else {
3537  ed2d->offset = ed2s->offset;
3538  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3539  }
3540 
3541  if (ext->csum) {
3542  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3543  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG);
3544  if (!ext2->csum) {
3545  ERR("out of memory\n");
3547  ExFreePool(ext2);
3548  goto end;
3549  }
3550 
3551  RtlCopyMemory(ext2->csum, &ext->csum[(ed2d->offset - ed2s->offset) / Vcb->superblock.sector_size],
3552  (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size));
3553  } else {
3554  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG);
3555  if (!ext2->csum) {
3556  ERR("out of memory\n");
3558  ExFreePool(ext2);
3559  goto end;
3560  }
3561 
3562  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * sizeof(uint32_t) / Vcb->superblock.sector_size));
3563  }
3564  } else
3565  ext2->csum = NULL;
3566 
3567  InsertTailList(&newexts, &ext2->list_entry);
3568 
3569  c = get_chunk_from_address(Vcb, ed2s->address);
3570  if (!c) {
3571  ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3573  goto end;
3574  }
3575 
3576  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3577  1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3578  if (!NT_SUCCESS(Status)) {
3579  ERR("update_changed_extent_ref returned %08x\n", Status);
3580  goto end;
3581  }
3582 
3583  nbytes += ed2d->num_bytes;
3584  }
3585  }
3586 
3587  le = le->Flink;
3588  }
3589 
3591  if (!NT_SUCCESS(Status)) {
3592  ERR("excise_extents returned %08x\n", Status);
3593 
3594  while (!IsListEmpty(&newexts)) {
3596  ExFreePool(ext);
3597  }
3598 
3599  goto end;
3600  }
3601 
3602  // clear unique flags in source fcb
3603  le = sourcefcb->extents.Flink;
3604  while (le != &sourcefcb->extents) {
3606 
3607  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3608  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3609  LIST_ENTRY* le2;
3610 
3611  le2 = newexts.Flink;
3612  while (le2 != &newexts) {
3614 
3615  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3616  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3617 
3618  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3619  ext->unique = false;
3620  break;
3621  }
3622  }
3623 
3624  le2 = le2->Flink;
3625  }
3626  }
3627 
3628  le = le->Flink;
3629  }
3630 
3631  lastextle = &fcb->extents;
3632  while (!IsListEmpty(&newexts)) {
3634 
3635  add_extent(fcb, lastextle, ext);
3636  lastextle = &ext->list_entry;
3637  }
3638  }
3639 
3642 
3643  if (fcb->ads) {
3644  ccb->fileref->parent->fcb->inode_item.sequence++;
3645 
3646  if (!ccb->user_set_change_time)
3647  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3648 
3649  ccb->fileref->parent->fcb->inode_item_changed = true;
3650  mark_fcb_dirty(ccb->fileref->parent->fcb);
3651  } else {
3652  fcb->inode_item.st_blocks += nbytes;
3653  fcb->inode_item.sequence++;
3654 
3655  if (!ccb->user_set_change_time)
3657 
3658  if (!ccb->user_set_write_time) {
3661  }
3662 
3663  fcb->inode_item_changed = true;
3664  fcb->extents_changed = true;
3665  }
3666 
3668 
3669  if (FileObject->SectionObjectPointer->DataSectionObject)
3670  CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3671 
3673 
3674 end:
3675  ObDereferenceObject(sourcefo);
3676 
3677  if (NT_SUCCESS(Status))
3679  else
3681 
3682  if (fcb != sourcefcb)
3683  ExReleaseResourceLite(sourcefcb->Header.Resource);
3684 
3685  ExReleaseResourceLite(fcb->Header.Resource);
3686 
3687  ExReleaseResourceLite(&Vcb->tree_lock);
3688 
3689  return Status;
3690 }
3691 
3693  NTSTATUS Status;
3694  btrfs_mknod* bmn;
3695  fcb *parfcb, *fcb;
3696  ccb* parccb;
3697  file_ref *parfileref, *fileref;
3699  root* subvol;
3700  uint64_t inode;
3701  dir_child* dc;
3703  BTRFS_TIME now;
3704  LIST_ENTRY* lastle;
3705  ANSI_STRING utf8;
3706  ULONG len, i;
3707  SECURITY_SUBJECT_CONTEXT subjcont;
3708  PSID owner;
3709  BOOLEAN defaulted;
3710 
3711  TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
3712 
3713  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3714  return STATUS_INVALID_PARAMETER;
3715 
3716  if (Vcb->readonly)
3718 
3719  parfcb = FileObject->FsContext;
3720 
3721  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3722  WARN("trying to create file in something other than a directory\n");
3723  return STATUS_INVALID_PARAMETER;
3724  }
3725 
3726  if (is_subvol_readonly(parfcb->subvol, Irp))
3727  return STATUS_ACCESS_DENIED;
3728 
3729  parccb = FileObject->FsContext2;
3730  parfileref = parccb->fileref;
3731 
3732  if (!parfileref)
3733  return STATUS_INVALID_PARAMETER;
3734 
3735  if (datalen < sizeof(btrfs_mknod))
3736  return STATUS_INVALID_PARAMETER;
3737 
3738  bmn = (btrfs_mknod*)data;
3739 
3740  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3741  return STATUS_INVALID_PARAMETER;
3742 
3743  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3744  return STATUS_INVALID_PARAMETER;
3745 
3746  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3747  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3748  WARN("insufficient privileges\n");
3749  return STATUS_ACCESS_DENIED;
3750  }
3751 
3752  if (bmn->inode != 0) {
3753  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3755  }
3756 
3757  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3758  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3760  }
3761 
3762  // don't allow files called . or ..
3763  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3765 
3766  Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3767  if (!NT_SUCCESS(Status)) {
3768  ERR("utf16_to_utf8 return %08x\n", Status);
3769  return Status;
3770  }
3771 
3772  if (len == 0) {
3773  ERR("utf16_to_utf8 returned a length of 0\n");
3774  return STATUS_INTERNAL_ERROR;
3775  }
3776 
3777  if (len > 0xffff) {
3778  ERR("len was too long (%x)\n", len);
3779  return STATUS_INVALID_PARAMETER;
3780  }
3781 
3782  utf8.MaximumLength = utf8.Length = (USHORT)len;
3784 
3785  if (!utf8.Buffer) {
3786  ERR("out of memory\n");
3788  }
3789 
3790  Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3791  if (!NT_SUCCESS(Status)) {
3792  ERR("utf16_to_utf8 failed with error %08x\n", Status);
3793  ExFreePool(utf8.Buffer);
3794  return Status;
3795  }
3796 
3797  name.Length = name.MaximumLength = bmn->namelen;
3798  name.Buffer = bmn->name;
3799 
3800  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3802  ERR("find_file_in_dir returned %08x\n", Status);
3803  goto end;
3804  }
3805 
3806  if (NT_SUCCESS(Status)) {
3807  WARN("filename already exists\n");
3809  goto end;
3810  }
3811 
3814 
3816  if (!fcb) {
3817  ERR("out of memory\n");
3819  goto end;
3820  }
3821 
3822  fcb->Vcb = Vcb;
3823 
3824  fcb->inode_item.generation = Vcb->superblock.generation;
3825  fcb->inode_item.transid = Vcb->superblock.generation;
3826  fcb->inode_item.st_size = 0;
3827  fcb->inode_item.st_blocks = 0;
3828  fcb->inode_item.block_group = 0;
3829  fcb->inode_item.st_nlink = 1;
3833 
3834  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3835  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3836  else
3837  fcb->inode_item.st_rdev = 0;
3838 
3839  fcb->inode_item.flags = 0;
3840  fcb->inode_item.sequence = 1;
3844  fcb->inode_item.otime = now;
3845 
3846  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3848  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3850  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3852  else if (bmn->type == BTRFS_TYPE_FIFO)
3854  else if (bmn->type == BTRFS_TYPE_SOCKET)
3856  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3858  else
3860 
3861  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3862  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3863 
3864  // inherit nodatacow flag from parent directory
3865  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3867 
3868  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3870  }
3871 
3872  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3874 
3877 
3878  fcb->inode_item_changed = true;
3879 
3880  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3881  fcb->Header.AllocationSize.QuadPart = 0;
3882  fcb->Header.FileSize.QuadPart = 0;
3883  fcb->Header.ValidDataLength.QuadPart = 0;
3884 
3885  fcb->atts = 0;
3886 
3887  if (bmn->name[0] == '.')
3889 
3890  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3892 
3893  fcb->atts_changed = false;
3894 
3895  InterlockedIncrement(&parfcb->refcount);
3896  fcb->subvol = parfcb->subvol;
3897 
3898  SeCaptureSubjectContext(&subjcont);
3899 
3900  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3902 
3903  if (!NT_SUCCESS(Status)) {
3904  ERR("SeAssignSecurityEx returned %08x\n", Status);
3905  reap_fcb(fcb);
3906  goto end;
3907  }
3908 
3909  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3910  if (!NT_SUCCESS(Status)) {
3911  WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
3912  fcb->sd_dirty = true;
3913  } else {
3914  fcb->inode_item.st_uid = sid_to_uid(owner);
3916  }
3917 
3918  find_gid(fcb, parfcb, &subjcont);
3919 
3920  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3921  acquire_fcb_lock_exclusive(Vcb);
3922 
3923  if (bmn->inode == 0) {
3924  inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3925  lastle = parfcb->subvol->fcbs.Blink;
3926  } else {
3927  if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3928  inode = parfcb->subvol->lastinode = bmn->inode;
3929  lastle = parfcb->subvol->fcbs.Blink;
3930  } else {
3931  LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3932 
3933  lastle = parfcb->subvol->fcbs.Blink;;
3934  while (le != &parfcb->subvol->fcbs) {
3935  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3936 
3937  if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3938  release_fcb_lock(Vcb);
3939  ExReleaseResourceLite(&Vcb->fileref_lock);
3940 
3941  WARN("inode collision\n");
3943  goto end;
3944  } else if (fcb2->inode > bmn->inode) {
3945  lastle = fcb2->list_entry.Blink;
3946  break;
3947  }
3948 
3949  le = le->Flink;
3950  }
3951 
3952  inode = bmn->inode;
3953  }
3954  }
3955 
3956  fcb->inode = inode;
3957  fcb->type = bmn->type;
3958 
3960  if (!fileref) {
3961  release_fcb_lock(Vcb);
3962  ExReleaseResourceLite(&Vcb->fileref_lock);
3963 
3964  ERR("out of memory\n");
3965  reap_fcb(fcb);
3967  goto end;
3968  }
3969 
3970  fileref->fcb = fcb;
3971 
3972  fcb->created = true;
3973  fileref->created = true;
3974 
3975  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3976  fcb->subvol->root_item.ctime = now;
3977 
3978  fileref->parent = parfileref;
3979 
3982 
3983  Status = add_dir_child(fileref->parent->fcb, fcb->inode, false, &utf8, &name, fcb->type, &dc);
3984  if (!NT_SUCCESS(Status))
3985  WARN("add_dir_child returned %08x\n", Status);
3986 
3987  fileref->dc = dc;
3988  dc->fileref = fileref;
3989 
3990  ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
3991  InsertTailList(&parfileref->children, &fileref->list_entry);
3992  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
3993 
3994  increase_fileref_refcount(parfileref);
3995 
3996  if (fcb->type == BTRFS_TYPE_DIRECTORY) {