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