ReactOS  0.4.15-dev-449-g64abd9f
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 %08lx\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 %08lx\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.metadata_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 %08lx\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 %08lx\n", Status);
182  goto end;
183  }
184  }
185  }
186 
187  calc_tree_checksum(Vcb, th);
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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\n", Status);
833  return Status;
834  }
835 
836  if (len == 0) {
837  ERR("utf16_to_utf8 returned a length of 0\n");
838  return STATUS_INTERNAL_ERROR;
839  }
840 
841  if (len > 0xffff) {
842  ERR("len was too long\n");
844  }
845 
846  utf8.MaximumLength = utf8.Length = (USHORT)len;
848 
849  if (!utf8.Buffer) {
850  ERR("out of memory\n");
852  }
853 
854  Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length);
855  if (!NT_SUCCESS(Status)) {
856  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
857  goto end2;
858  }
859 
860  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
861 
864 
865  // no need for fcb_lock as we have tree_lock exclusively
866  Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || bcs->posix, Irp);
867 
868  if (NT_SUCCESS(Status)) {
869  if (!fr2->deleted) {
870  WARN("file already exists\n");
871  free_fileref(fr2);
873  goto end;
874  } else
875  free_fileref(fr2);
877  ERR("open_fileref returned %08lx\n", Status);
878  goto end;
879  }
880 
881  id = InterlockedIncrement64(&Vcb->root_root->lastinode);
882  Status = create_root(Vcb, id, &r, false, 0, Irp);
883 
884  if (!NT_SUCCESS(Status)) {
885  ERR("create_root returned %08lx\n", Status);
886  goto end;
887  }
888 
889  TRACE("created root %I64x\n", id);
890 
891  if (!Vcb->uuid_root) {
892  root* uuid_root;
893 
894  TRACE("uuid root doesn't exist, creating it\n");
895 
896  Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp);
897 
898  if (!NT_SUCCESS(Status)) {
899  ERR("create_root returned %08lx\n", Status);
900  goto end;
901  }
902 
903  Vcb->uuid_root = uuid_root;
904  }
905 
906  root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG);
907  if (!root_num) {
908  ERR("out of memory\n");
910  goto end;
911  }
912 
913  tp.tree = NULL;
914 
915  do {
916  get_uuid(&r->root_item.uuid);
917 
918  RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t));
919  searchkey.obj_type = TYPE_SUBVOL_UUID;
920  RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t));
921 
922  Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp);
923  } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key));
924 
925  *root_num = r->id;
926 
927  Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp);
928  if (!NT_SUCCESS(Status)) {
929  ERR("insert_tree_item returned %08lx\n", Status);
930  ExFreePool(root_num);
931  goto end;
932  }
933 
934  r->root_item.inode.generation = 1;
935  r->root_item.inode.st_size = 3;
936  r->root_item.inode.st_blocks = Vcb->superblock.node_size;
937  r->root_item.inode.st_nlink = 1;
938  r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755
939  r->root_item.inode.flags = 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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\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 %08lx\n", Status);
2032  goto end;
2033  }
2034  }
2035 
2036  if (end > start) {
2038  if (!NT_SUCCESS(Status)) {
2039  ERR("excise_extents returned %08lx\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, %lx, %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 %08lx\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 %08lx\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  ObReferenceObject(devobj);
2414 
2415  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2416 
2417  Vcb->removing = true;
2418 
2419  ExReleaseResourceLite(&Vcb->tree_lock);
2420 
2422 
2423  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2424 
2426 
2427  if (Vcb->need_write && !Vcb->readonly)
2428  Status = do_write(Vcb, Irp);
2429  else
2431 
2432  free_trees(Vcb);
2433 
2434  if (!NT_SUCCESS(Status)) {
2435  ERR("do_write returned %08lx\n", Status);
2436  ExReleaseResourceLite(&Vcb->tree_lock);
2437  ExFreePool(newvpb);
2438  ObDereferenceObject(devobj);
2439  goto end;
2440  }
2441 
2443 
2444  ExReleaseResourceLite(&Vcb->tree_lock);
2445 
2447 
2448  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2449  newvpb->Type = IO_TYPE_VPB;
2450  newvpb->Size = sizeof(VPB);
2451  newvpb->RealDevice = devobj;
2452  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2453 
2454  devobj->Vpb = newvpb;
2455  } else
2456  free_newvpb = true;
2457 
2459 
2460  if (free_newvpb)
2461  ExFreePool(newvpb);
2462 
2463  if (Vcb->open_files == 0)
2464  uninit(Vcb);
2465 
2466  ObDereferenceObject(devobj);
2467  }
2468 
2469  break;
2470  }
2471 
2472  le = le->Flink;
2473  }
2474 
2476 
2477 end:
2479 
2480  ObDereferenceObject(fileobj);
2481 
2482  return Status;
2483 }
2484 
2487  ULONG* volstate;
2488 
2489  if (Irp->AssociatedIrp.SystemBuffer) {
2490  volstate = Irp->AssociatedIrp.SystemBuffer;
2491  } else if (Irp->MdlAddress != NULL) {
2492  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2493 
2494  if (!volstate)
2496  } else
2498 
2499  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2500  return STATUS_INVALID_PARAMETER;
2501 
2502  *volstate = 0;
2503 
2504  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2505  return STATUS_INVALID_PARAMETER;
2506 
2507  Irp->IoStatus.Information = sizeof(ULONG);
2508 
2509  return STATUS_SUCCESS;
2510 }
2511 
2515 
2516  TRACE("FSCTL_GET_COMPRESSION\n");
2517 
2518  if (Irp->AssociatedIrp.SystemBuffer) {
2519  compression = Irp->AssociatedIrp.SystemBuffer;
2520  } else if (Irp->MdlAddress != NULL) {
2522 
2523  if (!compression)
2525  } else
2527 
2528  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2529  return STATUS_INVALID_PARAMETER;
2530 
2532 
2533  Irp->IoStatus.Information = sizeof(USHORT);
2534 
2535  return STATUS_SUCCESS;
2536 }
2537 
2541 
2542  TRACE("FSCTL_SET_COMPRESSION\n");
2543 
2544  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2545  return STATUS_INVALID_PARAMETER;
2546 
2547  compression = Irp->AssociatedIrp.SystemBuffer;
2548 
2550  return STATUS_INVALID_PARAMETER;
2551 
2552  return STATUS_SUCCESS;
2553 }
2554 
2556  LIST_ENTRY* le;
2557  volume_device_extension* vde = Vcb->vde;
2558  pdo_device_extension* pdode = vde->pdode;
2559 
2560  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2561 
2563 
2564  le = pdode->children.Flink;
2565  while (le != &pdode->children) {
2567 
2568  vc->generation = Vcb->superblock.generation - 1;
2569 
2570  le = le->Flink;
2571  }
2572 
2574 
2575  ExReleaseResourceLite(&Vcb->tree_lock);
2576 }
2577 
2579  NTSTATUS Status;
2580  bool open_files;
2581 
2582  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2583 
2584  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2585  return STATUS_SUCCESS;
2586 
2587  if (!shutdown) {
2588  if (Vcb->disallow_dismount || Vcb->page_file_count != 0) {
2589  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2590  return STATUS_ACCESS_DENIED;
2591  }
2592 
2594  if (!NT_SUCCESS(Status)) {
2595  WARN("FsRtlNotifyVolumeEvent returned %08lx\n", Status);
2596  }
2597  }
2598 
2599  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2600 
2601  if (!Vcb->locked) {
2603 
2604  if (Vcb->need_write && !Vcb->readonly) {
2605  Status = do_write(Vcb, Irp);
2606 
2607  if (!NT_SUCCESS(Status))
2608  ERR("do_write returned %08lx\n", Status);
2609  }
2610  }
2611 
2612  free_trees(Vcb);
2613 
2614  Vcb->removing = true;
2615 
2616  open_files = Vcb->open_files > 0;
2617 
2618  if (Vcb->vde) {
2620  Vcb->vde->mounted_device = NULL;
2621  }
2622 
2623  ExReleaseResourceLite(&Vcb->tree_lock);
2624 
2625  if (!open_files)
2626  uninit(Vcb);
2627 
2628  return STATUS_SUCCESS;
2629 }
2630 
2632  NTSTATUS Status;
2633  ULONG to_read;
2634  superblock* sb;
2635  BTRFS_UUID fsuuid, devuuid;
2636  LIST_ENTRY* le;
2637 
2638  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2639 
2641  if (!sb) {
2642  ERR("out of memory\n");
2644  }
2645 
2646  Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true);
2647  if (!NT_SUCCESS(Status)) {
2648  ERR("sync_read_phys returned %08lx\n", Status);
2649  ExFreePool(sb);
2650  return Status;
2651  }
2652 
2653  if (sb->magic != BTRFS_MAGIC) {
2654  TRACE("device is not Btrfs\n");
2655  ExFreePool(sb);
2656  return STATUS_SUCCESS;
2657  }
2658 
2659  if (!check_superblock_checksum(sb)) {
2660  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2661  ExFreePool(sb);
2662  return STATUS_SUCCESS;
2663  }
2664 
2665  fsuuid = sb->uuid;
2666  devuuid = sb->dev_item.device_uuid;
2667 
2668  ExFreePool(sb);
2669 
2671 
2672  le = VcbList.Flink;
2673 
2674  while (le != &VcbList) {
2676 
2677  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2678  LIST_ENTRY* le2;
2679 
2680  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2681 
2682  if (Vcb->superblock.num_devices > 1) {
2683  le2 = Vcb->devices.Flink;
2684  while (le2 != &Vcb->devices) {
2686 
2687  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2688  ExReleaseResourceLite(&Vcb->tree_lock);
2690  return STATUS_DEVICE_NOT_READY;
2691  }
2692 
2693  le2 = le2->Flink;
2694  }
2695  }
2696 
2697  ExReleaseResourceLite(&Vcb->tree_lock);
2699  return STATUS_SUCCESS;
2700  }
2701 
2702  le = le->Flink;
2703  }
2704 
2706 
2707  return STATUS_SUCCESS;
2708 }
2709 
2712  NTSTATUS Status;
2713 
2714  // FIXME - avoid "bootloader area"??
2715 
2716  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2717  dmdsa.Action = DeviceDsmAction_Trim;
2719  dmdsa.ParameterBlockOffset = 0;
2720  dmdsa.ParameterBlockLength = 0;
2721  dmdsa.DataSetRangesOffset = 0;
2722  dmdsa.DataSetRangesLength = 0;
2723 
2725  if (!NT_SUCCESS(Status))
2726  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status);
2727 }
2728 
2731  NTSTATUS Status;
2732  PFILE_OBJECT fileobj, mountmgrfo;
2734  HANDLE h;
2735  LIST_ENTRY* le;
2736  device* dev;
2737  DEV_ITEM* di;
2738  uint64_t dev_id, size;
2739  uint8_t* mb;
2740  uint64_t* stats;
2741  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2742  volume_child* vc;
2744  KEY searchkey;
2745  traverse_ptr tp;
2748  pdo_device_extension* pdode;
2749  const GUID* pnp_guid;
2751 
2752  pnp_name.Buffer = NULL;
2753 
2754  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2756 
2757  if (!Vcb->vde) {
2758  WARN("not allowing second device to be added to non-PNP device\n");
2759  return STATUS_NOT_SUPPORTED;
2760  }
2761 
2762  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2764 
2765 #if defined(_WIN64)
2766  if (IoIs32bitProcess(Irp)) {
2767  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t))
2768  return STATUS_INVALID_PARAMETER;
2769 
2770  h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer));
2771  } else {
2772 #endif
2773  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2774  return STATUS_INVALID_PARAMETER;
2775 
2776  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2777 #if defined(_WIN64)
2778  }
2779 #endif
2780 
2781  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2782 
2783  if (!NT_SUCCESS(Status)) {
2784  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
2785  return Status;
2786  }
2787 
2788  DeviceObject = fileobj->DeviceObject;
2789 
2791  if (!NT_SUCCESS(Status)) {
2792  ERR("get_device_pnp_name returned %08lx\n", Status);
2793  ObDereferenceObject(fileobj);
2794  return Status;
2795  }
2796 
2797  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2798  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2799  DeviceObject = DeviceObject->AttachedDevice;
2800 
2802  if (!NT_SUCCESS(Status)) {
2803  ERR("IOCTL_DISK_IS_WRITABLE returned %08lx\n", Status);
2804  ObDereferenceObject(fileobj);
2805  return Status;
2806  }
2807 
2809  if (!NT_SUCCESS(Status)) {
2810  ERR("is_device_part_of_mounted_btrfs_raid returned %08lx\n", Status);
2811  ObDereferenceObject(fileobj);
2812  return Status;
2813  }
2814 
2815  // if disk, check it has no partitions
2816  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2817  ULONG dlisize;
2819 
2820  dlisize = 0;
2821 
2822  do {
2823  dlisize += 1024;
2824 
2825  if (dli)
2826  ExFreePool(dli);
2827 
2828  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2829  if (!dli) {
2830  ERR("out of memory\n");
2832  goto end2;
2833  }
2834 
2836  } while (Status == STATUS_BUFFER_TOO_SMALL);
2837 
2838  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2839  ExFreePool(dli);
2840  ERR("not adding disk which has partitions\n");
2842  goto end2;
2843  }
2844 
2845  ExFreePool(dli);
2846  }
2847 
2849  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
2850  if (NT_SUCCESS(Status)) {
2851  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2852  WARN("device was not disk\n");
2853  ObDereferenceObject(fileobj);
2854  return STATUS_INVALID_PARAMETER;
2855  }
2856  } else {
2857  sdn.DeviceNumber = 0xffffffff;
2858  sdn.PartitionNumber = 0xffffffff;
2859  }
2860 
2862  &gli, sizeof(gli), true, NULL);
2863  if (!NT_SUCCESS(Status)) {
2864  ERR("error reading length information: %08lx\n", Status);
2865  ObDereferenceObject(fileobj);
2866  return Status;
2867  }
2868 
2869  size = gli.Length.QuadPart;
2870 
2871  if (size < 0x100000) {
2872  ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size);
2873  ObDereferenceObject(fileobj);
2874  return STATUS_INTERNAL_ERROR;
2875  }
2876 
2878 
2879  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2880 
2881  if (Vcb->need_write)
2882  Status = do_write(Vcb, Irp);
2883  else
2885 
2886  free_trees(Vcb);
2887 
2888  if (!NT_SUCCESS(Status)) {
2889  ERR("do_write returned %08lx\n", Status);
2890  goto end;
2891  }
2892 
2894  if (!dev) {
2895  ERR("out of memory\n");
2897  goto end;
2898  }
2899 
2900  RtlZeroMemory(dev, sizeof(device));
2901 
2902  dev->devobj = DeviceObject;
2903  dev->fileobj = fileobj;
2904  dev->seeding = false;
2905  init_device(Vcb, dev, true);
2906 
2907  InitializeListHead(&dev->space);
2908 
2909  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2910  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2911  if (!NT_SUCCESS(Status)) {
2912  ERR("add_space_entry returned %08lx\n", Status);
2913  goto end;
2914  }
2915  }
2916 
2917  dev_id = 0;
2918 
2919  le = Vcb->devices.Flink;
2920  while (le != &Vcb->devices) {
2921  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2922 
2923  if (dev2->devitem.dev_id > dev_id)
2924  dev_id = dev2->devitem.dev_id;
2925 
2926  le = le->Flink;
2927  }
2928 
2929  dev_id++;
2930 
2931  dev->devitem.dev_id = dev_id;
2932  dev->devitem.num_bytes = size;
2933  dev->devitem.bytes_used = 0;
2934  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2935  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2936  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2937  dev->devitem.type = 0;
2938  dev->devitem.generation = 0;
2939  dev->devitem.start_offset = 0;
2940  dev->devitem.dev_group = 0;
2941  dev->devitem.seek_speed = 0;
2942  dev->devitem.bandwidth = 0;
2943  get_uuid(&dev->devitem.device_uuid);
2944  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2945 
2947  if (!di) {
2948  ERR("out of memory\n");
2949  goto end;
2950  }
2951 
2952  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2953 
2954  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2955  if (!NT_SUCCESS(Status)) {
2956  ERR("insert_tree_item returned %08lx\n", Status);
2957  ExFreePool(di);
2958  goto end;
2959  }
2960 
2961  // add stats entry to dev tree
2962  stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG);
2963  if (!stats) {
2964  ERR("out of memory\n");
2966  goto end;
2967  }
2968 
2969  RtlZeroMemory(stats, sizeof(uint64_t) * 5);
2970 
2971  searchkey.obj_id = 0;
2972  searchkey.obj_type = TYPE_DEV_STATS;
2973  searchkey.offset = di->dev_id;
2974 
2975  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
2976  if (!NT_SUCCESS(Status)) {
2977  ERR("error - find_item returned %08lx\n", Status);
2978  ExFreePool(stats);
2979  goto end;
2980  }
2981 
2982  if (!keycmp(tp.item->key, searchkey)) {
2984  if (!NT_SUCCESS(Status)) {
2985  ERR("delete_tree_item returned %08lx\n", Status);
2986  ExFreePool(stats);
2987  goto end;
2988  }
2989  }
2990 
2991  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp);
2992  if (!NT_SUCCESS(Status)) {
2993  ERR("insert_tree_item returned %08lx\n", Status);
2994  ExFreePool(stats);
2995  goto end;
2996  }
2997 
2998  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
3000 
3001  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
3002  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
3003  if (!mb) {
3004  ERR("out of memory\n");
3006  goto end;
3007  }
3008 
3009  RtlZeroMemory(mb, 0x100000);
3010 
3011  Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000);
3012  if (!NT_SUCCESS(Status)) {
3013  ERR("write_data_phys returned %08lx\n", Status);
3014  ExFreePool(mb);
3015  goto end;
3016  }
3017 
3018  ExFreePool(mb);
3019 
3020  vde = Vcb->vde;
3021  pdode = vde->pdode;
3022 
3024  if (!vc) {
3025  ERR("out of memory\n");
3027  goto end;
3028  }
3029 
3030  vc->uuid = dev->devitem.device_uuid;
3031  vc->devid = dev_id;
3032  vc->generation = Vcb->superblock.generation;
3033  vc->devobj = DeviceObject;
3034  vc->fileobj = fileobj;
3035  vc->notification_entry = NULL;
3036  vc->boot_volume = false;
3037 
3039  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3040  if (!NT_SUCCESS(Status))
3041  WARN("IoRegisterPlugPlayNotification returned %08lx\n", Status);
3042 
3043  pnp_name2 = pnp_name;
3044 
3045  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3046  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3047  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3048  pnp_name2.Length -= 3 * sizeof(WCHAR);
3049  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3050  }
3051 
3052  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3053 
3054  if (pnp_name2.Length == 0)
3055  vc->pnp_name.Buffer = NULL;
3056  else {
3058  if (!vc->pnp_name.Buffer) {
3059  ERR("out of memory\n");
3061  goto end;
3062  }
3063 
3064  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3065  }
3066 
3067  vc->size = size;
3068  vc->seeding = false;
3069  vc->disk_num = sdn.DeviceNumber;
3070  vc->part_num = sdn.PartitionNumber;
3071  vc->had_drive_letter = false;
3072 
3074  InsertTailList(&pdode->children, &vc->list_entry);
3075  pdode->num_children++;
3076  pdode->children_loaded++;
3078 
3080  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3081  if (!NT_SUCCESS(Status))
3082  ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
3083  else {
3086  WARN("remove_drive_letter returned %08lx\n", Status);
3087 
3089 
3090  ObDereferenceObject(mountmgrfo);
3091  }
3092 
3093  Vcb->superblock.num_devices++;
3094  Vcb->superblock.total_bytes += size;
3095  Vcb->devices_loaded++;
3096  InsertTailList(&Vcb->devices, &dev->list_entry);
3097 
3098  // FIXME - send notification that volume size has increased
3099 
3100  ObReferenceObject(DeviceObject); // for Vcb
3101 
3102  Status = do_write(Vcb, Irp);
3103  if (!NT_SUCCESS(Status))
3104  ERR("do_write returned %08lx\n", Status);
3105 
3106  ObReferenceObject(fileobj);
3107 
3108 end:
3109  free_trees(Vcb);
3110 
3111  ExReleaseResourceLite(&Vcb->tree_lock);
3112 
3113 end2:
3114  ObDereferenceObject(fileobj);
3115 
3116  if (pnp_name.Buffer)
3118 
3119  if (NT_SUCCESS(Status))
3121 
3122  return Status;
3123 }
3124 
3126  fcb* fcb;
3127  ccb* ccb;
3128 
3129  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3130 
3131  if (!FileObject)
3132  return STATUS_INVALID_PARAMETER;
3133 
3134  fcb = FileObject->FsContext;
3135  ccb = FileObject->FsContext2;
3136 
3137  if (!fcb)
3138  return STATUS_INVALID_PARAMETER;
3139 
3140  if (fcb != Vcb->volume_fcb)
3141  return STATUS_INVALID_PARAMETER;
3142 
3143  if (!ccb)
3144  return STATUS_INVALID_PARAMETER;
3145 
3146  ccb->allow_extended_dasd_io = true;
3147 
3148  return STATUS_SUCCESS;
3149 }
3150 
3152  if (length < sizeof(BTRFS_UUID))
3153  return STATUS_BUFFER_OVERFLOW;
3154 
3155  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3156 
3157  return STATUS_SUCCESS;
3158 }
3159 
3161  uint64_t devid;
3162  NTSTATUS Status;
3163  LIST_ENTRY* le;
3164 
3165  if (length < sizeof(uint64_t))
3166  return STATUS_INVALID_PARAMETER;
3167 
3168  if (Vcb->readonly)
3170 
3171  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3173 
3174  devid = *((uint64_t*)data);
3175 
3176  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3177 
3178  le = Vcb->devices.Flink;
3179 
3180  while (le != &Vcb->devices) {
3182 
3183  if (dev->devitem.dev_id == devid) {
3184  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3185  dev->stats_changed = true;
3186  Vcb->stats_changed = true;
3187  Vcb->need_write = true;
3189  goto end;
3190  }
3191 
3192  le = le->Flink;
3193  }
3194 
3195  WARN("device %I64x not found\n", devid);
3197 
3198 end:
3199  ExReleaseResourceLite(&Vcb->tree_lock);
3200 
3201  return Status;
3202 }
3203 
3206 
3207  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3208 
3209  // STUB
3210 
3211  if (!FileObject)
3212  return STATUS_INVALID_PARAMETER;
3213 
3215  return STATUS_INVALID_PARAMETER;
3216 
3217  fgiib->ChecksumAlgorithm = 0;
3218  fgiib->Reserved = 0;
3219  fgiib->Flags = 0;
3220  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3221  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3222 
3223  return STATUS_SUCCESS;
3224 }
3225 
3227  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3228 
3229  // STUB
3230 
3231  if (!FileObject)
3232  return STATUS_INVALID_PARAMETER;
3233 
3235  return STATUS_INVALID_PARAMETER;
3236 
3237  return STATUS_SUCCESS;
3238 }
3239 
3241  LIST_ENTRY* le;
3242 
3243  le = fcb->extents.Flink;
3244  while (le != &fcb->extents) {
3246 
3247  if (!ext->ignore)
3248  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3249 
3250  le = le->Flink;
3251  }
3252 
3253  return false;
3254 }
3255 
3258  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3259  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3260  NTSTATUS Status;
3261  PFILE_OBJECT sourcefo;
3262  uint64_t sourcelen, nbytes = 0;
3263  LIST_ENTRY rollback, *le, newexts;
3265  BTRFS_TIME now;
3266  bool make_inline;
3267 
3268  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3269  return STATUS_BUFFER_TOO_SMALL;
3270 
3271  if (Vcb->readonly)
3273 
3274  if (ded->ByteCount.QuadPart == 0)
3275  return STATUS_SUCCESS;
3276 
3277  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3278  return STATUS_INVALID_PARAMETER;
3279 
3281  return STATUS_ACCESS_DENIED;
3282 
3283  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3284  WARN("insufficient privileges\n");
3285  return STATUS_ACCESS_DENIED;
3286  }
3287 
3288  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3289  return STATUS_INVALID_PARAMETER;
3290 
3291  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3292  if (!NT_SUCCESS(Status)) {
3293  ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3294  return Status;
3295  }
3296 
3297  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3298  WARN("source and destination are on different volumes\n");
3299  ObDereferenceObject(sourcefo);
3300  return STATUS_INVALID_PARAMETER;
3301  }
3302 
3303  sourcefcb = sourcefo->FsContext;
3304  sourceccb = sourcefo->FsContext2;
3305 
3306  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3307  ObDereferenceObject(sourcefo);
3308  return STATUS_INVALID_PARAMETER;
3309  }
3310 
3311  if (!sourcefcb->ads && !fcb->ads) {
3312  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3313  ObDereferenceObject(sourcefo);
3314  return STATUS_INVALID_PARAMETER;
3315  }
3316 
3317  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3318  ObDereferenceObject(sourcefo);
3319  return STATUS_INVALID_PARAMETER;
3320  }
3321  }
3322 
3323  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3324  WARN("insufficient privileges\n");
3325  ObDereferenceObject(sourcefo);
3326  return STATUS_ACCESS_DENIED;
3327  }
3328 
3329  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3330  ObDereferenceObject(sourcefo);
3331  return STATUS_INVALID_PARAMETER;
3332  }
3333 
3334  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3335 
3336  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) {
3337  ObDereferenceObject(sourcefo);
3338  return STATUS_NOT_SUPPORTED;
3339  }
3340 
3341  if (fcb == sourcefcb &&
3344  WARN("source and destination are the same, and the ranges overlap\n");
3345  ObDereferenceObject(sourcefo);
3346  return STATUS_INVALID_PARAMETER;
3347  }
3348 
3349  // fail if nocsum flag set on one file but not the other
3350  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3351  ObDereferenceObject(sourcefo);
3352  return STATUS_INVALID_PARAMETER;
3353  }
3354 
3356  InitializeListHead(&newexts);
3357 
3358  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3359 
3360  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3361 
3362  if (fcb != sourcefcb)
3363  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true);
3364 
3367  goto end;
3368  }
3369 
3370  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3372  goto end;
3373  }
3374 
3375  make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3376 
3377  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3378  uint8_t* data2;
3379  ULONG bytes_read, dataoff, datalen2;
3380 
3381  if (make_inline) {
3382  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3383  datalen2 = (ULONG)fcb->inode_item.st_size;
3384  } else if (fcb->ads) {
3385  dataoff = 0;
3386  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3387  } else {
3388  dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3389  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3390  }
3391 
3393  if (!data2) {
3394  ERR("out of memory\n");
3396  goto end;
3397  }
3398 
3399  if (dataoff > 0) {
3400  if (make_inline)
3401  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3402  else
3403  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3404 
3405  if (!NT_SUCCESS(Status)) {
3406  ERR("read_file returned %08lx\n", Status);
3407  ExFreePool(data2);
3408  goto end;
3409  }
3410  }
3411 
3412  if (sourcefcb->ads) {
3413  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3414  if (!NT_SUCCESS(Status)) {
3415  ERR("read_stream returned %08lx\n", Status);
3416  ExFreePool(data2);
3417  goto end;
3418  }
3419  } else {
3420  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3421  if (!NT_SUCCESS(Status)) {
3422  ERR("read_file returned %08lx\n", Status);
3423  ExFreePool(data2);
3424  goto end;
3425  }
3426  }
3427 
3428  if (dataoff + bytes_read < datalen2)
3429  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3430 
3431  if (fcb->ads)
3433  else if (make_inline) {
3434  uint16_t edsize;
3435  EXTENT_DATA* ed;
3436 
3437  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3438  if (!NT_SUCCESS(Status)) {
3439  ERR("excise_extents returned %08lx\n", Status);
3440  ExFreePool(data2);
3441  goto end;
3442  }
3443 
3444  edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3445 
3447  if (!ed) {
3448  ERR("out of memory\n");
3449  ExFreePool(data2);
3451  goto end;
3452  }
3453 
3454  ed->generation = Vcb->superblock.generation;
3460 
3461  RtlCopyMemory(ed->data, data2, datalen2);
3462 
3463  Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback);
3464  if (!NT_SUCCESS(Status)) {
3465  ERR("add_extent_to_fcb returned %08lx\n", Status);
3466  ExFreePool(data2);
3467  goto end;
3468  }
3469 
3470  fcb->inode_item.st_blocks += datalen2;
3471  } else {
3472  uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3473 
3474  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback);
3475  if (!NT_SUCCESS(Status)) {
3476  ERR("do_write_file returned %08lx\n", Status);
3477  ExFreePool(data2);
3478  goto end;
3479  }
3480  }
3481 
3482  ExFreePool(data2);
3483  } else {
3484  LIST_ENTRY* lastextle;
3485 
3486  le = sourcefcb->extents.Flink;
3487  while (le != &sourcefcb->extents) {
3489 
3490  if (!ext->ignore) {
3491  if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart)
3492  break;
3493 
3494  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3495  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3496  extent* ext2;
3497  EXTENT_DATA2 *ed2s, *ed2d;
3498  chunk* c;
3499 
3500  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3501 
3502  if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) {
3503  le = le->Flink;
3504  continue;
3505  }
3506 
3508  if (!ext2) {
3509  ERR("out of memory\n");
3511  goto end;
3512  }
3513 
3514  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart)
3515  ext2->offset = ded->TargetFileOffset.QuadPart;
3516  else
3517  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3518 
3519  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3520  ext2->unique = false;
3521  ext2->ignore = false;
3522  ext2->inserted = true;
3523 
3524  ext2->extent_data.generation = Vcb->superblock.generation;
3525  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3526  ext2->extent_data.compression = ext->extent_data.compression;
3527  ext2->extent_data.encryption = ext->extent_data.encryption;
3528  ext2->extent_data.encoding = ext->extent_data.encoding;
3529  ext2->extent_data.type = ext->extent_data.type;
3530 
3531  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3532 
3533  ed2d->address = ed2s->address;
3534  ed2d->size = ed2s->size;
3535 
3536  if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) {
3537  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3538  ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3539  } else {
3540  ed2d->offset = ed2s->offset;
3541  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3542  }
3543 
3544  if (ext->csum) {
3545  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3546  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * Vcb->csum_size / Vcb->superblock.sector_size), ALLOC_TAG);
3547  if (!ext2->csum) {
3548  ERR("out of memory\n");
3550  ExFreePool(ext2);
3551  goto end;
3552  }
3553 
3554  RtlCopyMemory(ext2->csum, (uint8_t*)ext->csum + ((ed2d->offset - ed2s->offset) * Vcb->csum_size / Vcb->superblock.sector_size),
3555  (ULONG)(ed2d->num_bytes * Vcb->csum_size / Vcb->superblock.sector_size));
3556  } else {
3557  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * Vcb->csum_size / Vcb->superblock.sector_size), ALLOC_TAG);
3558  if (!ext2->csum) {
3559  ERR("out of memory\n");
3561  ExFreePool(ext2);
3562  goto end;
3563  }
3564 
3565  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * Vcb->csum_size / Vcb->superblock.sector_size));
3566  }
3567  } else
3568  ext2->csum = NULL;
3569 
3570  InsertTailList(&newexts, &ext2->list_entry);
3571 
3572  c = get_chunk_from_address(Vcb, ed2s->address);
3573  if (!c) {
3574  ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address);
3576  goto end;
3577  }
3578 
3579  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3580  1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
3581  if (!NT_SUCCESS(Status)) {
3582  ERR("update_changed_extent_ref returned %08lx\n", Status);
3583  goto end;
3584  }
3585 
3586  nbytes += ed2d->num_bytes;
3587  }
3588  }
3589 
3590  le = le->Flink;
3591  }
3592 
3594  if (!NT_SUCCESS(Status)) {
3595  ERR("excise_extents returned %08lx\n", Status);
3596 
3597  while (!IsListEmpty(&newexts)) {
3599  ExFreePool(ext);
3600  }
3601 
3602  goto end;
3603  }
3604 
3605  // clear unique flags in source fcb
3606  le = sourcefcb->extents.Flink;
3607  while (le != &sourcefcb->extents) {
3609 
3610  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3611  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3612  LIST_ENTRY* le2;
3613 
3614  le2 = newexts.Flink;
3615  while (le2 != &newexts) {
3617 
3618  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3619  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3620 
3621  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3622  ext->unique = false;
3623  break;
3624  }
3625  }
3626 
3627  le2 = le2->Flink;
3628  }
3629  }
3630 
3631  le = le->Flink;
3632  }
3633 
3634  lastextle = &fcb->extents;
3635  while (!IsListEmpty(&newexts)) {
3637 
3638  add_extent(fcb, lastextle, ext);
3639  lastextle = &ext->list_entry;
3640  }
3641  }
3642 
3645 
3646  if (fcb->ads) {
3647  ccb->fileref->parent->fcb->inode_item.sequence++;
3648 
3649  if (!ccb->user_set_change_time)
3650  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3651 
3652  ccb->fileref->parent->fcb->inode_item_changed = true;
3653  mark_fcb_dirty(ccb->fileref->parent->fcb);
3654  } else {
3655  fcb->inode_item.st_blocks += nbytes;
3656  fcb->inode_item.sequence++;
3657 
3658  if (!ccb->user_set_change_time)
3660 
3661  if (!ccb->user_set_write_time) {
3664  }
3665 
3666  fcb->inode_item_changed = true;
3667  fcb->extents_changed = true;
3668  }
3669 
3671 
3672  if (FileObject->SectionObjectPointer->DataSectionObject)
3673  CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false);
3674 
3676 
3677 end:
3678  ObDereferenceObject(sourcefo);
3679 
3680  if (NT_SUCCESS(Status))
3682  else
3684 
3685  if (fcb != sourcefcb)
3686  ExReleaseResourceLite(sourcefcb->Header.Resource);
3687 
3688  ExReleaseResourceLite(fcb->Header.Resource);
3689 
3690  ExReleaseResourceLite(&Vcb->tree_lock);
3691 
3692  return Status;
3693 }
3694 
3696  NTSTATUS Status;
3697  btrfs_mknod* bmn;
3698  fcb *parfcb, *fcb;
3699  ccb* parccb;
3700  file_ref *parfileref, *fileref;
3702  root* subvol;
3703  uint64_t inode;
3704  dir_child* dc;
3706  BTRFS_TIME now;
3707  LIST_ENTRY* lastle;
3708  ANSI_STRING utf8;
3709  ULONG len, i;
3710  SECURITY_SUBJECT_CONTEXT subjcont;
3711  PSID owner;
3712  BOOLEAN defaulted;
3713 
3714  TRACE("(%p, %p, %p, %lu)\n", Vcb, FileObject, data, datalen);
3715 
3716  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3717  return STATUS_INVALID_PARAMETER;
3718 
3719  if (Vcb->readonly)
3721 
3722  parfcb = FileObject->FsContext;
3723 
3724  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3725  WARN("trying to create file in something other than a directory\n");
3726  return STATUS_INVALID_PARAMETER;
3727  }
3728 
3729  if (is_subvol_readonly(parfcb->subvol, Irp))
3730  return STATUS_ACCESS_DENIED;
3731 
3732  parccb = FileObject->FsContext2;
3733  parfileref = parccb->fileref;
3734 
3735  if (!parfileref)
3736  return STATUS_INVALID_PARAMETER;
3737 
3738  if (datalen < sizeof(btrfs_mknod))
3739  return STATUS_INVALID_PARAMETER;
3740 
3741  bmn = (btrfs_mknod*)data;
3742 
3743  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3744  return STATUS_INVALID_PARAMETER;
3745 
3746  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3747  return STATUS_INVALID_PARAMETER;
3748 
3749  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3750  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3751  WARN("insufficient privileges\n");
3752  return STATUS_ACCESS_DENIED;
3753  }
3754 
3755  if (bmn->inode != 0) {
3756  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3758  }
3759 
3760  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3761  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3763  }
3764 
3765  // don't allow files called . or ..
3766  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3768 
3769  Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen);
3770  if (!NT_SUCCESS(Status)) {
3771  ERR("utf16_to_utf8 returned %08lx\n", Status);
3772  return Status;
3773  }
3774 
3775  if (len == 0) {
3776  ERR("utf16_to_utf8 returned a length of 0\n");
3777  return STATUS_INTERNAL_ERROR;
3778  }
3779 
3780  if (len > 0xffff) {
3781  ERR("len was too long (%lx)\n", len);
3782  return STATUS_INVALID_PARAMETER;
3783  }
3784 
3785  utf8.MaximumLength = utf8.Length = (USHORT)len;
3787 
3788  if (!utf8.Buffer) {
3789  ERR("out of memory\n");
3791  }
3792 
3793  Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3794  if (!NT_SUCCESS(Status)) {
3795  ERR("utf16_to_utf8 failed with error %08lx\n", Status);
3796  ExFreePool(utf8.Buffer);
3797  return Status;
3798  }
3799 
3800  name.Length = name.MaximumLength = bmn->namelen;
3801  name.Buffer = bmn->name;
3802 
3803  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true);
3805  ERR("find_file_in_dir returned %08lx\n", Status);
3806  goto end;
3807  }
3808 
3809  if (NT_SUCCESS(Status)) {
3810  WARN("filename already exists\n");
3812  goto end;
3813  }
3814 
3817 
3819  if (!fcb) {
3820  ERR("out of memory\n");
3822  goto end;
3823  }
3824 
3825  fcb->Vcb = Vcb;
3826 
3827  fcb->inode_item.generation = Vcb->superblock.generation;
3828  fcb->inode_item.transid = Vcb->superblock.generation;
3829  fcb->inode_item.st_size = 0;
3830  fcb->inode_item.st_blocks = 0;
3831  fcb->inode_item.block_group = 0;
3832  fcb->inode_item.st_nlink = 1;
3836 
3837  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3838  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3839  else
3840  fcb->inode_item.st_rdev = 0;
3841 
3842  fcb->inode_item.flags = 0;
3843  fcb->inode_item.sequence = 1;
3847  fcb->inode_item.otime = now;
3848 
3849  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3851  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3853  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3855  else if (bmn->type == BTRFS_TYPE_FIFO)
3857  else if (bmn->type == BTRFS_TYPE_SOCKET)
3859  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3861  else
3863 
3864  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3865  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3866 
3867  // inherit nodatacow flag from parent directory
3868  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3870 
3871  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3873  }
3874 
3875  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3877 
3880 
3881  fcb->inode_item_changed = true;
3882 
3883  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3884  fcb->Header.AllocationSize.QuadPart = 0;
3885  fcb->Header.FileSize.QuadPart = 0;
3886  fcb->Header.ValidDataLength.QuadPart = 0;
3887 
3888  fcb->atts = 0;
3889 
3890  if (bmn->name[0] == '.')
3892 
3893  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3895 
3896  fcb->atts_changed = false;
3897 
3898  InterlockedIncrement(&parfcb->refcount);
3899  fcb->subvol = parfcb->subvol;
3900 
3901  SeCaptureSubjectContext(&subjcont);
3902 
3903  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3905 
3906  if (!NT_SUCCESS(Status)) {
3907  ERR("SeAssignSecurityEx returned %08lx\n", Status);
3908  reap_fcb(fcb);
3909  goto end;
3910  }
3911 
3912  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3913  if (!NT_SUCCESS(Status)) {
3914  WARN("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
3915  fcb->sd_dirty = true;
3916  } else {
3917  fcb->inode_item.st_uid = sid_to_uid(owner);
3919  }
3920 
3921  find_gid(fcb, parfcb, &subjcont);
3922 
3923  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3924  acquire_fcb_lock_exclusive(Vcb);
3925 
3926  if (bmn->inode == 0) {
3927  inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3928  lastle = parfcb->subvol->fcbs.Blink;
3929  } else {
3930  if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) {
3931  inode = parfcb->subvol->lastinode = bmn->inode;
3932  lastle = parfcb->subvol->fcbs.Blink;
3933  } else {
3934  LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3935 
3936  lastle = parfcb->subvol->fcbs.Blink;;
3937  while (le != &parfcb->subvol->fcbs) {
3938  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3939 
3940  if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3941  release_fcb_lock(Vcb);
3942  ExReleaseResourceLite(&Vcb->fileref_lock);
3943 
3944  WARN("inode collision\n");
3946  goto end;
3947  } else if (fcb2->inode > bmn->inode) {
3948  lastle = fcb2->list_entry.Blink;
3949  break;
3950  }
3951 
3952  le = le->Flink;
3953  }
3954 
3955  inode = bmn->inode;
3956  }
3957  }
3958 
3959  fcb->inode = inode;
3960  fcb->type = bmn->type;
3961 
3963  if (!fileref) {
3964  release_fcb_lock(Vcb);
3965  ExReleaseResourceLite(&Vcb->fileref_lock);
3966 
3967  ERR("out of memory\n");
3968  reap_fcb(fcb);
3970  goto end;
3971  }
3972 
3973  fileref->fcb = fcb;
3974 
3975  fcb->created = true;
3976  fileref->created = true;
3977 
3978  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3979  fcb->subvol->root_item.ctime = now;
3980 
3981  fileref->parent = parfileref;
3982 
3985 
3986  Status = add_dir_child(fileref->parent->fcb, fcb->inode, false, &utf8, &name, fcb->type, &dc);
3987  if (!NT_SUCCESS(Status))
3988  WARN("add_dir_child returned %08lx\n", Status);
3989 
3990  fileref->dc = dc;
3991  dc->fileref = fileref;
3992 
3993  ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
3994  InsertTailList(&parfileref->children, &fileref->list_entry);
3995  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
3996 
3997  increase_fileref_refcount(parfileref);
3998 
3999  if (fcb->type == BTRFS_TYPE_DIRECTORY) {
4001  if (!fcb->hash_ptrs) {
4002  release_fcb_lock(Vcb);
4003  ExReleaseResourceLite(&Vcb->fileref_lock);
4004 
4005