ReactOS  0.4.13-dev-464-g6b95727
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, FALSE, fcb, &fr->fcb, PagedPool, Irp);
436  if (!NT_SUCCESS(Status)) {
437  ERR("open_fcb returned %08x\n", Status);
438  free_fileref(fr);
439  goto end;
440  }
441 
442  fr->parent = fileref;
443 
444  Status = add_dir_child(fileref->fcb, r->id, TRUE, utf8, name, BTRFS_TYPE_DIRECTORY, &dc);
445  if (!NT_SUCCESS(Status))
446  WARN("add_dir_child returned %08x\n", Status);
447 
448  fr->dc = dc;
449  dc->fileref = fr;
450 
451  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
452  InsertTailList(&fileref->children, &fr->list_entry);
453  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
454 
456 
457  fr->created = TRUE;
458  mark_fileref_dirty(fr);
459 
460  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
461  fr->fcb->fileref = fr;
462 
463  fr->fcb->subvol->parent = fileref->fcb->subvol->id;
464 
465  free_fileref(fr);
466 
467  // change fcb's INODE_ITEM
468 
469  fcb->inode_item.transid = Vcb->superblock.generation;
471  fcb->inode_item.st_size += utf8->Length * 2;
472 
475 
476  if (!ccb->user_set_write_time)
478 
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(fr2);
646  goto end3;
647  } else
648  free_fileref(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(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(fr2);
871  goto end;
872  } else
873  free_fileref(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  r->fcbs_version++;
1009  release_fcb_lock(Vcb);
1010 
1011  rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
1012  rootfcb->Header.AllocationSize.QuadPart = 0;
1013  rootfcb->Header.FileSize.QuadPart = 0;
1014  rootfcb->Header.ValidDataLength.QuadPart = 0;
1015 
1016  rootfcb->created = TRUE;
1017 
1018  if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
1020 
1021  rootfcb->prop_compression = fileref->fcb->prop_compression;
1023 
1024  r->lastinode = rootfcb->inode;
1025 
1026  // add INODE_REF
1027 
1028  irsize = (UINT16)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
1029  ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
1030  if (!ir) {
1031  ERR("out of memory\n");
1033  goto end;
1034  }
1035 
1036  ir->index = 0;
1037  ir->n = sizeof(DOTDOT) - 1;
1038  RtlCopyMemory(ir->name, DOTDOT, ir->n);
1039 
1040  Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
1041  if (!NT_SUCCESS(Status)) {
1042  ERR("insert_tree_item returned %08x\n", Status);
1043  ExFreePool(ir);
1044  goto end;
1045  }
1046 
1047  // create fileref for entry in other subvolume
1048 
1049  fr = create_fileref(Vcb);
1050  if (!fr) {
1051  ERR("out of memory\n");
1052 
1053  reap_fcb(rootfcb);
1054 
1056  goto end;
1057  }
1058 
1059  fr->fcb = rootfcb;
1060 
1061  mark_fcb_dirty(rootfcb);
1062 
1063  fr->parent = fileref;
1064 
1065  Status = add_dir_child(fileref->fcb, r->id, TRUE, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
1066  if (!NT_SUCCESS(Status))
1067  WARN("add_dir_child returned %08x\n", Status);
1068 
1069  fr->dc = dc;
1070  dc->fileref = fr;
1071 
1073  if (!fr->fcb->hash_ptrs) {
1074  ERR("out of memory\n");
1075  free_fileref(fr);
1077  goto end;
1078  }
1079 
1080  RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
1081 
1083  if (!fr->fcb->hash_ptrs_uc) {
1084  ERR("out of memory\n");
1085  free_fileref(fr);
1087  goto end;
1088  }
1089 
1090  RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
1091 
1092  ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
1093  InsertTailList(&fileref->children, &fr->list_entry);
1094  ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
1095 
1097 
1098  if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
1099  fr->fcb->fileref = fr;
1100 
1101  fr->created = TRUE;
1102  mark_fileref_dirty(fr);
1103 
1104  // change fcb->subvol's ROOT_ITEM
1105 
1106  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1107  fcb->subvol->root_item.ctime = now;
1108 
1109  // change fcb's INODE_ITEM
1110 
1111  fcb->inode_item.transid = Vcb->superblock.generation;
1112  fcb->inode_item.st_size += utf8.Length * 2;
1113  fcb->inode_item.sequence++;
1114 
1115  if (!ccb->user_set_change_time)
1117 
1118  if (!ccb->user_set_write_time)
1120 
1123 
1124  fr->fcb->subvol->parent = fcb->subvol->id;
1125 
1127 
1128 end:
1129  if (!NT_SUCCESS(Status)) {
1130  if (fr) {
1131  fr->deleted = TRUE;
1132  mark_fileref_dirty(fr);
1133  } else if (rootfcb) {
1134  rootfcb->deleted = TRUE;
1135  mark_fcb_dirty(rootfcb);
1136  }
1137 
1138  if (r) {
1139  RemoveEntryList(&r->list_entry);
1140  InsertTailList(&Vcb->drop_roots, &r->list_entry);
1141  }
1142  }
1143 
1144  ExReleaseResourceLite(&Vcb->tree_lock);
1145 
1146  if (NT_SUCCESS(Status)) {
1149  }
1150 
1151 end2:
1152  if (fr)
1153  free_fileref(fr);
1154 
1155  return Status;
1156 }
1157 
1159  btrfs_inode_info2* bii = data;
1160  fcb* fcb;
1161  ccb* ccb;
1162  BOOL old_style;
1163 
1164  if (length < sizeof(btrfs_inode_info))
1165  return STATUS_BUFFER_OVERFLOW;
1166 
1167  if (!FileObject)
1168  return STATUS_INVALID_PARAMETER;
1169 
1170  fcb = FileObject->FsContext;
1171 
1172  if (!fcb)
1173  return STATUS_INVALID_PARAMETER;
1174 
1175  ccb = FileObject->FsContext2;
1176 
1177  if (!ccb)
1178  return STATUS_INVALID_PARAMETER;
1179 
1180  if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
1181  WARN("insufficient privileges\n");
1182  return STATUS_ACCESS_DENIED;
1183  }
1184 
1185  if (fcb->ads)
1186  fcb = ccb->fileref->parent->fcb;
1187 
1188  old_style = length < sizeof(btrfs_inode_info2);
1189 
1191 
1192  bii->subvol = fcb->subvol->id;
1193  bii->inode = fcb->inode;
1194  bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
1195  bii->type = fcb->type;
1196  bii->st_uid = fcb->inode_item.st_uid;
1197  bii->st_gid = fcb->inode_item.st_gid;
1198  bii->st_mode = fcb->inode_item.st_mode;
1199 
1200  if (fcb->inode_item.st_rdev == 0)
1201  bii->st_rdev = 0;
1202  else
1203  bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF);
1204 
1205  bii->flags = fcb->inode_item.flags;
1206 
1207  bii->inline_length = 0;
1208  bii->disk_size_uncompressed = 0;
1209  bii->disk_size_zlib = 0;
1210  bii->disk_size_lzo = 0;
1211 
1212  if (!old_style) {
1213  bii->disk_size_zstd = 0;
1214  bii->sparse_size = 0;
1215  }
1216 
1217  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
1218  UINT64 last_end = 0;
1219  LIST_ENTRY* le;
1220  BOOL extents_inline = FALSE;
1221 
1222  le = fcb->extents.Flink;
1223  while (le != &fcb->extents) {
1225 
1226  if (!ext->ignore) {
1227  if (!old_style && ext->offset > last_end)
1228  bii->sparse_size += ext->offset - last_end;
1229 
1230  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1231  bii->inline_length += ext->datalen - (UINT16)offsetof(EXTENT_DATA, data[0]);
1232  last_end = ext->offset + ext->extent_data.decoded_size;
1233  extents_inline = TRUE;
1234  } else {
1235  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1236 
1237  // FIXME - compressed extents with a hole in them are counted more than once
1238  if (ed2->size != 0) {
1239  switch (ext->extent_data.compression) {
1242  break;
1243 
1245  bii->disk_size_zlib += ed2->size;
1246  break;
1247 
1248  case BTRFS_COMPRESSION_LZO:
1249  bii->disk_size_lzo += ed2->size;
1250  break;
1251 
1253  if (!old_style)
1254  bii->disk_size_zstd += ed2->size;
1255  break;
1256  }
1257  }
1258 
1259  last_end = ext->offset + ed2->num_bytes;
1260  }
1261  }
1262 
1263  le = le->Flink;
1264  }
1265 
1266  if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end)
1267  bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end;
1268  }
1269 
1270  switch (fcb->prop_compression) {
1271  case PropCompression_Zlib:
1273  break;
1274 
1275  case PropCompression_LZO:
1277  break;
1278 
1279  case PropCompression_ZSTD:
1281  break;
1282 
1283  default:
1285  break;
1286  }
1287 
1288  ExReleaseResourceLite(fcb->Header.Resource);
1289 
1290  return STATUS_SUCCESS;
1291 }
1292 
1294  btrfs_set_inode_info* bsii = data;
1295  NTSTATUS Status;
1296  fcb* fcb;
1297  ccb* ccb;
1298 
1299  if (length < sizeof(btrfs_set_inode_info))
1300  return STATUS_INVALID_PARAMETER;
1301 
1302  if (!FileObject)
1303  return STATUS_INVALID_PARAMETER;
1304 
1305  fcb = FileObject->FsContext;
1306 
1307  if (!fcb)
1308  return STATUS_INVALID_PARAMETER;
1309 
1310  ccb = FileObject->FsContext2;
1311 
1312  if (!ccb)
1313  return STATUS_INVALID_PARAMETER;
1314 
1315  if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1316  WARN("insufficient privileges\n");
1317  return STATUS_ACCESS_DENIED;
1318  }
1319 
1320  if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) {
1321  WARN("insufficient privileges\n");
1322  return STATUS_ACCESS_DENIED;
1323  }
1324 
1326  return STATUS_INVALID_PARAMETER;
1327 
1328  if (fcb->ads)
1329  fcb = ccb->fileref->parent->fcb;
1330 
1331  if (is_subvol_readonly(fcb->subvol, Irp)) {
1332  WARN("trying to change inode on readonly subvolume\n");
1333  return STATUS_ACCESS_DENIED;
1334  }
1335 
1337 
1338  if (bsii->flags_changed) {
1339  if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
1341  WARN("trying to change nocow flag on non-empty file\n");
1343  goto end;
1344  }
1345 
1346  fcb->inode_item.flags = bsii->flags;
1347 
1350  else
1352  }
1353 
1354  if (bsii->mode_changed) {
1356  S_ISGID | S_ISVTX;
1357 
1358  if (ccb->access & WRITE_OWNER)
1359  allowed |= S_ISUID;
1360 
1361  fcb->inode_item.st_mode &= ~allowed;
1362  fcb->inode_item.st_mode |= bsii->st_mode & allowed;
1363  }
1364 
1365  if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) {
1366  fcb->inode_item.st_uid = bsii->st_uid;
1367 
1368  fcb->sd_dirty = TRUE;
1369  fcb->sd_deleted = FALSE;
1370  }
1371 
1372  if (bsii->gid_changed)
1373  fcb->inode_item.st_gid = bsii->st_gid;
1374 
1375  if (bsii->compression_type_changed) {
1376  switch (bsii->compression_type) {
1377  case BTRFS_COMPRESSION_ANY:
1379  break;
1380 
1383  break;
1384 
1385  case BTRFS_COMPRESSION_LZO:
1387  break;
1388 
1391  break;
1392  }
1393 
1395  }
1396 
1397  if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
1400  }
1401 
1403 
1404 end:
1405  ExReleaseResourceLite(fcb->Header.Resource);
1406 
1407  return Status;
1408 }
1409 
1411  btrfs_device* dev = NULL;
1412  NTSTATUS Status;
1413  LIST_ENTRY* le;
1414 
1415  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1416 
1417  le = Vcb->devices.Flink;
1418  while (le != &Vcb->devices) {
1419  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
1420  ULONG structlen;
1421 
1422  if (length < sizeof(btrfs_device) - sizeof(WCHAR)) {
1424  goto end;
1425  }
1426 
1427  if (!dev)
1428  dev = data;
1429  else {
1430  dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1431  dev = (btrfs_device*)((UINT8*)dev + dev->next_entry);
1432  }
1433 
1434  structlen = length - offsetof(btrfs_device, namelen);
1435 
1436  if (dev2->devobj) {
1437  Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, TRUE, NULL);
1438  if (!NT_SUCCESS(Status))
1439  goto end;
1440 
1441  dev->missing = FALSE;
1442  } else {
1443  dev->namelen = 0;
1444  dev->missing = TRUE;
1445  }
1446 
1447  dev->next_entry = 0;
1448  dev->dev_id = dev2->devitem.dev_id;
1449  dev->readonly = (Vcb->readonly || dev2->readonly) ? TRUE : FALSE;
1450  dev->device_number = dev2->disk_num;
1451  dev->partition_number = dev2->part_num;
1452  dev->size = dev2->devitem.num_bytes;
1453 
1454  if (dev2->devobj) {
1456 
1457  Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL);
1458  if (!NT_SUCCESS(Status))
1459  goto end;
1460 
1461  dev->max_size = gli.Length.QuadPart;
1462  } else
1463  dev->max_size = dev->size;
1464 
1465  RtlCopyMemory(dev->stats, dev2->stats, sizeof(UINT64) * 5);
1466 
1467  length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
1468 
1469  le = le->Flink;
1470  }
1471 
1472 end:
1473  ExReleaseResourceLite(&Vcb->tree_lock);
1474 
1475  return Status;
1476 }
1477 
1480  btrfs_usage* lastbue = NULL;
1481  NTSTATUS Status;
1482  LIST_ENTRY* le;
1483 
1484  if (length < sizeof(btrfs_usage))
1485  return STATUS_BUFFER_OVERFLOW;
1486 
1487  if (!Vcb->chunk_usage_found) {
1488  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1489 
1490  if (!Vcb->chunk_usage_found)
1492  else
1494 
1495  ExReleaseResourceLite(&Vcb->tree_lock);
1496 
1497  if (!NT_SUCCESS(Status)) {
1498  ERR("find_chunk_usage returned %08x\n", Status);
1499  return Status;
1500  }
1501  }
1502 
1504 
1505  ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
1506 
1507  le = Vcb->chunks.Flink;
1508  while (le != &Vcb->chunks) {
1509  BOOL addnew = FALSE;
1510 
1512 
1513  if (!lastbue) // first entry
1514  addnew = TRUE;
1515  else {
1516  btrfs_usage* bue = usage;
1517 
1518  addnew = TRUE;
1519 
1520  while (TRUE) {
1521  if (bue->type == c->chunk_item->type) {
1522  addnew = FALSE;
1523  break;
1524  }
1525 
1526  if (bue->next_entry == 0)
1527  break;
1528  else
1529  bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
1530  }
1531  }
1532 
1533  if (addnew) {
1534  btrfs_usage* bue;
1535  LIST_ENTRY* le2;
1536  UINT64 factor;
1537 
1538  if (!lastbue) {
1539  bue = usage;
1540  } else {
1541  if (length < offsetof(btrfs_usage, devices)) {
1543  goto end;
1544  }
1545 
1547 
1548  lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device));
1549 
1550  bue = (btrfs_usage*)((UINT8*)lastbue + lastbue->next_entry);
1551  }
1552 
1553  bue->next_entry = 0;
1554  bue->type = c->chunk_item->type;
1555  bue->size = 0;
1556  bue->used = 0;
1557  bue->num_devices = 0;
1558 
1559  if (c->chunk_item->type & BLOCK_FLAG_RAID0)
1560  factor = c->chunk_item->num_stripes;
1561  else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
1562  factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
1563  else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
1564  factor = c->chunk_item->num_stripes - 1;
1565  else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
1566  factor = c->chunk_item->num_stripes - 2;
1567  else
1568  factor = 1;
1569 
1570  le2 = le;
1571  while (le2 != &Vcb->chunks) {
1572  chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
1573 
1574  if (c2->chunk_item->type == c->chunk_item->type) {
1575  UINT16 i;
1577  UINT64 stripesize;
1578 
1579  bue->size += c2->chunk_item->size;
1580  bue->used += c2->used;
1581 
1582  stripesize = c2->chunk_item->size / factor;
1583 
1584  for (i = 0; i < c2->chunk_item->num_stripes; i++) {
1585  UINT64 j;
1586  BOOL found = FALSE;
1587 
1588  for (j = 0; j < bue->num_devices; j++) {
1589  if (bue->devices[j].dev_id == cis[i].dev_id) {
1590  bue->devices[j].alloc += stripesize;
1591  found = TRUE;
1592  break;
1593  }
1594  }
1595 
1596  if (!found) {
1597  if (length < sizeof(btrfs_usage_device)) {
1599  goto end;
1600  }
1601 
1602  length -= sizeof(btrfs_usage_device);
1603 
1604  bue->devices[bue->num_devices].dev_id = cis[i].dev_id;
1605  bue->devices[bue->num_devices].alloc = stripesize;
1606  bue->num_devices++;
1607  }
1608  }
1609  }
1610 
1611  le2 = le2->Flink;
1612  }
1613 
1614  lastbue = bue;
1615  }
1616 
1617  le = le->Flink;
1618  }
1619 
1621 
1622 end:
1623  ExReleaseResourceLite(&Vcb->chunk_lock);
1624 
1625  return Status;
1626 }
1627 
1629  NTSTATUS Status;
1630  ULONG cc;
1632  BOOL verify = FALSE;
1633  LIST_ENTRY* le;
1634 
1635  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1636 
1637  le = Vcb->devices.Flink;
1638  while (le != &Vcb->devices) {
1640 
1641  if (dev->devobj && dev->removable) {
1642  Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), FALSE, &iosb);
1643 
1644  if (iosb.Information != sizeof(ULONG))
1645  cc = 0;
1646 
1647  if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) {
1648  dev->devobj->Flags |= DO_VERIFY_VOLUME;
1649  verify = TRUE;
1650  }
1651 
1652  if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG))
1653  dev->change_count = cc;
1654 
1655  if (!NT_SUCCESS(Status) || verify) {
1657  ExReleaseResourceLite(&Vcb->tree_lock);
1658 
1659  return verify ? STATUS_VERIFY_REQUIRED : Status;
1660  }
1661  }
1662 
1663  le = le->Flink;
1664  }
1665 
1666  ExReleaseResourceLite(&Vcb->tree_lock);
1667 
1668  return STATUS_SUCCESS;
1669 }
1670 
1671 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
1672  FILESYSTEM_STATISTICS* fss;
1673 
1674  WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
1675 
1676  // This is hideously wrong, but at least it stops SMB from breaking
1677 
1678  if (buflen < sizeof(FILESYSTEM_STATISTICS))
1679  return STATUS_BUFFER_TOO_SMALL;
1680 
1681  fss = buffer;
1682  RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS));
1683 
1684  fss->Version = 1;
1687 
1688  *retlen = sizeof(FILESYSTEM_STATISTICS);
1689 
1690  return STATUS_SUCCESS;
1691 }
1692 
1694  FILE_SET_SPARSE_BUFFER* fssb = data;
1695  NTSTATUS Status;
1696  BOOL set;
1697  fcb* fcb;
1698  ccb* ccb = FileObject->FsContext2;
1699  file_ref* fileref = ccb ? ccb->fileref : NULL;
1700 
1701  if (data && length < sizeof(FILE_SET_SPARSE_BUFFER))
1702  return STATUS_INVALID_PARAMETER;
1703 
1704  if (!FileObject) {
1705  ERR("FileObject was NULL\n");
1706  return STATUS_INVALID_PARAMETER;
1707  }
1708 
1709  fcb = FileObject->FsContext;
1710 
1711  if (!fcb) {
1712  ERR("FCB was NULL\n");
1713  return STATUS_INVALID_PARAMETER;
1714  }
1715 
1716  if (!ccb) {
1717  ERR("CCB was NULL\n");
1718  return STATUS_INVALID_PARAMETER;
1719  }
1720 
1721  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
1722  WARN("insufficient privileges\n");
1723  return STATUS_ACCESS_DENIED;
1724  }
1725 
1726  if (!fileref) {
1727  ERR("no fileref\n");
1728  return STATUS_INVALID_PARAMETER;
1729  }
1730 
1731  if (fcb->ads) {
1732  fileref = fileref->parent;
1733  fcb = fileref->fcb;
1734  }
1735 
1736  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1738 
1739  if (fcb->type != BTRFS_TYPE_FILE) {
1740  WARN("FileObject did not point to a file\n");
1742  goto end;
1743  }
1744 
1745  if (fssb)
1746  set = fssb->SetSparse;
1747  else
1748  set = TRUE;
1749 
1750  if (set) {
1752  fcb->atts_changed = TRUE;
1753  } else {
1754  ULONG defda;
1755 
1757  fcb->atts_changed = TRUE;
1758 
1760  fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
1761 
1762  fcb->atts_deleted = defda == fcb->atts;
1763  }
1764 
1767 
1769 
1770 end:
1771  ExReleaseResourceLite(fcb->Header.Resource);
1772  ExReleaseResourceLite(&Vcb->tree_lock);
1773 
1774  return Status;
1775 }
1776 
1778  NTSTATUS Status;
1779  BOOL make_inline, compress;
1780  UINT64 start_data, end_data;
1781  ULONG buf_head;
1782  UINT8* data;
1783 
1784  make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb);
1785 
1786  if (!make_inline)
1788 
1789  if (make_inline) {
1790  start_data = 0;
1791  end_data = fcb->inode_item.st_size;
1792  buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]);
1793  } else if (compress) {
1796  sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
1797  buf_head = 0;
1798  } else {
1799  start_data = start & ~(UINT64)(Vcb->superblock.sector_size - 1);
1800  end_data = sector_align(start + length, Vcb->superblock.sector_size);
1801  buf_head = 0;
1802  }
1803 
1804  data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG);
1805  if (!data) {
1806  ERR("out of memory\n");
1808  }
1809 
1810  RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data));
1811 
1812  if (start > start_data || start + length < end_data) {
1813  Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp);
1814 
1815  if (!NT_SUCCESS(Status)) {
1816  ERR("read_file returned %08x\n", Status);
1817  ExFreePool(data);
1818  return Status;
1819  }
1820  }
1821 
1822  RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length);
1823 
1824  if (make_inline) {
1825  UINT16 edsize;
1827 
1828  Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback);
1829  if (!NT_SUCCESS(Status)) {
1830  ERR("excise_extents returned %08x\n", Status);
1831  ExFreePool(data);
1832  return Status;
1833  }
1834 
1835  edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end_data);
1836 
1837  ed->generation = Vcb->superblock.generation;
1838  ed->decoded_size = end_data;
1843 
1845  if (!NT_SUCCESS(Status)) {
1846  ERR("add_extent_to_fcb returned %08x\n", Status);
1847  ExFreePool(data);
1848  return Status;
1849  }
1850 
1851  ExFreePool(data);
1852 
1853  fcb->inode_item.st_blocks += end_data;
1854  } else if (compress) {
1856 
1857  ExFreePool(data);
1858 
1859  if (!NT_SUCCESS(Status)) {
1860  ERR("write_compressed returned %08x\n", Status);
1861  return Status;
1862  }
1863  } else {
1864  Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
1865 
1866  ExFreePool(data);
1867 
1868  if (!NT_SUCCESS(Status)) {
1869  ERR("do_write_file returned %08x\n", Status);
1870  return Status;
1871  }
1872  }
1873 
1874  return STATUS_SUCCESS;
1875 }
1876 
1878  FILE_ZERO_DATA_INFORMATION* fzdi = data;
1879  NTSTATUS Status;
1880  fcb* fcb;
1881  ccb* ccb;
1882  file_ref* fileref;
1883  LIST_ENTRY rollback, *le;
1885  BTRFS_TIME now;
1886  UINT64 start, end;
1887  extent* ext;
1889 
1890  if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
1891  return STATUS_INVALID_PARAMETER;
1892 
1893  if (!FileObject) {
1894  ERR("FileObject was NULL\n");
1895  return STATUS_INVALID_PARAMETER;
1896  }
1897 
1898  if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
1899  WARN("BeyondFinalZero was less than or equal to FileOffset (%llx <= %llx)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
1900  return STATUS_INVALID_PARAMETER;
1901  }
1902 
1903  fcb = FileObject->FsContext;
1904 
1905  if (!fcb) {
1906  ERR("FCB was NULL\n");
1907  return STATUS_INVALID_PARAMETER;
1908  }
1909 
1910  ccb = FileObject->FsContext2;
1911 
1912  if (!ccb) {
1913  ERR("ccb was NULL\n");
1914  return STATUS_INVALID_PARAMETER;
1915  }
1916 
1917  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
1918  WARN("insufficient privileges\n");
1919  return STATUS_ACCESS_DENIED;
1920  }
1921 
1922  fileref = ccb->fileref;
1923 
1924  if (!fileref) {
1925  ERR("fileref was NULL\n");
1926  return STATUS_INVALID_PARAMETER;
1927  }
1928 
1930 
1931  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1933 
1934  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
1935 
1936  if (fcb->type != BTRFS_TYPE_FILE) {
1937  WARN("FileObject did not point to a file\n");
1939  goto end;
1940  }
1941 
1942  if (fcb->ads) {
1943  ERR("FileObject is stream\n");
1945  goto end;
1946  }
1947 
1948  if ((UINT64)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) {
1950  goto end;
1951  }
1952 
1953  ext = NULL;
1954  le = fcb->extents.Flink;
1955  while (le != &fcb->extents) {
1957 
1958  if (!ext2->ignore) {
1959  ext = ext2;
1960  break;
1961  }
1962 
1963  le = le->Flink;
1964  }
1965 
1966  if (!ext) {
1968  goto end;
1969  }
1970 
1971  if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
1972  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1973  if (!NT_SUCCESS(Status)) {
1974  ERR("zero_data returned %08x\n", Status);
1975  goto end;
1976  }
1977  } else {
1978  start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size);
1979 
1980  if ((UINT64)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size)
1981  end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size);
1982  else
1983  end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
1984 
1985  if (end <= start) {
1986  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
1987  if (!NT_SUCCESS(Status)) {
1988  ERR("zero_data returned %08x\n", Status);
1989  goto end;
1990  }
1991  } else {
1992  if (start > (UINT64)fzdi->FileOffset.QuadPart) {
1993  Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback);
1994  if (!NT_SUCCESS(Status)) {
1995  ERR("zero_data returned %08x\n", Status);
1996  goto end;
1997  }
1998  }
1999 
2000  if (end < (UINT64)fzdi->BeyondFinalZero.QuadPart) {
2001  Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback);
2002  if (!NT_SUCCESS(Status)) {
2003  ERR("zero_data returned %08x\n", Status);
2004  goto end;
2005  }
2006  }
2007 
2008  if (end > start) {
2010  if (!NT_SUCCESS(Status)) {
2011  ERR("excise_extents returned %08x\n", Status);
2012  goto end;
2013  }
2014  }
2015  }
2016  }
2017 
2018  CcPurgeCacheSection(&fcb->nonpaged->segment_object, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), FALSE);
2019 
2022 
2023  fcb->inode_item.transid = Vcb->superblock.generation;
2024  fcb->inode_item.sequence++;
2025 
2026  if (!ccb->user_set_change_time)
2028 
2029  if (!ccb->user_set_write_time)
2031 
2035 
2037 
2038  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2039  fcb->subvol->root_item.ctime = now;
2040 
2042 
2043 end:
2044  if (!NT_SUCCESS(Status))
2046  else
2048 
2049  ExReleaseResourceLite(fcb->Header.Resource);
2050  ExReleaseResourceLite(&Vcb->tree_lock);
2051 
2052  return Status;
2053 }
2054 
2055 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
2056  NTSTATUS Status;
2057  fcb* fcb;
2058  LIST_ENTRY* le;
2059  FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
2060  ULONG i = 0;
2061  UINT64 last_start, last_end;
2062 
2063  TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
2064 
2065  if (!FileObject) {
2066  ERR("FileObject was NULL\n");
2067  return STATUS_INVALID_PARAMETER;
2068  }
2069 
2070  if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf)
2071  return STATUS_INVALID_PARAMETER;
2072 
2073  fcb = FileObject->FsContext;
2074 
2075  if (!fcb) {
2076  ERR("FCB was NULL\n");
2077  return STATUS_INVALID_PARAMETER;
2078  }
2079 
2081 
2082  // If file is not marked as sparse, claim the whole thing as an allocated range
2083 
2084  if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) {
2085  if (fcb->inode_item.st_size == 0)
2087  else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER))
2089  else {
2090  ranges[i].FileOffset.QuadPart = 0;
2091  ranges[i].Length.QuadPart = fcb->inode_item.st_size;
2092  i++;
2094  }
2095 
2096  goto end;
2097 
2098  }
2099 
2100  le = fcb->extents.Flink;
2101 
2102  last_start = 0;
2103  last_end = 0;
2104 
2105  while (le != &fcb->extents) {
2107 
2108  if (!ext->ignore) {
2109  EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
2110  UINT64 len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
2111 
2112  if (ext->offset > last_end) { // first extent after a hole
2113  if (last_end > last_start) {
2114  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2115  ranges[i].FileOffset.QuadPart = last_start;
2116  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2117  i++;
2118  } else {
2120  goto end;
2121  }
2122  }
2123 
2124  last_start = ext->offset;
2125  }
2126 
2127  last_end = ext->offset + len;
2128  }
2129 
2130  le = le->Flink;
2131  }
2132 
2133  if (last_end > last_start) {
2134  if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
2135  ranges[i].FileOffset.QuadPart = last_start;
2136  ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
2137  i++;
2138  } else {
2140  goto end;
2141  }
2142  }
2143 
2145 
2146 end:
2147  *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER);
2148 
2149  ExReleaseResourceLite(fcb->Header.Resource);
2150 
2151  return Status;
2152 }
2153 
2154 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) {
2155  fcb* fcb;
2156 
2157  TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen);
2158 
2159  if (!FileObject) {
2160  ERR("FileObject was NULL\n");
2161  return STATUS_INVALID_PARAMETER;
2162  }
2163 
2164  if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER))
2165  return STATUS_INVALID_PARAMETER;
2166 
2167  fcb = FileObject->FsContext;
2168 
2169  if (!fcb) {
2170  ERR("FCB was NULL\n");
2171  return STATUS_INVALID_PARAMETER;
2172  }
2173 
2175 
2176  RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(UINT64));
2177  RtlCopyMemory(&buf->ObjectId[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
2178 
2179  ExReleaseResourceLite(fcb->Header.Resource);
2180 
2181  RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo));
2182 
2183  *retlen = sizeof(FILE_OBJECTID_BUFFER);
2184 
2185  return STATUS_SUCCESS;
2186 }
2187 
2189  LIST_ENTRY* le;
2190 
2191  le = Vcb->all_fcbs.Flink;
2192  while (le != &Vcb->all_fcbs) {
2193  struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
2195 
2196  if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted)
2197  CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb);
2198 
2199  le = le->Flink;
2200  }
2201 }
2202 
2205  NTSTATUS Status;
2206  KIRQL irql;
2207  BOOL lock_paused_balance = FALSE;
2208 
2209  TRACE("FSCTL_LOCK_VOLUME\n");
2210 
2211  if (Vcb->scrub.thread) {
2212  WARN("cannot lock while scrub running\n");
2213  return STATUS_DEVICE_NOT_READY;
2214  }
2215 
2216  if (Vcb->balance.thread) {
2217  WARN("cannot lock while balance running\n");
2218  return STATUS_DEVICE_NOT_READY;
2219  }
2220 
2221  TRACE("locking volume\n");
2222 
2224 
2225  if (Vcb->locked)
2226  return STATUS_SUCCESS;
2227 
2228  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
2229 
2230  if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) {
2232  ExReleaseResourceLite(&Vcb->fileref_lock);
2233  goto end;
2234  }
2235 
2236  ExReleaseResourceLite(&Vcb->fileref_lock);
2237 
2238  if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
2239  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2240  KeClearEvent(&Vcb->balance.event);
2241  ExReleaseResourceLite(&Vcb->tree_lock);
2242 
2243  lock_paused_balance = TRUE;
2244  }
2245 
2246  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2247 
2249 
2250  if (Vcb->need_write && !Vcb->readonly)
2251  Status = do_write(Vcb, Irp);
2252  else
2254 
2255  free_trees(Vcb);
2256 
2257  ExReleaseResourceLite(&Vcb->tree_lock);
2258 
2259  if (!NT_SUCCESS(Status)) {
2260  ERR("do_write returned %08x\n", Status);
2261  goto end;
2262  }
2263 
2265 
2266  if (!(Vcb->Vpb->Flags & VPB_LOCKED)) {
2267  Vcb->Vpb->Flags |= VPB_LOCKED;
2268  Vcb->locked = TRUE;
2269  Vcb->locked_fileobj = IrpSp->FileObject;
2270  Vcb->lock_paused_balance = lock_paused_balance;
2271  } else {
2274 
2275  if (lock_paused_balance)
2276  KeSetEvent(&Vcb->balance.event, 0, FALSE);
2277 
2278  goto end;
2279  }
2280 
2282 
2284 
2285 end:
2286  if (!NT_SUCCESS(Status))
2288 
2289  return Status;
2290 }
2291 
2293  KIRQL irql;
2294 
2296 
2297  Vcb->locked = FALSE;
2298  Vcb->Vpb->Flags &= ~VPB_LOCKED;
2299  Vcb->locked_fileobj = NULL;
2300 
2302 
2303  if (Vcb->lock_paused_balance)
2304  KeSetEvent(&Vcb->balance.event, 0, FALSE);
2305 }
2306 
2309 
2310  TRACE("FSCTL_UNLOCK_VOLUME\n");
2311 
2312  if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj)
2313  return STATUS_NOT_LOCKED;
2314 
2315  TRACE("unlocking volume\n");
2316 
2318 
2320 
2321  return STATUS_SUCCESS;
2322 }
2323 
2326  LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2327  NTSTATUS Status;
2328  HANDLE h;
2329  PFILE_OBJECT fileobj;
2330  PDEVICE_OBJECT devobj;
2331  LIST_ENTRY* le;
2332 
2333  TRACE("FSCTL_INVALIDATE_VOLUMES\n");
2334 
2335  if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode))
2337 
2338 #if defined(_WIN64)
2339  if (IoIs32bitProcess(Irp)) {
2340  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2341  return STATUS_INVALID_PARAMETER;
2342 
2343  h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2344  } else {
2345 #endif
2346  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2347  return STATUS_INVALID_PARAMETER;
2348 
2349  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2350 #if defined(_WIN64)
2351  }
2352 #endif
2353 
2354  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2355 
2356  if (!NT_SUCCESS(Status)) {
2357  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2358  return Status;
2359  }
2360 
2361  devobj = fileobj->DeviceObject;
2362 
2364 
2365  le = VcbList.Flink;
2366 
2367  while (le != &VcbList) {
2369 
2370  if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
2371  if (Vcb->Vpb == devobj->Vpb) {
2372  KIRQL irql;
2373  PVPB newvpb;
2374  BOOL free_newvpb = FALSE;
2375 
2376  newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
2377  if (!newvpb) {
2378  ERR("out of memory\n");
2380  goto end;
2381  }
2382 
2383  RtlZeroMemory(newvpb, sizeof(VPB));
2384 
2385  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2386 
2387  Vcb->removing = TRUE;
2388 
2389  ExReleaseResourceLite(&Vcb->tree_lock);
2390 
2392 
2393  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2394 
2396 
2397  if (Vcb->need_write && !Vcb->readonly)
2398  Status = do_write(Vcb, Irp);
2399  else
2401 
2402  free_trees(Vcb);
2403 
2404  if (!NT_SUCCESS(Status)) {
2405  ERR("do_write returned %08x\n", Status);
2406  ExReleaseResourceLite(&Vcb->tree_lock);
2407  ExFreePool(newvpb);
2408  goto end;
2409  }
2410 
2412 
2413  ExReleaseResourceLite(&Vcb->tree_lock);
2414 
2416 
2417  if (devobj->Vpb->Flags & VPB_MOUNTED) {
2418  newvpb->Type = IO_TYPE_VPB;
2419  newvpb->Size = sizeof(VPB);
2420  newvpb->RealDevice = devobj;
2421  newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING;
2422 
2423  devobj->Vpb = newvpb;
2424  } else
2425  free_newvpb = TRUE;
2426 
2428 
2429  if (free_newvpb)
2430  ExFreePool(newvpb);
2431 
2432  if (Vcb->open_files == 0)
2433  uninit(Vcb);
2434  }
2435 
2436  break;
2437  }
2438 
2439  le = le->Flink;
2440  }
2441 
2443 
2444 end:
2446 
2447  ObDereferenceObject(fileobj);
2448 
2449  return Status;
2450 }
2451 
2454  ULONG* volstate;
2455 
2456  if (Irp->AssociatedIrp.SystemBuffer) {
2457  volstate = Irp->AssociatedIrp.SystemBuffer;
2458  } else if (Irp->MdlAddress != NULL) {
2459  volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
2460 
2461  if (!volstate)
2463  } else
2465 
2466  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
2467  return STATUS_INVALID_PARAMETER;
2468 
2469  *volstate = 0;
2470 
2471  if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
2472  return STATUS_INVALID_PARAMETER;
2473 
2474  Irp->IoStatus.Information = sizeof(ULONG);
2475 
2476  return STATUS_SUCCESS;
2477 }
2478 
2482 
2483  TRACE("FSCTL_GET_COMPRESSION\n");
2484 
2485  if (Irp->AssociatedIrp.SystemBuffer) {
2486  compression = Irp->AssociatedIrp.SystemBuffer;
2487  } else if (Irp->MdlAddress != NULL) {
2489 
2490  if (!compression)
2492  } else
2494 
2495  if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT))
2496  return STATUS_INVALID_PARAMETER;
2497 
2499 
2500  Irp->IoStatus.Information = sizeof(USHORT);
2501 
2502  return STATUS_SUCCESS;
2503 }
2504 
2508 
2509  TRACE("FSCTL_SET_COMPRESSION\n");
2510 
2511  if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT))
2512  return STATUS_INVALID_PARAMETER;
2513 
2514  compression = Irp->AssociatedIrp.SystemBuffer;
2515 
2517  return STATUS_INVALID_PARAMETER;
2518 
2519  return STATUS_SUCCESS;
2520 }
2521 
2523  LIST_ENTRY* le;
2524  volume_device_extension* vde = Vcb->vde;
2525  pdo_device_extension* pdode = vde->pdode;
2526 
2527  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2528 
2530 
2531  le = pdode->children.Flink;
2532  while (le != &pdode->children) {
2534 
2535  vc->generation = Vcb->superblock.generation - 1;
2536 
2537  le = le->Flink;
2538  }
2539 
2541 
2542  ExReleaseResourceLite(&Vcb->tree_lock);
2543 }
2544 
2546  NTSTATUS Status;
2547 
2548  TRACE("FSCTL_DISMOUNT_VOLUME\n");
2549 
2550  if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
2551  return STATUS_SUCCESS;
2552 
2553  if (Vcb->disallow_dismount) {
2554  WARN("attempting to dismount boot volume or one containing a pagefile\n");
2555  return STATUS_ACCESS_DENIED;
2556  }
2557 
2559  if (!NT_SUCCESS(Status)) {
2560  WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
2561  }
2562 
2563  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2564 
2565  if (!Vcb->locked) {
2567 
2568  if (Vcb->need_write && !Vcb->readonly) {
2569  Status = do_write(Vcb, Irp);
2570 
2571  if (!NT_SUCCESS(Status))
2572  ERR("do_write returned %08x\n", Status);
2573  }
2574  }
2575 
2576  free_trees(Vcb);
2577 
2578  Vcb->removing = TRUE;
2579 
2580  if (Vcb->vde) {
2582  Vcb->vde->mounted_device = NULL;
2583  }
2584 
2585  ExReleaseResourceLite(&Vcb->tree_lock);
2586 
2587  return STATUS_SUCCESS;
2588 }
2589 
2591  NTSTATUS Status;
2592  ULONG to_read;
2593  superblock* sb;
2594  UINT32 crc32;
2595  BTRFS_UUID fsuuid, devuuid;
2596  LIST_ENTRY* le;
2597 
2598  to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize);
2599 
2601  if (!sb) {
2602  ERR("out of memory\n");
2604  }
2605 
2607  if (!NT_SUCCESS(Status)) {
2608  ERR("sync_read_phys returned %08x\n", Status);
2609  ExFreePool(sb);
2610  return Status;
2611  }
2612 
2613  if (sb->magic != BTRFS_MAGIC) {
2614  TRACE("device is not Btrfs\n");
2615  ExFreePool(sb);
2616  return STATUS_SUCCESS;
2617  }
2618 
2619  crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2620 
2621  if (crc32 != *((UINT32*)sb->checksum)) {
2622  TRACE("device has Btrfs magic, but invalid superblock checksum\n");
2623  ExFreePool(sb);
2624  return STATUS_SUCCESS;
2625  }
2626 
2627  fsuuid = sb->uuid;
2628  devuuid = sb->dev_item.device_uuid;
2629 
2630  ExFreePool(sb);
2631 
2633 
2634  le = VcbList.Flink;
2635 
2636  while (le != &VcbList) {
2638 
2639  if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2640  LIST_ENTRY* le2;
2641 
2642  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2643 
2644  if (Vcb->superblock.num_devices > 1) {
2645  le2 = Vcb->devices.Flink;
2646  while (le2 != &Vcb->devices) {
2648 
2649  if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2650  ExReleaseResourceLite(&Vcb->tree_lock);
2652  return STATUS_DEVICE_NOT_READY;
2653  }
2654 
2655  le2 = le2->Flink;
2656  }
2657  }
2658 
2659  ExReleaseResourceLite(&Vcb->tree_lock);
2661  return STATUS_SUCCESS;
2662  }
2663 
2664  le = le->Flink;
2665  }
2666 
2668 
2669  return STATUS_SUCCESS;
2670 }
2671 
2674  NTSTATUS Status;
2675 
2676  // FIXME - avoid "bootloader area"??
2677 
2678  dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
2679  dmdsa.Action = DeviceDsmAction_Trim;
2681  dmdsa.ParameterBlockOffset = 0;
2682  dmdsa.ParameterBlockLength = 0;
2683  dmdsa.DataSetRangesOffset = 0;
2684  dmdsa.DataSetRangesLength = 0;
2685 
2687  if (!NT_SUCCESS(Status))
2688  WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
2689 }
2690 
2693  NTSTATUS Status;
2694  PFILE_OBJECT fileobj, mountmgrfo;
2696  HANDLE h;
2697  LIST_ENTRY* le;
2698  device* dev;
2699  DEV_ITEM* di;
2700  UINT64 dev_id, size;
2701  UINT8* mb;
2702  UINT64* stats;
2703  UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
2704  volume_child* vc;
2705  PDEVICE_OBJECT mountmgr;
2706  KEY searchkey;
2707  traverse_ptr tp;
2710  pdo_device_extension* pdode;
2711  const GUID* pnp_guid;
2713 
2714  pnp_name.Buffer = NULL;
2715 
2716  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
2718 
2719  if (!Vcb->vde) {
2720  WARN("not allowing second device to be added to non-PNP device\n");
2721  return STATUS_NOT_SUPPORTED;
2722  }
2723 
2724  if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device
2726 
2727 #if defined(_WIN64)
2728  if (IoIs32bitProcess(Irp)) {
2729  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32))
2730  return STATUS_INVALID_PARAMETER;
2731 
2732  h = (HANDLE)LongToHandle((*(PUINT32)Irp->AssociatedIrp.SystemBuffer));
2733  } else {
2734 #endif
2735  if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE))
2736  return STATUS_INVALID_PARAMETER;
2737 
2738  h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
2739 #if defined(_WIN64)
2740  }
2741 #endif
2742 
2743  Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
2744 
2745  if (!NT_SUCCESS(Status)) {
2746  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
2747  return Status;
2748  }
2749 
2750  DeviceObject = fileobj->DeviceObject;
2751 
2753  if (!NT_SUCCESS(Status)) {
2754  ERR("get_device_pnp_name returned %08x\n", Status);
2755  ObDereferenceObject(fileobj);
2756  return Status;
2757  }
2758 
2759  // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
2760  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
2761  DeviceObject = DeviceObject->AttachedDevice;
2762 
2764  if (!NT_SUCCESS(Status)) {
2765  ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
2766  ObDereferenceObject(fileobj);
2767  return Status;
2768  }
2769 
2771  if (!NT_SUCCESS(Status)) {
2772  ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
2773  ObDereferenceObject(fileobj);
2774  return Status;
2775  }
2776 
2777  // if disk, check it has no partitions
2778  if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
2779  ULONG dlisize;
2781 
2782  dlisize = 0;
2783 
2784  do {
2785  dlisize += 1024;
2786 
2787  if (dli)
2788  ExFreePool(dli);
2789 
2790  dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
2791  if (!dli) {
2792  ERR("out of memory\n");
2794  goto end2;
2795  }
2796 
2798  } while (Status == STATUS_BUFFER_TOO_SMALL);
2799 
2800  if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
2801  ExFreePool(dli);
2802  ERR("not adding disk which has partitions\n");
2804  goto end2;
2805  }
2806 
2807  ExFreePool(dli);
2808  }
2809 
2811  &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
2812  if (NT_SUCCESS(Status)) {
2813  if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
2814  WARN("device was not disk\n");
2815  ObDereferenceObject(fileobj);
2816  return STATUS_INVALID_PARAMETER;
2817  }
2818  } else {
2819  sdn.DeviceNumber = 0xffffffff;
2820  sdn.PartitionNumber = 0xffffffff;
2821  }
2822 
2824  &gli, sizeof(gli), TRUE, NULL);
2825  if (!NT_SUCCESS(Status)) {
2826  ERR("error reading length information: %08x\n", Status);
2827  ObDereferenceObject(fileobj);
2828  return Status;
2829  }
2830 
2831  size = gli.Length.QuadPart;
2832 
2833  if (size < 0x100000) {
2834  ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size);
2835  ObDereferenceObject(fileobj);
2836  return STATUS_INTERNAL_ERROR;
2837  }
2838 
2840 
2841  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2842 
2843  if (Vcb->need_write)
2844  Status = do_write(Vcb, Irp);
2845  else
2847 
2848  free_trees(Vcb);
2849 
2850  if (!NT_SUCCESS(Status)) {
2851  ERR("do_write returned %08x\n", Status);
2852  goto end;
2853  }
2854 
2856  if (!dev) {
2857  ERR("out of memory\n");
2859  goto end;
2860  }
2861 
2862  RtlZeroMemory(dev, sizeof(device));
2863 
2864  dev->devobj = DeviceObject;
2865  dev->seeding = FALSE;
2866  init_device(Vcb, dev, TRUE);
2867 
2868  InitializeListHead(&dev->space);
2869 
2870  if (size > 0x100000) { // add disk hole - the first MB is marked as used
2871  Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
2872  if (!NT_SUCCESS(Status)) {
2873  ERR("add_space_entry returned %08x\n", Status);
2874  goto end;
2875  }
2876  }
2877 
2878  dev_id = 0;
2879 
2880  le = Vcb->devices.Flink;
2881  while (le != &Vcb->devices) {
2882  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
2883 
2884  if (dev2->devitem.dev_id > dev_id)
2885  dev_id = dev2->devitem.dev_id;
2886 
2887  le = le->Flink;
2888  }
2889 
2890  dev_id++;
2891 
2892  dev->devitem.dev_id = dev_id;
2893  dev->devitem.num_bytes = size;
2894  dev->devitem.bytes_used = 0;
2895  dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
2896  dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
2897  dev->devitem.minimal_io_size = Vcb->superblock.sector_size;
2898  dev->devitem.type = 0;
2899  dev->devitem.generation = 0;
2900  dev->devitem.start_offset = 0;
2901  dev->devitem.dev_group = 0;
2902  dev->devitem.seek_speed = 0;
2903  dev->devitem.bandwidth = 0;
2904  get_uuid(&dev->devitem.device_uuid);
2905  dev->devitem.fs_uuid = Vcb->superblock.uuid;
2906 
2908  if (!di) {
2909  ERR("out of memory\n");
2910  goto end;
2911  }
2912 
2913  RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
2914 
2915  Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
2916  if (!NT_SUCCESS(Status)) {
2917  ERR("insert_tree_item returned %08x\n", Status);
2918  ExFreePool(di);
2919  goto end;
2920  }
2921 
2922  // add stats entry to dev tree
2923  stats = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * 5, ALLOC_TAG);
2924  if (!stats) {
2925  ERR("out of memory\n");
2927  goto end;
2928  }
2929 
2930  RtlZeroMemory(stats, sizeof(UINT64) * 5);
2931 
2932  searchkey.obj_id = 0;
2933  searchkey.obj_type = TYPE_DEV_STATS;
2934  searchkey.offset = di->dev_id;
2935 
2936  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2937  if (!NT_SUCCESS(Status)) {
2938  ERR("error - find_item returned %08x\n", Status);
2939  ExFreePool(stats);
2940  goto end;
2941  }
2942 
2943  if (!keycmp(tp.item->key, searchkey)) {
2945  if (!NT_SUCCESS(Status)) {
2946  ERR("delete_tree_item returned %08x\n", Status);
2947  ExFreePool(stats);
2948  goto end;
2949  }
2950  }
2951 
2952  Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(UINT64) * 5, NULL, Irp);
2953  if (!NT_SUCCESS(Status)) {
2954  ERR("insert_tree_item returned %08x\n", Status);
2955  ExFreePool(stats);
2956  goto end;
2957  }
2958 
2959  if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
2961 
2962  // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
2963  mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
2964  if (!mb) {
2965  ERR("out of memory\n");
2967  goto end;
2968  }
2969 
2970  RtlZeroMemory(mb, 0x100000);
2971 
2972  Status = write_data_phys(DeviceObject, 0, mb, 0x100000);
2973  if (!NT_SUCCESS(Status)) {
2974  ERR("write_data_phys returned %08x\n", Status);
2975  ExFreePool(mb);
2976  goto end;
2977  }
2978 
2979  ExFreePool(mb);
2980 
2981  vde = Vcb->vde;
2982  pdode = vde->pdode;
2983 
2985  if (!vc) {
2986  ERR("out of memory\n");
2988  goto end;
2989  }
2990 
2991  vc->uuid = dev->devitem.device_uuid;
2992  vc->devid = dev_id;
2993  vc->generation = Vcb->superblock.generation;
2994  vc->devobj = DeviceObject;
2995  vc->fileobj = fileobj;
2996  vc->notification_entry = NULL;
2997 
2999  drvobj, pnp_removal, vde->pdode, &vc->notification_entry);
3000  if (!NT_SUCCESS(Status))
3001  WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
3002 
3003  pnp_name2 = pnp_name;
3004 
3005  if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
3006  pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
3007  pnp_name2.Buffer = &pnp_name2.Buffer[3];
3008  pnp_name2.Length -= 3 * sizeof(WCHAR);
3009  pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
3010  }
3011 
3012  vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
3013 
3014  if (pnp_name2.Length == 0)
3015  vc->pnp_name.Buffer = NULL;
3016  else {
3018  if (!vc->pnp_name.Buffer) {
3019  ERR("out of memory\n");
3021  goto end;
3022  }
3023 
3024  RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
3025  }
3026 
3027  vc->size = size;
3028  vc->seeding = FALSE;
3029  vc->disk_num = sdn.DeviceNumber;
3030  vc->part_num = sdn.PartitionNumber;
3031  vc->had_drive_letter = FALSE;
3032 
3034  InsertTailList(&pdode->children, &vc->list_entry);
3035  pdode->num_children++;
3036  pdode->children_loaded++;
3038 
3040  Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
3041  if (!NT_SUCCESS(Status))
3042  ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
3043  else {
3044  Status = remove_drive_letter(mountmgr, &pnp_name);
3046  WARN("remove_drive_letter returned %08x\n", Status);
3047 
3049 
3050  ObDereferenceObject(mountmgrfo);
3051  }
3052 
3053  Vcb->superblock.num_devices++;
3054  Vcb->superblock.total_bytes += size;
3055  Vcb->devices_loaded++;
3056  InsertTailList(&Vcb->devices, &dev->list_entry);
3057 
3058  // FIXME - send notification that volume size has increased
3059 
3060  ObReferenceObject(DeviceObject); // for Vcb
3061 
3062  Status = do_write(Vcb, Irp);
3063  if (!NT_SUCCESS(Status))
3064  ERR("do_write returned %08x\n", Status);
3065 
3066  ObReferenceObject(fileobj);
3067 
3068 end:
3069  free_trees(Vcb);
3070 
3071  ExReleaseResourceLite(&Vcb->tree_lock);
3072 
3073 end2:
3074  ObDereferenceObject(fileobj);
3075 
3076  if (pnp_name.Buffer)
3078 
3079  if (NT_SUCCESS(Status))
3081 
3082  return Status;
3083 }
3084 
3086  fcb* fcb;
3087  ccb* ccb;
3088 
3089  TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n");
3090 
3091  if (!FileObject)
3092  return STATUS_INVALID_PARAMETER;
3093 
3094  fcb = FileObject->FsContext;
3095  ccb = FileObject->FsContext2;
3096 
3097  if (!fcb)
3098  return STATUS_INVALID_PARAMETER;
3099 
3100  if (fcb != Vcb->volume_fcb)
3101  return STATUS_INVALID_PARAMETER;
3102 
3103  if (!ccb)
3104  return STATUS_INVALID_PARAMETER;
3105 
3107 
3108  return STATUS_SUCCESS;
3109 }
3110 
3112  if (length < sizeof(BTRFS_UUID))
3113  return STATUS_BUFFER_OVERFLOW;
3114 
3115  RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
3116 
3117  return STATUS_SUCCESS;
3118 }
3119 
3121  UINT64 devid;
3122  NTSTATUS Status;
3123  LIST_ENTRY* le;
3124 
3125  if (length < sizeof(UINT64))
3126  return STATUS_INVALID_PARAMETER;
3127 
3128  if (Vcb->readonly)
3130 
3131  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3133 
3134  devid = *((UINT64*)data);
3135 
3136  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3137 
3138  le = Vcb->devices.Flink;
3139 
3140  while (le != &Vcb->devices) {
3142 
3143  if (dev->devitem.dev_id == devid) {
3144  RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
3145  dev->stats_changed = TRUE;
3146  Vcb->stats_changed = TRUE;
3147  Vcb->need_write = TRUE;
3149  goto end;
3150  }
3151 
3152  le = le->Flink;
3153  }
3154 
3155  WARN("device %llx not found\n", devid);
3157 
3158 end:
3159  ExReleaseResourceLite(&Vcb->tree_lock);
3160 
3161  return Status;
3162 }
3163 
3166 
3167  TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n");
3168 
3169  // STUB
3170 
3171  if (!FileObject)
3172  return STATUS_INVALID_PARAMETER;
3173 
3175  return STATUS_INVALID_PARAMETER;
3176 
3177  fgiib->ChecksumAlgorithm = 0;
3178  fgiib->Reserved = 0;
3179  fgiib->Flags = 0;
3180  fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size;
3181  fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size;
3182 
3183  return STATUS_SUCCESS;
3184 }
3185 
3187  TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n");
3188 
3189  // STUB
3190 
3191  if (!FileObject)
3192  return STATUS_INVALID_PARAMETER;
3193 
3195  return STATUS_INVALID_PARAMETER;
3196 
3197  return STATUS_SUCCESS;
3198 }
3199 
3201  LIST_ENTRY* le;
3202 
3203  le = fcb->extents.Flink;
3204  while (le != &fcb->extents) {
3206 
3207  if (!ext->ignore)
3208  return ext->extent_data.type == EXTENT_TYPE_INLINE;
3209 
3210  le = le->Flink;
3211  }
3212 
3213  return FALSE;
3214 }
3215 
3218  fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb;
3219  ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb;
3220  NTSTATUS Status;
3221  PFILE_OBJECT sourcefo;
3222  UINT64 sourcelen, nbytes = 0;
3223  LIST_ENTRY rollback, *le, newexts;
3225  BTRFS_TIME now;
3226  BOOL make_inline;
3227 
3228  if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA))
3229  return STATUS_BUFFER_TOO_SMALL;
3230 
3231  if (Vcb->readonly)
3233 
3234  if (ded->ByteCount.QuadPart == 0)
3235  return STATUS_SUCCESS;
3236 
3237  if (!fcb || !ccb || fcb == Vcb->volume_fcb)
3238  return STATUS_INVALID_PARAMETER;
3239 
3241  return STATUS_ACCESS_DENIED;
3242 
3243  if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3244  WARN("insufficient privileges\n");
3245  return STATUS_ACCESS_DENIED;
3246  }
3247 
3248  if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)
3249  return STATUS_INVALID_PARAMETER;
3250 
3251  Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL);
3252  if (!NT_SUCCESS(Status)) {
3253  ERR("ObReferenceObjectByHandle returned %08x\n", Status);
3254  return Status;
3255  }
3256 
3257  if (sourcefo->DeviceObject != FileObject->DeviceObject) {
3258  WARN("source and destination are on different volumes\n");
3259  ObDereferenceObject(sourcefo);
3260  return STATUS_INVALID_PARAMETER;
3261  }
3262 
3263  sourcefcb = sourcefo->FsContext;
3264  sourceccb = sourcefo->FsContext2;
3265 
3266  if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) {
3267  ObDereferenceObject(sourcefo);
3268  return STATUS_INVALID_PARAMETER;
3269  }
3270 
3271  if (!sourcefcb->ads && !fcb->ads) {
3272  if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) {
3273  ObDereferenceObject(sourcefo);
3274  return STATUS_INVALID_PARAMETER;
3275  }
3276 
3277  if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) {
3278  ObDereferenceObject(sourcefo);
3279  return STATUS_INVALID_PARAMETER;
3280  }
3281  }
3282 
3283  if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) {
3284  WARN("insufficient privileges\n");
3285  ObDereferenceObject(sourcefo);
3286  return STATUS_ACCESS_DENIED;
3287  }
3288 
3289  if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) {
3290  ObDereferenceObject(sourcefo);
3291  return STATUS_INVALID_PARAMETER;
3292  }
3293 
3294  sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size;
3295 
3296  if (sector_align(sourcelen, Vcb->superblock.sector_size) < (UINT64)ded->SourceFileOffset.QuadPart + (UINT64)ded->ByteCount.QuadPart) {
3297  ObDereferenceObject(sourcefo);
3298  return STATUS_NOT_SUPPORTED;
3299  }
3300 
3301  if (fcb == sourcefcb &&
3304  WARN("source and destination are the same, and the ranges overlap\n");
3305  ObDereferenceObject(sourcefo);
3306  return STATUS_INVALID_PARAMETER;
3307  }
3308 
3309  // fail if nocsum flag set on one file but not the other
3310  if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3311  ObDereferenceObject(sourcefo);
3312  return STATUS_INVALID_PARAMETER;
3313  }
3314 
3316  InitializeListHead(&newexts);
3317 
3318  ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3319 
3321 
3322  if (fcb != sourcefcb)
3323  ExAcquireResourceSharedLite(sourcefcb->Header.Resource, TRUE);
3324 
3327  goto end;
3328  }
3329 
3330  if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) {
3332  goto end;
3333  }
3334 
3335  make_inline = fcb->ads ? FALSE : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb));
3336 
3337  if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) {
3338  UINT8* data2;
3339  ULONG bytes_read, dataoff, datalen2;
3340 
3341  if (make_inline) {
3342  dataoff = (ULONG)ded->TargetFileOffset.QuadPart;
3343  datalen2 = (ULONG)fcb->inode_item.st_size;
3344  } else if (fcb->ads) {
3345  dataoff = 0;
3346  datalen2 = (ULONG)ded->ByteCount.QuadPart;
3347  } else {
3348  dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size;
3349  datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size);
3350  }
3351 
3353  if (!data2) {
3354  ERR("out of memory\n");
3356  goto end;
3357  }
3358 
3359  if (dataoff > 0) {
3360  if (make_inline)
3361  Status = read_file(fcb, data2, 0, datalen2, NULL, Irp);
3362  else
3363  Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp);
3364 
3365  if (!NT_SUCCESS(Status)) {
3366  ERR("read_file returned %08x\n", Status);
3367  ExFreePool(data2);
3368  goto end;
3369  }
3370  }
3371 
3372  if (sourcefcb->ads) {
3373  Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read);
3374  if (!NT_SUCCESS(Status)) {
3375  ERR("read_stream returned %08x\n", Status);
3376  ExFreePool(data2);
3377  goto end;
3378  }
3379  } else {
3380  Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp);
3381  if (!NT_SUCCESS(Status)) {
3382  ERR("read_file returned %08x\n", Status);
3383  ExFreePool(data2);
3384  goto end;
3385  }
3386  }
3387 
3388  if (dataoff + bytes_read < datalen2)
3389  RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read);
3390 
3391  if (fcb->ads)
3393  else if (make_inline) {
3394  UINT16 edsize;
3395  EXTENT_DATA* ed;
3396 
3397  Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
3398  if (!NT_SUCCESS(Status)) {
3399  ERR("excise_extents returned %08x\n", Status);
3400  ExFreePool(data2);
3401  goto end;
3402  }
3403 
3404  edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + datalen2);
3405 
3407  if (!ed) {
3408  ERR("out of memory\n");
3409  ExFreePool(data2);
3411  goto end;
3412  }
3413 
3414  ed->generation = Vcb->superblock.generation;
3420 
3421  RtlCopyMemory(ed->data, data2, datalen2);
3422 
3424  if (!NT_SUCCESS(Status)) {
3425  ERR("add_extent_to_fcb returned %08x\n", Status);
3426  ExFreePool(data2);
3427  goto end;
3428  }
3429 
3430  fcb->inode_item.st_blocks += datalen2;
3431  } else {
3432  UINT64 start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size);
3433 
3434  Status = do_write_file(fcb, start, start + datalen2, data2, Irp, FALSE, 0, &rollback);
3435  if (!NT_SUCCESS(Status)) {
3436  ERR("do_write_file returned %08x\n", Status);
3437  ExFreePool(data2);
3438  goto end;
3439  }
3440  }
3441 
3442  ExFreePool(data2);
3443  } else {
3444  LIST_ENTRY* lastextle;
3445 
3446  le = sourcefcb->extents.Flink;
3447  while (le != &sourcefcb->extents) {
3449 
3450  if (!ext->ignore) {
3451  if (ext->offset >= (UINT64)ded->SourceFileOffset.QuadPart + (UINT64)ded->ByteCount.QuadPart)
3452  break;
3453 
3454  if (ext->extent_data.type != EXTENT_TYPE_INLINE) {
3455  ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3456  extent* ext2;
3457  EXTENT_DATA2 *ed2s, *ed2d;
3458  chunk* c;
3459 
3460  ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3461 
3462  if (ext->offset + ed2s->num_bytes <= (UINT64)ded->SourceFileOffset.QuadPart) {
3463  le = le->Flink;
3464  continue;
3465  }
3466 
3468  if (!ext2) {
3469  ERR("out of memory\n");
3471  goto end;
3472  }
3473 
3474  if (ext->offset < (UINT64)ded->SourceFileOffset.QuadPart)
3475  ext2->offset = ded->TargetFileOffset.QuadPart;
3476  else
3477  ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart;
3478 
3479  ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
3480  ext2->unique = FALSE;
3481  ext2->ignore = FALSE;
3482  ext2->inserted = TRUE;
3483 
3484  ext2->extent_data.generation = Vcb->superblock.generation;
3485  ext2->extent_data.decoded_size = ext->extent_data.decoded_size;
3486  ext2->extent_data.compression = ext->extent_data.compression;
3487  ext2->extent_data.encryption = ext->extent_data.encryption;
3488  ext2->extent_data.encoding = ext->extent_data.encoding;
3489  ext2->extent_data.type = ext->extent_data.type;
3490 
3491  ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3492 
3493  ed2d->address = ed2s->address;
3494  ed2d->size = ed2s->size;
3495 
3496  if (ext->offset < (UINT64)ded->SourceFileOffset.QuadPart) {
3497  ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset;
3498  ed2d->num_bytes = min((UINT64)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart);
3499  } else {
3500  ed2d->offset = ed2s->offset;
3501  ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes);
3502  }
3503 
3504  if (ext->csum) {
3505  if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
3506  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
3507  if (!ext2->csum) {
3508  ERR("out of memory\n");
3510  ExFreePool(ext2);
3511  goto end;
3512  }
3513 
3514  RtlCopyMemory(ext2->csum, &ext->csum[(ed2d->offset - ed2s->offset) / Vcb->superblock.sector_size],
3515  (ULONG)(ed2d->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
3516  } else {
3517  ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
3518  if (!ext2->csum) {
3519  ERR("out of memory\n");
3521  ExFreePool(ext2);
3522  goto end;
3523  }
3524 
3525  RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * sizeof(UINT32) / Vcb->superblock.sector_size));
3526  }
3527  } else
3528  ext2->csum = NULL;
3529 
3530  InsertTailList(&newexts, &ext2->list_entry);
3531 
3532  c = get_chunk_from_address(Vcb, ed2s->address);
3533  if (!c) {
3534  ERR("get_chunk_from_address(%llx) failed\n", ed2s->address);
3536  goto end;
3537  }
3538 
3539  Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset,
3541  if (!NT_SUCCESS(Status)) {
3542  ERR("update_changed_extent_ref returned %08x\n", Status);
3543  goto end;
3544  }
3545 
3546  nbytes += ed2d->num_bytes;
3547  }
3548  }
3549 
3550  le = le->Flink;
3551  }
3552 
3554  if (!NT_SUCCESS(Status)) {
3555  ERR("excise_extents returned %08x\n", Status);
3556 
3557  while (!IsListEmpty(&newexts)) {
3559  ExFreePool(ext);
3560  }
3561 
3562  goto end;
3563  }
3564 
3565  // clear unique flags in source fcb
3566  le = sourcefcb->extents.Flink;
3567  while (le != &sourcefcb->extents) {
3569 
3570  if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
3571  EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data;
3572  LIST_ENTRY* le2;
3573 
3574  le2 = newexts.Flink;
3575  while (le2 != &newexts) {
3577 
3578  if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) {
3579  EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data;
3580 
3581  if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) {
3582  ext->unique = FALSE;
3583  break;
3584  }
3585  }
3586 
3587  le2 = le2->Flink;
3588  }
3589  }
3590 
3591  le = le->Flink;
3592  }
3593 
3594  lastextle = &fcb->extents;
3595  while (!IsListEmpty(&newexts)) {
3597 
3598  add_extent(fcb, lastextle, ext);
3599  lastextle = &ext->list_entry;
3600  }
3601  }
3602 
3605 
3606  if (fcb->ads) {
3607  ccb->fileref->parent->fcb->inode_item.sequence++;
3608 
3609  if (!ccb->user_set_change_time)
3610  ccb->fileref->parent->fcb->inode_item.st_ctime = now;
3611 
3612  ccb->fileref->parent->fcb->inode_item_changed = TRUE;
3613  mark_fcb_dirty(ccb->fileref->parent->fcb);
3614  } else {
3615  fcb->inode_item.st_blocks += nbytes;
3616  fcb->inode_item.sequence++;
3617 
3618  if (!ccb->user_set_change_time)
3620 
3621  if (!ccb->user_set_write_time) {
3624  }
3625 
3628  }
3629 
3631 
3632  if (fcb->nonpaged->segment_object.DataSectionObject)
3633  CcPurgeCacheSection(&fcb->nonpaged->segment_object, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, FALSE);
3634 
3636 
3637 end:
3638  ObDereferenceObject(sourcefo);
3639 
3640  if (NT_SUCCESS(Status))
3642  else
3644 
3645  if (fcb != sourcefcb)
3646  ExReleaseResourceLite(sourcefcb->Header.Resource);
3647 
3648  ExReleaseResourceLite(fcb->Header.Resource);
3649 
3650  ExReleaseResourceLite(&Vcb->tree_lock);
3651 
3652  return Status;
3653 }
3654 
3656  NTSTATUS Status;
3657  btrfs_mknod* bmn;
3658  fcb *parfcb, *fcb;
3659  ccb* parccb;
3660  file_ref *parfileref, *fileref;
3662  root* subvol;
3663  UINT64 inode;
3664  dir_child* dc;
3666  BTRFS_TIME now;
3667  LIST_ENTRY* lastle;
3668  ANSI_STRING utf8;
3669  ULONG len, i;
3670  SECURITY_SUBJECT_CONTEXT subjcont;
3671  PSID owner;
3672  BOOLEAN defaulted;
3673 
3674  TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen);
3675 
3676  if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3677  return STATUS_INVALID_PARAMETER;
3678 
3679  if (Vcb->readonly)
3681 
3682  parfcb = FileObject->FsContext;
3683 
3684  if (parfcb->type != BTRFS_TYPE_DIRECTORY) {
3685  WARN("trying to create file in something other than a directory\n");
3686  return STATUS_INVALID_PARAMETER;
3687  }
3688 
3689  if (is_subvol_readonly(parfcb->subvol, Irp))
3690  return STATUS_ACCESS_DENIED;
3691 
3692  parccb = FileObject->FsContext2;
3693  parfileref = parccb->fileref;
3694 
3695  if (!parfileref)
3696  return STATUS_INVALID_PARAMETER;
3697 
3698  if (datalen < sizeof(btrfs_mknod))
3699  return STATUS_INVALID_PARAMETER;
3700 
3701  bmn = (btrfs_mknod*)data;
3702 
3703  if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR))
3704  return STATUS_INVALID_PARAMETER;
3705 
3706  if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK)
3707  return STATUS_INVALID_PARAMETER;
3708 
3709  if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) ||
3710  (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) {
3711  WARN("insufficient privileges\n");
3712  return STATUS_ACCESS_DENIED;
3713  }
3714 
3715  if (bmn->inode != 0) {
3716  if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3718  }
3719 
3720  for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) {
3721  if (bmn->name[i] == 0 || bmn->name[i] == '/')
3723  }
3724 
3725  // don't allow files called . or ..
3726  if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.')))
3728 
3729  Status = RtlUnicodeToUTF8N(NULL, 0, &len, bmn->name, bmn->namelen);
3730  if (!NT_SUCCESS(Status)) {
3731  ERR("RtlUnicodeToUTF8N return %08x\n", Status);
3732  return Status;
3733  }
3734 
3735  if (len == 0) {
3736  ERR("RtlUnicodeToUTF8N returned a length of 0\n");
3737  return STATUS_INTERNAL_ERROR;
3738  }
3739 
3740  if (len > 0xffff) {
3741  ERR("len was too long (%x)\n", len);
3742  return STATUS_INVALID_PARAMETER;
3743  }
3744 
3745  utf8.MaximumLength = utf8.Length = (USHORT)len;
3747 
3748  if (!utf8.Buffer) {
3749  ERR("out of memory\n");
3751  }
3752 
3753  Status = RtlUnicodeToUTF8N(utf8.Buffer, len, &len, bmn->name, bmn->namelen);
3754  if (!NT_SUCCESS(Status)) {
3755  ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
3756  ExFreePool(utf8.Buffer);
3757  return Status;
3758  }
3759 
3760  name.Length = name.MaximumLength = bmn->namelen;
3761  name.Buffer = bmn->name;
3762 
3763  Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, TRUE);
3765  ERR("find_file_in_dir returned %08x\n", Status);
3766  goto end;
3767  }
3768 
3769  if (NT_SUCCESS(Status)) {
3770  WARN("filename already exists\n");
3772  goto end;
3773  }
3774 
3777 
3779  if (!fcb) {
3780  ERR("out of memory\n");
3782  goto end;
3783  }
3784 
3785  fcb->Vcb = Vcb;
3786 
3787  fcb->inode_item.generation = Vcb->superblock.generation;
3788  fcb->inode_item.transid = Vcb->superblock.generation;
3789  fcb->inode_item.st_size = 0;
3790  fcb->inode_item.st_blocks = 0;
3791  fcb->inode_item.block_group = 0;
3792  fcb->inode_item.st_nlink = 1;
3796 
3797  if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV)
3798  fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20);
3799  else
3800  fcb->inode_item.st_rdev = 0;
3801 
3802  fcb->inode_item.flags = 0;
3803  fcb->inode_item.sequence = 1;
3807  fcb->inode_item.otime = now;
3808 
3809  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3811  else if (bmn->type == BTRFS_TYPE_CHARDEV)
3813  else if (bmn->type == BTRFS_TYPE_BLOCKDEV)
3815  else if (bmn->type == BTRFS_TYPE_FIFO)
3817  else if (bmn->type == BTRFS_TYPE_SOCKET)
3819  else if (bmn->type == BTRFS_TYPE_SYMLINK)
3821  else
3823 
3824  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3825  fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
3826 
3827  // inherit nodatacow flag from parent directory
3828  if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
3830 
3831  if (bmn->type != BTRFS_TYPE_DIRECTORY)
3833  }
3834 
3835  if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
3837 
3840 
3842 
3843  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3844  fcb->Header.AllocationSize.QuadPart = 0;
3845  fcb->Header.FileSize.QuadPart = 0;
3846  fcb->Header.ValidDataLength.QuadPart = 0;
3847 
3848  fcb->atts = 0;
3849 
3850  if (bmn->name[0] == '.')
3852 
3853  if (bmn->type == BTRFS_TYPE_DIRECTORY)
3855 
3856  fcb->atts_changed = FALSE;
3857 
3858  InterlockedIncrement(&parfcb->refcount);
3859  fcb->subvol = parfcb->subvol;
3860 
3861  SeCaptureSubjectContext(&subjcont);
3862 
3863  Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
3865 
3866  if (!NT_SUCCESS(Status)) {
3867  ERR("SeAssignSecurityEx returned %08x\n", Status);
3868  reap_fcb(fcb);
3869  goto end;
3870  }
3871 
3872  Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
3873  if (!NT_SUCCESS(Status)) {
3874  WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
3875  fcb->sd_dirty = TRUE;
3876  } else {
3877  fcb->inode_item.st_uid = sid_to_uid(owner);
3879  }
3880 
3881  find_gid(fcb, parfcb, &subjcont);
3882 
3883  ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
3884  acquire_fcb_lock_exclusive(Vcb);
3885 
3886  if (bmn->inode == 0) {
3887  inode = InterlockedIncrement64(&parfcb->subvol->lastinode);
3888  lastle = parfcb->subvol->fcbs.Blink;
3889  } else {
3890  if (bmn->inode > (UINT64)parfcb->subvol->lastinode) {
3891  inode = parfcb->subvol->lastinode = bmn->inode;
3892  lastle = parfcb->subvol->fcbs.Blink;
3893  } else {
3894  LIST_ENTRY* le = parfcb->subvol->fcbs.Flink;
3895 
3896  lastle = parfcb->subvol->fcbs.Blink;;
3897  while (le != &parfcb->subvol->fcbs) {
3898  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
3899 
3900  if (fcb2->inode == bmn->inode && !fcb2->deleted) {
3901  release_fcb_lock(Vcb);
3902  ExReleaseResourceLite(&Vcb->fileref_lock);
3903 
3904  WARN("inode collision\n");
3906  goto end;
3907  } else if (fcb2->inode > bmn->inode) {
3908  lastle = fcb2->list_entry.Blink;
3909  break;
3910  }
3911 
3912  le = le->Flink;
3913  }
3914 
3915  inode = bmn->inode;
3916  }
3917  }
3918 
3919  fcb->inode = inode;
3920  fcb->type = bmn->type;
3921 
3923  if (!fileref) {
3924  release_fcb_lock(Vcb);
3925  ExReleaseResourceLite(&Vcb->fileref_lock);
3926 
3927  ERR("out of memory\n");
3928  reap_fcb(fcb);
3930  goto end;
3931  }
3932 
3933  fileref->fcb = fcb;
3934 
3935  fcb->created = TRUE;
3936  fileref->created = TRUE;
3937 
3938  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3939  fcb->subvol->root_item.ctime = now;
3940 
3941  fileref->parent = parfileref;
3942 
3945 
3946  Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8, &name, fcb->type, &dc);
3947  if (!NT_SUCCESS(Status))