[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / fileinfo.c
1 /* Copyright (c) Mark Harmstone 2016
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
20 static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback);
21
22 static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
23 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
24 fcb* fcb = FileObject->FsContext;
25 ULONG defda;
26 BOOL inode_item_changed = FALSE;
27 NTSTATUS Status;
28
29 if (fcb->ads)
30 fcb = fcb->par;
31
32 TRACE("file = %.*S, attributes = %x\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fbi->FileAttributes);
33
34 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
35 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
36 return STATUS_INVALID_PARAMETER;
37 }
38
39 // FIXME - what if FCB is volume or root?
40 // FIXME - what about subvol roots?
41
42 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
43 // FIXME - handle times == -1
44
45 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
46 if (fbi->FileAttributes != 0) {
47 LARGE_INTEGER time;
48 BTRFS_TIME now;
49
50 defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fcb->filepart.Length > 0 && fcb->filepart.Buffer[0] == '.', TRUE);
51
52 if (fcb->type == BTRFS_TYPE_DIRECTORY)
53 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
54 else if (fcb->type == BTRFS_TYPE_SYMLINK)
55 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
56
57 // create new xattr
58 if (defda != fbi->FileAttributes) {
59 char val[64];
60
61 TRACE("inserting new DOSATTRIB xattr\n");
62 sprintf(val, "0x%lx", fbi->FileAttributes);
63
64 Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
65 if (!NT_SUCCESS(Status)) {
66 ERR("set_xattr returned %08x\n", Status);
67 return Status;
68 }
69 } else
70 delete_xattr(Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
71
72 fcb->atts = fbi->FileAttributes;
73
74 KeQuerySystemTime(&time);
75 win_time_to_unix(time, &now);
76
77 fcb->inode_item.st_ctime = now;
78 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
79 fcb->subvol->root_item.ctime = now;
80
81 inode_item_changed = TRUE;
82 }
83
84 // FIXME - CreationTime
85 // FIXME - LastAccessTime
86 // FIXME - LastWriteTime
87 // FIXME - ChangeTime
88
89 if (inode_item_changed) {
90 KEY searchkey;
91 traverse_ptr tp;
92 INODE_ITEM* ii;
93
94 fcb->inode_item.transid = Vcb->superblock.generation;
95 fcb->inode_item.sequence++;
96
97 searchkey.obj_id = fcb->inode;
98 searchkey.obj_type = TYPE_INODE_ITEM;
99 searchkey.offset = 0xffffffffffffffff;
100
101 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
102 if (!NT_SUCCESS(Status)) {
103 ERR("error - find_item returned %08x\n", Status);
104 return Status;
105 }
106
107 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
108 delete_tree_item(Vcb, &tp, rollback);
109 else
110 WARN("couldn't find old INODE_ITEM\n");
111
112 free_traverse_ptr(&tp);
113
114 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
115 if (!ii) {
116 ERR("out of memory\n");
117 return STATUS_INSUFFICIENT_RESOURCES;
118 }
119
120 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
121
122 if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
123 ERR("error - failed to insert item\n");
124 ExFreePool(ii);
125 return STATUS_INTERNAL_ERROR;
126 }
127 }
128
129 return STATUS_SUCCESS;
130 }
131
132 static NTSTATUS STDCALL set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
133 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
134 fcb* fcb = FileObject->FsContext;
135 ULONG atts;
136
137 TRACE("changing delete_on_close to %s for %.*S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
138
139 atts = fcb->ads ? fcb->par->atts : fcb->atts;
140 TRACE("atts = %x\n", atts);
141
142 if (atts & FILE_ATTRIBUTE_READONLY)
143 return STATUS_CANNOT_DELETE;
144
145 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
146 return STATUS_DIRECTORY_NOT_EMPTY;
147
148 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
149 WARN("trying to delete file which is being mapped as an image\n");
150 return STATUS_CANNOT_DELETE;
151 }
152
153 if (fcb->inode == SUBVOL_ROOT_INODE) {
154 FIXME("FIXME - subvol deletion not yet supported\n");
155 return STATUS_INTERNAL_ERROR;
156 }
157
158 fcb->delete_on_close = fdi->DeleteFile;
159 // FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
160 FileObject->DeletePending = fdi->DeleteFile;
161
162 return STATUS_SUCCESS;
163 }
164
165 static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
166 KEY searchkey;
167 traverse_ptr tp;
168 INODE_EXTREF* ier;
169 NTSTATUS Status;
170
171 searchkey.obj_id = inode;
172 searchkey.obj_type = TYPE_INODE_EXTREF;
173 searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
174
175 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
176 if (!NT_SUCCESS(Status)) {
177 ERR("error - find_item returned %08x\n", Status);
178 return Status;
179 }
180
181 if (!keycmp(&searchkey, &tp.item->key)) {
182 ULONG iersize = tp.item->size + sizeof(INODE_EXTREF) - 1 + utf8->Length;
183 UINT8* ier2;
184 UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
185
186 if (iersize > maxlen) {
187 ERR("item would be too long (%u > %u)\n", iersize, maxlen);
188 free_traverse_ptr(&tp);
189 return STATUS_INTERNAL_ERROR;
190 }
191
192 ier2 = ExAllocatePoolWithTag(PagedPool, iersize, ALLOC_TAG);
193 if (!ier2) {
194 ERR("out of memory\n");
195 free_traverse_ptr(&tp);
196 return STATUS_INSUFFICIENT_RESOURCES;
197 }
198
199 if (tp.item->size > 0)
200 RtlCopyMemory(ier2, tp.item->data, tp.item->size);
201
202 ier = (INODE_EXTREF*)&ier2[tp.item->size];
203 ier->dir = parinode;
204 ier->index = index;
205 ier->n = utf8->Length;
206 RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
207
208 delete_tree_item(Vcb, &tp, rollback);
209
210 if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) {
211 ERR("error - failed to insert item\n");
212 free_traverse_ptr(&tp);
213 return STATUS_INTERNAL_ERROR;
214 }
215 } else {
216 ier = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_EXTREF) - 1 + utf8->Length, ALLOC_TAG);
217 if (!ier) {
218 ERR("out of memory\n");
219 free_traverse_ptr(&tp);
220 return STATUS_INSUFFICIENT_RESOURCES;
221 }
222
223 ier->dir = parinode;
224 ier->index = index;
225 ier->n = utf8->Length;
226 RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
227
228 if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, rollback)) {
229 ERR("error - failed to insert item\n");
230 free_traverse_ptr(&tp);
231 return STATUS_INTERNAL_ERROR;
232 }
233 }
234
235 free_traverse_ptr(&tp);
236
237 return STATUS_SUCCESS;
238 }
239
240 NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
241 KEY searchkey;
242 traverse_ptr tp;
243 INODE_REF* ir;
244 NTSTATUS Status;
245
246 searchkey.obj_id = inode;
247 searchkey.obj_type = TYPE_INODE_REF;
248 searchkey.offset = parinode;
249
250 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
251 if (!NT_SUCCESS(Status)) {
252 ERR("error - find_item returned %08x\n", Status);
253 return Status;
254 }
255
256 if (!keycmp(&searchkey, &tp.item->key)) {
257 ULONG irsize = tp.item->size + sizeof(INODE_REF) - 1 + utf8->Length;
258 UINT8* ir2;
259 UINT32 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
260
261 if (irsize > maxlen) {
262 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
263 TRACE("INODE_REF too long, creating INODE_EXTREF\n");
264 free_traverse_ptr(&tp);
265 return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback);
266 } else {
267 ERR("item would be too long (%u > %u)\n", irsize, maxlen);
268 free_traverse_ptr(&tp);
269 return STATUS_INTERNAL_ERROR;
270 }
271 }
272
273 ir2 = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
274 if (!ir2) {
275 ERR("out of memory\n");
276 free_traverse_ptr(&tp);
277 return STATUS_INSUFFICIENT_RESOURCES;
278 }
279
280 if (tp.item->size > 0)
281 RtlCopyMemory(ir2, tp.item->data, tp.item->size);
282
283 ir = (INODE_REF*)&ir2[tp.item->size];
284 ir->index = index;
285 ir->n = utf8->Length;
286 RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
287
288 delete_tree_item(Vcb, &tp, rollback);
289
290 if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) {
291 ERR("error - failed to insert item\n");
292 free_traverse_ptr(&tp);
293 return STATUS_INTERNAL_ERROR;
294 }
295 } else {
296 ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + utf8->Length, ALLOC_TAG);
297 if (!ir) {
298 ERR("out of memory\n");
299 free_traverse_ptr(&tp);
300 return STATUS_INSUFFICIENT_RESOURCES;
301 }
302
303 ir->index = index;
304 ir->n = utf8->Length;
305 RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
306
307 if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, rollback)) {
308 ERR("error - failed to insert item\n");
309 free_traverse_ptr(&tp);
310 return STATUS_INTERNAL_ERROR;
311 }
312 }
313
314 free_traverse_ptr(&tp);
315
316 return STATUS_SUCCESS;
317 }
318
319 static NTSTATUS get_fcb_from_dir_item(device_extension* Vcb, fcb** pfcb, fcb* parent, root* subvol, DIR_ITEM* di) {
320 LIST_ENTRY* le;
321 fcb* sf2;
322 struct _fcb* c;
323 KEY searchkey;
324 traverse_ptr tp;
325 NTSTATUS Status;
326
327 le = parent->children.Flink;
328
329 while (le != &parent->children) {
330 c = CONTAINING_RECORD(le, struct _fcb, list_entry);
331
332 if (c->refcount > 0 && c->inode == di->key.obj_id && c->subvol == subvol) {
333 c->refcount++;
334 #ifdef DEBUG_FCB_REFCOUNTS
335 WARN("fcb %p: refcount now %i (%.*S)\n", c, c->refcount, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
336 #endif
337 *pfcb = c;
338 return STATUS_SUCCESS;
339 }
340
341 le = le->Flink;
342 }
343
344 sf2 = create_fcb();
345 if (!sf2) {
346 ERR("out of memory\n");
347 return STATUS_INSUFFICIENT_RESOURCES;
348 }
349
350 sf2->Vcb = Vcb;
351
352 sf2->utf8.Length = sf2->utf8.MaximumLength = di->n;
353 sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
354 if (!sf2->utf8.Buffer) {
355 ERR("out of memory\n");
356 return STATUS_INSUFFICIENT_RESOURCES;
357 }
358
359 RtlCopyMemory(sf2->utf8.Buffer, di->name, di->n);
360
361 sf2->par = parent;
362
363 parent->refcount++;
364 #ifdef DEBUG_FCB_REFCOUNTS
365 WARN("fcb %p: refcount now %i (%.*S)\n", parent, parent->refcount, parent->full_filename.Length / sizeof(WCHAR), parent->full_filename.Buffer);
366 #endif
367
368 if (di->key.obj_type == TYPE_ROOT_ITEM) {
369 root* fcbroot = Vcb->roots;
370 while (fcbroot && fcbroot->id != di->key.obj_id)
371 fcbroot = fcbroot->next;
372
373 sf2->subvol = fcbroot;
374 sf2->inode = SUBVOL_ROOT_INODE;
375 } else {
376 sf2->subvol = subvol;
377 sf2->inode = di->key.obj_id;
378 }
379
380 sf2->type = di->type;
381
382 if (Vcb->fcbs)
383 Vcb->fcbs->prev = sf2;
384
385 sf2->next = Vcb->fcbs;
386 Vcb->fcbs = sf2;
387
388 sf2->name_offset = parent->full_filename.Length / sizeof(WCHAR);
389
390 if (parent != Vcb->root_fcb)
391 sf2->name_offset++;
392
393 InsertTailList(&parent->children, &sf2->list_entry);
394
395 searchkey.obj_id = sf2->inode;
396 searchkey.obj_type = TYPE_INODE_ITEM;
397 searchkey.offset = 0xffffffffffffffff;
398
399 Status = find_item(Vcb, sf2->subvol, &tp, &searchkey, FALSE);
400 if (!NT_SUCCESS(Status)) {
401 ERR("error - find_item returned %08x\n", Status);
402 free_fcb(sf2);
403 return Status;
404 }
405
406 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
407 ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", sf2->inode, sf2->subvol->id);
408 free_traverse_ptr(&tp);
409 free_fcb(sf2);
410 return STATUS_INTERNAL_ERROR;
411 }
412
413 if (tp.item->size > 0)
414 RtlCopyMemory(&sf2->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
415
416 free_traverse_ptr(&tp);
417
418 // This is just a quick function for the sake of move_across_subvols. As such, we don't bother
419 // with sf2->atts, sf2->sd, or sf2->full_filename.
420
421 *pfcb = sf2;
422
423 return STATUS_SUCCESS;
424 }
425
426 // static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) {
427 // LONG rc = 0;
428 // LIST_ENTRY* le = Vcb->trees.Flink;
429 //
430 // while (le != &Vcb->trees) {
431 // tree* t = CONTAINING_RECORD(le, tree, list_entry);
432 //
433 // rc += t->refcount;
434 //
435 // le = le->Flink;
436 // }
437 //
438 // le = tc->Flink;
439 // while (le != tc) {
440 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
441 // tree* t;
442 //
443 // rc--;
444 //
445 // t = tc2->tree->parent;
446 // while (t) {
447 // rc--;
448 // t = t->parent;
449 // }
450 //
451 // le = le->Flink;
452 // }
453 //
454 // return rc;
455 // }
456
457 static NTSTATUS STDCALL move_inode_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, UINT64 inode, UINT64 oldparinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
458 UINT64 oldindex, index;
459 UINT32 oldcrc32;
460 INODE_ITEM* ii;
461 BOOL has_hardlink = FALSE;
462 DIR_ITEM* di;
463 KEY searchkey;
464 traverse_ptr tp, next_tp;
465 NTSTATUS Status;
466 BOOL b;
467
468 // move INODE_ITEM
469
470 fcb->inode_item.transid = Vcb->superblock.generation;
471 fcb->inode_item.sequence++;
472 fcb->inode_item.st_ctime = *now;
473
474 searchkey.obj_id = fcb->inode;
475 searchkey.obj_type = TYPE_INODE_ITEM;
476 searchkey.offset = 0;
477
478 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
479 if (!NT_SUCCESS(Status)) {
480 ERR("error - find_item returned %08x\n", Status);
481 return Status;
482 }
483
484 if (!keycmp(&searchkey, &tp.item->key)) {
485 delete_tree_item(Vcb, &tp, rollback);
486
487 if (fcb->inode_item.st_nlink > 1) {
488 fcb->inode_item.st_nlink--;
489
490 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
491 if (!ii) {
492 ERR("out of memory\n");
493 free_traverse_ptr(&tp);
494 return STATUS_INSUFFICIENT_RESOURCES;
495 }
496
497 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
498
499 if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
500 ERR("error - failed to insert item\n");
501 free_traverse_ptr(&tp);
502 return STATUS_INTERNAL_ERROR;
503 }
504
505 has_hardlink = TRUE;
506 }
507 } else {
508 WARN("couldn't find old INODE_ITEM\n");
509 }
510
511 free_traverse_ptr(&tp);
512
513 fcb->inode_item.st_nlink = 1;
514
515 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
516 if (!ii) {
517 ERR("out of memory\n");
518 return STATUS_INSUFFICIENT_RESOURCES;
519 }
520
521 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
522
523 if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
524 ERR("error - failed to insert item\n");
525 return STATUS_INTERNAL_ERROR;
526 }
527
528 oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
529
530 // delete old DIR_ITEM
531
532 Status = delete_dir_item(Vcb, fcb->subvol, oldparinode, oldcrc32, &fcb->utf8, rollback);
533 if (!NT_SUCCESS(Status)) {
534 ERR("delete_dir_item returned %08x\n", Status);
535 return Status;
536 }
537
538 // create new DIR_ITEM
539
540 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
541 if (!di) {
542 ERR("out of memory\n");
543 return STATUS_INSUFFICIENT_RESOURCES;
544 }
545
546 di->key.obj_id = inode;
547 di->key.obj_type = TYPE_INODE_ITEM;
548 di->key.offset = 0;
549 di->transid = Vcb->superblock.generation;
550 di->m = 0;
551 di->n = utf8->Length;
552 di->type = fcb->type;
553 RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
554
555 Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback);
556 if (!NT_SUCCESS(Status)) {
557 ERR("add_dir_item returned %08x\n", Status);
558 return Status;
559 }
560
561 Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, oldparinode, &fcb->utf8, &oldindex, rollback);
562 if (!NT_SUCCESS(Status)) {
563 ERR("delete_inode_ref returned %08x\n", Status);
564 return Status;
565 }
566
567 // delete DIR_INDEX
568
569 if (oldindex == 0) {
570 WARN("couldn't find old INODE_REF\n");
571 } else {
572 searchkey.obj_id = oldparinode;
573 searchkey.obj_type = TYPE_DIR_INDEX;
574 searchkey.offset = oldindex;
575
576 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
577 if (!NT_SUCCESS(Status)) {
578 ERR("error - find_item returned %08x\n", Status);
579 return Status;
580 }
581
582 if (!keycmp(&searchkey, &tp.item->key))
583 delete_tree_item(Vcb, &tp, rollback);
584 else
585 WARN("couldn't find old DIR_INDEX\n");
586
587 free_traverse_ptr(&tp);
588 }
589
590 // get new index
591
592 searchkey.obj_id = destinode;
593 searchkey.obj_type = TYPE_DIR_INDEX + 1;
594 searchkey.offset = 0;
595
596 Status = find_item(Vcb, destsubvol, &tp, &searchkey, FALSE);
597 if (!NT_SUCCESS(Status)) {
598 ERR("error - find_item returned %08x\n", Status);
599 return Status;
600 }
601
602 if (!keycmp(&searchkey, &tp.item->key)) {
603 if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
604 free_traverse_ptr(&tp);
605 tp = next_tp;
606
607 TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
608 }
609 }
610
611 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
612 index = tp.item->key.offset + 1;
613 } else
614 index = 2;
615
616 free_traverse_ptr(&tp);
617
618 // create INODE_REF
619
620 Status = add_inode_ref(Vcb, destsubvol, inode, destinode, index, utf8, rollback);
621 if (!NT_SUCCESS(Status)) {
622 ERR("add_inode_ref returned %08x\n", Status);
623 return Status;
624 }
625
626 // create DIR_INDEX
627
628 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
629 if (!di) {
630 ERR("out of memory\n");
631 return STATUS_INSUFFICIENT_RESOURCES;
632 }
633
634 di->key.obj_id = inode;
635 di->key.obj_type = TYPE_INODE_ITEM;
636 di->key.offset = 0;
637 di->transid = Vcb->superblock.generation;
638 di->m = 0;
639 di->n = utf8->Length;
640 di->type = fcb->type;
641 RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
642
643 if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) {
644 ERR("error - failed to insert item\n");
645 return STATUS_INTERNAL_ERROR;
646 }
647
648 // move XATTR_ITEMs
649
650 searchkey.obj_id = fcb->inode;
651 searchkey.obj_type = TYPE_XATTR_ITEM;
652 searchkey.offset = 0;
653
654 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
655 if (!NT_SUCCESS(Status)) {
656 ERR("error - find_item returned %08x\n", Status);
657 return Status;
658 }
659
660 do {
661 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->size > 0) {
662 di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
663
664 if (!di) {
665 ERR("out of memory\n");
666 free_traverse_ptr(&tp);
667 return STATUS_INSUFFICIENT_RESOURCES;
668 }
669
670 RtlCopyMemory(di, tp.item->data, tp.item->size);
671
672 if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_XATTR_ITEM, tp.item->key.offset, di, tp.item->size, NULL, rollback)) {
673 ERR("error - failed to insert item\n");
674 free_traverse_ptr(&tp);
675 return STATUS_INTERNAL_ERROR;
676 }
677
678 if (!has_hardlink)
679 delete_tree_item(Vcb, &tp, rollback);
680 }
681
682 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
683 if (b) {
684 free_traverse_ptr(&tp);
685 tp = next_tp;
686
687 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
688 break;
689 }
690 } while (b);
691
692 free_traverse_ptr(&tp);
693
694 // do extents
695
696 searchkey.obj_id = fcb->inode;
697 searchkey.obj_type = TYPE_EXTENT_DATA;
698 searchkey.offset = 0;
699
700 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
701 if (!NT_SUCCESS(Status)) {
702 ERR("error - find_item returned %08x\n", Status);
703 return Status;
704 }
705
706 do {
707 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
708 if (tp.item->size < sizeof(EXTENT_DATA)) {
709 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
710 } else {
711 EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
712
713 if (!ed) {
714 ERR("out of memory\n");
715 free_traverse_ptr(&tp);
716 return STATUS_INSUFFICIENT_RESOURCES;
717 }
718
719 RtlCopyMemory(ed, tp.item->data, tp.item->size);
720
721 // FIXME - update ed's generation
722
723 if (!insert_tree_item(Vcb, destsubvol, inode, TYPE_EXTENT_DATA, tp.item->key.offset, ed, tp.item->size, NULL, rollback)) {
724 ERR("error - failed to insert item\n");
725 free_traverse_ptr(&tp);
726 return STATUS_INTERNAL_ERROR;
727 }
728
729 if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
730 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
731
732 if (ed2->address != 0) {
733 Status = add_extent_ref(Vcb, ed2->address, ed2->size, destsubvol, inode, tp.item->key.offset, rollback);
734
735 if (!NT_SUCCESS(Status)) {
736 ERR("add_extent_ref returned %08x\n", Status);
737 free_traverse_ptr(&tp);
738 return Status;
739 }
740
741 if (!has_hardlink) {
742 Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, NULL, rollback);
743
744 if (!NT_SUCCESS(Status)) {
745 ERR("remove_extent_ref returned %08x\n", Status);
746 free_traverse_ptr(&tp);
747 return Status;
748 }
749 }
750 }
751 }
752
753 if (!has_hardlink)
754 delete_tree_item(Vcb, &tp, rollback);
755 }
756 }
757
758 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
759 if (b) {
760 free_traverse_ptr(&tp);
761 tp = next_tp;
762
763 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_EXTENT_DATA)
764 break;
765 }
766 } while (b);
767
768 free_traverse_ptr(&tp);
769
770 return STATUS_SUCCESS;
771 }
772
773 typedef struct {
774 fcb* fcb;
775 UINT8 level;
776 UINT32 crc32;
777 UINT64 newinode;
778 UINT64 newparinode;
779 BOOL subvol;
780 ANSI_STRING utf8;
781 LIST_ENTRY list_entry;
782 } dir_list;
783
784 static NTSTATUS add_to_dir_list(fcb* fcb, UINT8 level, LIST_ENTRY* dl, UINT64 newparinode, BOOL* empty) {
785 KEY searchkey;
786 traverse_ptr tp, next_tp;
787 BOOL b;
788 NTSTATUS Status;
789
790 *empty = TRUE;
791
792 searchkey.obj_id = fcb->inode;
793 searchkey.obj_type = TYPE_DIR_INDEX;
794 searchkey.offset = 2;
795
796 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
797 if (!NT_SUCCESS(Status)) {
798 ERR("error - find_item returned %08x\n", Status);
799 return Status;
800 }
801
802 do {
803 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX) {
804 if (tp.item->size < sizeof(DIR_ITEM)) {
805 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
806 } else {
807 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
808 struct _fcb* child;
809 dir_list* dl2;
810
811 if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n + di->m) {
812 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
813 } else {
814 if (di->key.obj_type == TYPE_INODE_ITEM || di->key.obj_type == TYPE_ROOT_ITEM) {
815 if (di->key.obj_type == TYPE_ROOT_ITEM)
816 TRACE("moving subvol %llx\n", di->key.obj_id);
817 else
818 TRACE("moving inode %llx\n", di->key.obj_id);
819
820 *empty = FALSE;
821
822 Status = get_fcb_from_dir_item(fcb->Vcb, &child, fcb, fcb->subvol, di);
823 if (!NT_SUCCESS(Status)) {
824 ERR("get_fcb_from_dir_item returned %08x\n", Status);
825 free_traverse_ptr(&tp);
826 return Status;
827 }
828
829 dl2 = ExAllocatePoolWithTag(PagedPool, sizeof(dir_list), ALLOC_TAG);
830 if (!dl2) {
831 ERR("out of memory\n");
832 free_traverse_ptr(&tp);
833 return STATUS_INSUFFICIENT_RESOURCES;
834 }
835
836 dl2->fcb = child;
837 dl2->level = level;
838 dl2->newparinode = newparinode;
839 dl2->subvol = di->key.obj_type == TYPE_ROOT_ITEM;
840
841 dl2->utf8.Length = dl2->utf8.MaximumLength = di->n;
842 dl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dl2->utf8.MaximumLength, ALLOC_TAG);
843 if (!dl2->utf8.Buffer) {
844 ERR("out of memory\n");
845 free_traverse_ptr(&tp);
846 return STATUS_INSUFFICIENT_RESOURCES;
847 }
848
849 RtlCopyMemory(dl2->utf8.Buffer, di->name, dl2->utf8.Length);
850 dl2->crc32 = calc_crc32c(0xfffffffe, (UINT8*)dl2->utf8.Buffer, (ULONG)dl2->utf8.Length);
851
852 InsertTailList(dl, &dl2->list_entry);
853 }
854 }
855 }
856 }
857
858 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
859 if (b) {
860 free_traverse_ptr(&tp);
861 tp = next_tp;
862
863 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
864 break;
865 }
866 } while (b);
867
868 free_traverse_ptr(&tp);
869
870 return STATUS_SUCCESS;
871 }
872
873 static NTSTATUS STDCALL move_across_subvols(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, BTRFS_TIME* now, LIST_ENTRY* rollback) {
874 UINT64 inode, oldparinode;
875 NTSTATUS Status;
876 LIST_ENTRY dl;
877
878 if (destsubvol->lastinode == 0)
879 get_last_inode(Vcb, destsubvol);
880
881 inode = destsubvol->lastinode + 1;
882 destsubvol->lastinode++;
883
884 oldparinode = fcb->subvol == fcb->par->subvol ? fcb->par->inode : SUBVOL_ROOT_INODE;
885
886 Status = move_inode_across_subvols(Vcb, fcb, destsubvol, destinode, inode, oldparinode, utf8, crc32, now, rollback);
887 if (!NT_SUCCESS(Status)) {
888 ERR("move_inode_across_subvols returned %08x\n", Status);
889 return Status;
890 }
891
892 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
893 BOOL b, empty;
894 UINT8 level, max_level;
895 LIST_ENTRY* le;
896
897 InitializeListHead(&dl);
898
899 add_to_dir_list(fcb, 0, &dl, inode, &b);
900
901 level = 0;
902 do {
903 empty = TRUE;
904
905 le = dl.Flink;
906 while (le != &dl) {
907 dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
908
909 if (dl2->level == level && !dl2->subvol) {
910 inode++;
911 destsubvol->lastinode++;
912
913 dl2->newinode = inode;
914
915 if (dl2->fcb->type == BTRFS_TYPE_DIRECTORY) {
916 add_to_dir_list(dl2->fcb, level+1, &dl, dl2->newinode, &b);
917 if (!b) empty = FALSE;
918 }
919 }
920
921 le = le->Flink;
922 }
923
924 if (!empty) level++;
925 } while (!empty);
926
927 max_level = level;
928
929 for (level = 0; level <= max_level; level++) {
930 TRACE("level %u\n", level);
931
932 le = dl.Flink;
933 while (le != &dl) {
934 dir_list* dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
935
936 if (dl2->level == level) {
937 if (dl2->subvol) {
938 TRACE("subvol %llx\n", dl2->fcb->subvol->id);
939
940 Status = move_subvol(Vcb, dl2->fcb, destsubvol, dl2->newparinode, &dl2->utf8, dl2->crc32, dl2->crc32, now, FALSE, rollback);
941 if (!NT_SUCCESS(Status)) {
942 ERR("move_subvol returned %08x\n", Status);
943 return Status;
944 }
945 } else {
946 TRACE("inode %llx\n", dl2->fcb->inode);
947
948 Status = move_inode_across_subvols(Vcb, dl2->fcb, destsubvol, dl2->newparinode, dl2->newinode, dl2->fcb->par->inode, &dl2->utf8, dl2->crc32, now, rollback);
949 if (!NT_SUCCESS(Status)) {
950 ERR("move_inode_across_subvols returned %08x\n", Status);
951 return Status;
952 }
953 }
954 }
955
956 le = le->Flink;
957 }
958 }
959
960 while (!IsListEmpty(&dl)) {
961 dir_list* dl2;
962
963 le = RemoveHeadList(&dl);
964 dl2 = CONTAINING_RECORD(le, dir_list, list_entry);
965
966 ExFreePool(dl2->utf8.Buffer);
967 free_fcb(dl2->fcb);
968
969 ExFreePool(dl2);
970 }
971 }
972
973 fcb->inode = inode;
974 fcb->subvol = destsubvol;
975
976 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
977 fcb->subvol->root_item.ctime = *now;
978
979 return STATUS_SUCCESS;
980 }
981
982 static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) {
983 KEY searchkey;
984 traverse_ptr tp;
985 NTSTATUS Status;
986
987 searchkey.obj_id = parsubvolid;
988 searchkey.obj_type = TYPE_ROOT_REF;
989 searchkey.offset = subvolid;
990
991 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
992 if (!NT_SUCCESS(Status)) {
993 ERR("error - find_item returned %08x\n", Status);
994 return Status;
995 }
996
997 if (!keycmp(&searchkey, &tp.item->key)) {
998 if (tp.item->size < sizeof(ROOT_REF)) {
999 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
1000 } else {
1001 ROOT_REF* rr;
1002 ULONG len;
1003
1004 rr = (ROOT_REF*)tp.item->data;
1005 len = tp.item->size;
1006
1007 do {
1008 ULONG itemlen;
1009
1010 if (len < sizeof(ROOT_REF) || len < sizeof(ROOT_REF) - 1 + rr->n) {
1011 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1012 break;
1013 }
1014
1015 itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n;
1016
1017 if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) {
1018 ULONG newlen = tp.item->size - itemlen;
1019
1020 delete_tree_item(Vcb, &tp, rollback);
1021
1022 if (newlen == 0) {
1023 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1024 } else {
1025 UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff;
1026
1027 if (!newrr) {
1028 ERR("out of memory\n");
1029 free_traverse_ptr(&tp);
1030 return STATUS_INSUFFICIENT_RESOURCES;
1031 }
1032
1033 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1034
1035 if ((UINT8*)rr > tp.item->data) {
1036 RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data);
1037 rroff = newrr + ((UINT8*)rr - tp.item->data);
1038 } else {
1039 rroff = newrr;
1040 }
1041
1042 if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size)
1043 RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data));
1044
1045 insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, rollback);
1046 }
1047
1048 if (index)
1049 *index = rr->index;
1050
1051 break;
1052 }
1053
1054 if (len > itemlen) {
1055 len -= itemlen;
1056 rr = (ROOT_REF*)&rr->name[rr->n];
1057 } else
1058 break;
1059 } while (len > 0);
1060 }
1061 } else {
1062 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id);
1063 }
1064
1065 free_traverse_ptr(&tp);
1066
1067 return STATUS_SUCCESS;
1068 }
1069
1070 static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) {
1071 KEY searchkey;
1072 traverse_ptr tp;
1073 NTSTATUS Status;
1074
1075 searchkey.obj_id = parsubvolid;
1076 searchkey.obj_type = TYPE_ROOT_REF;
1077 searchkey.offset = subvolid;
1078
1079 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
1080 if (!NT_SUCCESS(Status)) {
1081 ERR("error - find_item returned %08x\n", Status);
1082 return Status;
1083 }
1084
1085 if (!keycmp(&searchkey, &tp.item->key)) {
1086 ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n;
1087 UINT8* rr2;
1088
1089 rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
1090 if (!rr2) {
1091 ERR("out of memory\n");
1092 return STATUS_INSUFFICIENT_RESOURCES;
1093 }
1094
1095 if (tp.item->size > 0)
1096 RtlCopyMemory(rr2, tp.item->data, tp.item->size);
1097
1098 RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n);
1099 ExFreePool(rr);
1100
1101 delete_tree_item(Vcb, &tp, rollback);
1102
1103 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, rollback)) {
1104 ERR("error - failed to insert item\n");
1105 ExFreePool(rr2);
1106 free_traverse_ptr(&tp);
1107 return STATUS_INTERNAL_ERROR;
1108 }
1109 } else {
1110 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, rollback)) {
1111 ERR("error - failed to insert item\n");
1112 ExFreePool(rr);
1113 free_traverse_ptr(&tp);
1114 return STATUS_INTERNAL_ERROR;
1115 }
1116 }
1117
1118 free_traverse_ptr(&tp);
1119
1120 return STATUS_SUCCESS;
1121 }
1122
1123 static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) {
1124 KEY searchkey;
1125 traverse_ptr tp;
1126 UINT8* data;
1127 ULONG datalen;
1128 NTSTATUS Status;
1129
1130 searchkey.obj_id = parsubvolid;
1131 searchkey.obj_type = TYPE_ROOT_REF;
1132 searchkey.offset = subvolid;
1133
1134 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
1135 if (!NT_SUCCESS(Status)) {
1136 ERR("error - find_item returned %08x\n", Status);
1137 return Status;
1138 }
1139
1140 if (!keycmp(&tp.item->key, &searchkey) && tp.item->size > 0) {
1141 datalen = tp.item->size;
1142
1143 data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
1144 if (!data) {
1145 ERR("out of memory\n");
1146 return STATUS_INSUFFICIENT_RESOURCES;
1147 }
1148
1149 RtlCopyMemory(data, tp.item->data, datalen);
1150 } else {
1151 datalen = 0;
1152 }
1153
1154 free_traverse_ptr(&tp);
1155
1156 searchkey.obj_id = subvolid;
1157 searchkey.obj_type = TYPE_ROOT_BACKREF;
1158 searchkey.offset = parsubvolid;
1159
1160 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
1161 if (!NT_SUCCESS(Status)) {
1162 ERR("error - find_item returned %08x\n", Status);
1163 return Status;
1164 }
1165
1166 if (!keycmp(&tp.item->key, &searchkey))
1167 delete_tree_item(Vcb, &tp, rollback);
1168
1169 free_traverse_ptr(&tp);
1170
1171 if (datalen > 0) {
1172 if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, rollback)) {
1173 ERR("error - failed to insert item\n");
1174 ExFreePool(data);
1175 return STATUS_INTERNAL_ERROR;
1176 }
1177 }
1178
1179 return STATUS_SUCCESS;
1180 }
1181
1182 static NTSTATUS STDCALL move_subvol(device_extension* Vcb, fcb* fcb, root* destsubvol, UINT64 destinode, PANSI_STRING utf8, UINT32 crc32, UINT32 oldcrc32, BTRFS_TIME* now, BOOL ReplaceIfExists, LIST_ENTRY* rollback) {
1183 DIR_ITEM* di;
1184 NTSTATUS Status;
1185 KEY searchkey;
1186 traverse_ptr tp;
1187 UINT64 oldindex, index;
1188 ROOT_REF* rr;
1189
1190 // delete old DIR_ITEM
1191
1192 Status = delete_dir_item(Vcb, fcb->par->subvol, fcb->par->inode, oldcrc32, &fcb->utf8, rollback);
1193 if (!NT_SUCCESS(Status)) {
1194 ERR("delete_dir_item returned %08x\n", Status);
1195 return Status;
1196 }
1197
1198 // create new DIR_ITEM
1199
1200 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
1201 if (!di) {
1202 ERR("out of memory\n");
1203 return STATUS_INSUFFICIENT_RESOURCES;
1204 }
1205
1206 di->key.obj_id = fcb->subvol->id;
1207 di->key.obj_type = TYPE_ROOT_ITEM;
1208 di->key.offset = 0;
1209 di->transid = Vcb->superblock.generation;
1210 di->m = 0;
1211 di->n = utf8->Length;
1212 di->type = fcb->type;
1213 RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
1214
1215 Status = add_dir_item(Vcb, destsubvol, destinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8->Length, rollback);
1216 if (!NT_SUCCESS(Status)) {
1217 ERR("add_dir_item returned %08x\n", Status);
1218 return Status;
1219 }
1220
1221 // delete old ROOT_REF
1222
1223 oldindex = 0;
1224
1225 Status = delete_root_ref(Vcb, fcb->subvol->id, fcb->par->subvol->id, fcb->par->inode, &fcb->utf8, &oldindex, rollback);
1226 if (!NT_SUCCESS(Status)) {
1227 ERR("delete_root_ref returned %08x\n", Status);
1228 return Status;
1229 }
1230
1231 TRACE("root index = %llx\n", oldindex);
1232
1233 // delete old DIR_INDEX
1234
1235 if (oldindex != 0) {
1236 searchkey.obj_id = fcb->par->inode;
1237 searchkey.obj_type = TYPE_DIR_INDEX;
1238 searchkey.offset = oldindex;
1239
1240 Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
1241 if (!NT_SUCCESS(Status)) {
1242 ERR("error - find_item returned %08x\n", Status);
1243 return Status;
1244 }
1245
1246 if (!keycmp(&searchkey, &tp.item->key)) {
1247 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1248
1249 delete_tree_item(Vcb, &tp, rollback);
1250 } else {
1251 WARN("could not find old DIR_INDEX entry\n");
1252 }
1253
1254 free_traverse_ptr(&tp);
1255 }
1256
1257 // create new DIR_INDEX
1258
1259 if (fcb->par->subvol == destsubvol && fcb->par->inode == destinode) {
1260 index = oldindex;
1261 } else {
1262 index = find_next_dir_index(Vcb, destsubvol, destinode);
1263 }
1264
1265 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8->Length, ALLOC_TAG);
1266 if (!di) {
1267 ERR("out of memory\n");
1268 return STATUS_INSUFFICIENT_RESOURCES;
1269 }
1270
1271 di->key.obj_id = fcb->subvol->id;
1272 di->key.obj_type = TYPE_ROOT_ITEM;
1273 di->key.offset = 0;
1274 di->transid = Vcb->superblock.generation;
1275 di->m = 0;
1276 di->n = utf8->Length;
1277 di->type = fcb->type;
1278 RtlCopyMemory(di->name, utf8->Buffer, utf8->Length);
1279
1280 if (!insert_tree_item(Vcb, destsubvol, destinode, TYPE_DIR_INDEX, index, di, sizeof(DIR_ITEM) - 1 + utf8->Length, NULL, rollback)) {
1281 ERR("error - failed to insert item\n");
1282 return STATUS_INTERNAL_ERROR;
1283 }
1284
1285 // create new ROOT_REF
1286
1287 rr = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_REF) - 1 + utf8->Length, ALLOC_TAG);
1288 if (!rr) {
1289 ERR("out of memory\n");
1290 return STATUS_INSUFFICIENT_RESOURCES;
1291 }
1292
1293 rr->dir = destinode;
1294 rr->index = index;
1295 rr->n = utf8->Length;
1296 RtlCopyMemory(rr->name, utf8->Buffer, utf8->Length);
1297
1298 Status = add_root_ref(Vcb, fcb->subvol->id, destsubvol->id, rr, rollback);
1299 if (!NT_SUCCESS(Status)) {
1300 ERR("add_root_ref returned %08x\n", Status);
1301 return Status;
1302 }
1303
1304 Status = update_root_backref(Vcb, fcb->subvol->id, fcb->par->subvol->id, rollback);
1305 if (!NT_SUCCESS(Status)) {
1306 ERR("update_root_backref 1 returned %08x\n", Status);
1307 return Status;
1308 }
1309
1310 if (fcb->par->subvol != destsubvol) {
1311 Status = update_root_backref(Vcb, fcb->subvol->id, destsubvol->id, rollback);
1312 if (!NT_SUCCESS(Status)) {
1313 ERR("update_root_backref 1 returned %08x\n", Status);
1314 return Status;
1315 }
1316
1317 fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
1318 fcb->par->subvol->root_item.ctime = *now;
1319 }
1320
1321 destsubvol->root_item.ctransid = Vcb->superblock.generation;
1322 destsubvol->root_item.ctime = *now;
1323
1324 return STATUS_SUCCESS;
1325 }
1326
1327 static BOOL has_open_children(fcb* fcb) {
1328 LIST_ENTRY* le = fcb->children.Flink;
1329 struct _fcb* c;
1330
1331 while (le != &fcb->children) {
1332 c = CONTAINING_RECORD(le, struct _fcb, list_entry);
1333
1334 if (c->refcount > 0) {
1335 if (c->open_count > 0)
1336 return TRUE;
1337
1338 if (has_open_children(c))
1339 return TRUE;
1340 }
1341
1342 le = le->Flink;
1343 }
1344
1345 return FALSE;
1346 }
1347
1348 static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists, LIST_ENTRY* rollback) {
1349 FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
1350 fcb *fcb = FileObject->FsContext, *tfofcb, *oldparfcb, *oldfcb;
1351 root* parsubvol;
1352 UINT64 parinode, dirpos;
1353 WCHAR* fn;
1354 UNICODE_STRING fnus;
1355 ULONG fnlen, utf8len, disize;
1356 NTSTATUS Status;
1357 ANSI_STRING utf8;
1358 UINT32 crc32, oldcrc32;
1359 KEY searchkey;
1360 traverse_ptr tp, next_tp;
1361 DIR_ITEM* di;
1362 LARGE_INTEGER time;
1363 BTRFS_TIME now;
1364 BOOL across_directories;
1365 INODE_ITEM* ii;
1366
1367 // FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
1368
1369 TRACE(" tfo = %p\n", tfo);
1370 TRACE(" ReplaceIfExists = %u\n", ReplaceIfExists);
1371 TRACE(" RootDirectory = %p\n", fri->RootDirectory);
1372 TRACE(" FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
1373
1374 KeQuerySystemTime(&time);
1375 win_time_to_unix(time, &now);
1376
1377 utf8.Buffer = NULL;
1378
1379 if (!fcb->par) {
1380 ERR("error - tried to rename file with no parent\n");
1381 Status = STATUS_ACCESS_DENIED;
1382 goto end;
1383 }
1384
1385 fn = fri->FileName;
1386 fnlen = fri->FileNameLength / sizeof(WCHAR);
1387
1388 if (!tfo) {
1389 parsubvol = fcb->par->subvol;
1390 parinode = fcb->par->inode;
1391 tfofcb = NULL;
1392
1393 across_directories = FALSE;
1394 } else {
1395 LONG i;
1396
1397 tfofcb = tfo->FsContext;
1398 parsubvol = tfofcb->subvol;
1399 parinode = tfofcb->inode;
1400
1401 for (i = fnlen - 1; i >= 0; i--) {
1402 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
1403 fn = &fri->FileName[i+1];
1404 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
1405 break;
1406 }
1407 }
1408
1409 across_directories = parsubvol != fcb->par->subvol || parinode != fcb->par->inode;
1410 }
1411
1412 fnus.Buffer = fn;
1413 fnus.Length = fnus.MaximumLength = fnlen * sizeof(WCHAR);
1414
1415 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
1416
1417 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1418 if (!NT_SUCCESS(Status))
1419 goto end;
1420
1421 utf8.MaximumLength = utf8.Length = utf8len;
1422 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1423 if (!utf8.Buffer) {
1424 ERR("out of memory\n");
1425 Status = STATUS_INSUFFICIENT_RESOURCES;
1426 goto end;
1427 }
1428
1429 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1430 if (!NT_SUCCESS(Status))
1431 goto end;
1432
1433 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, (ULONG)utf8.Length);
1434
1435 // FIXME - set to crc32 if utf8 and oldutf8 are identical
1436 oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fcb->utf8.Buffer, (ULONG)fcb->utf8.Length);
1437
1438 // TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
1439
1440 oldfcb = NULL;
1441
1442 Status = get_fcb(Vcb, &oldfcb, &fnus, tfo ? tfo->FsContext : NULL, FALSE);
1443
1444 if (NT_SUCCESS(Status)) {
1445 WARN("destination file %.*S already exists\n", oldfcb->full_filename.Length / sizeof(WCHAR), oldfcb->full_filename.Buffer);
1446
1447 if (fcb != oldfcb && !(oldfcb->open_count == 0 && oldfcb->deleted)) {
1448 if (!ReplaceIfExists) {
1449 Status = STATUS_OBJECT_NAME_COLLISION;
1450 goto end;
1451 } else if (oldfcb->open_count >= 1 && !oldfcb->deleted) {
1452 WARN("trying to overwrite open file\n");
1453 Status = STATUS_ACCESS_DENIED;
1454 goto end;
1455 }
1456
1457 if (oldfcb->type == BTRFS_TYPE_DIRECTORY) {
1458 WARN("trying to overwrite directory\n");
1459 Status = STATUS_ACCESS_DENIED;
1460 goto end;
1461 }
1462 }
1463 }
1464
1465 if (has_open_children(fcb)) {
1466 WARN("trying to rename file with open children\n");
1467 Status = STATUS_ACCESS_DENIED;
1468 goto end;
1469 }
1470
1471 if (oldfcb) {
1472 Status = delete_fcb(oldfcb, NULL, rollback);
1473 if (!NT_SUCCESS(Status)) {
1474 ERR("delete_fcb returned %08x\n", Status);
1475 goto end;
1476 }
1477 }
1478
1479 if (fcb->inode == SUBVOL_ROOT_INODE) {
1480 UNICODE_STRING filename;
1481
1482 filename.Buffer = fn;
1483 filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
1484
1485 Status = move_subvol(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, oldcrc32, &now, ReplaceIfExists, rollback);
1486
1487 if (!NT_SUCCESS(Status)) {
1488 ERR("move_subvol returned %08x\n", Status);
1489 goto end;
1490 }
1491 } else if (parsubvol != fcb->subvol) {
1492 UNICODE_STRING filename;
1493
1494 filename.Buffer = fn;
1495 filename.MaximumLength = filename.Length = fnlen * sizeof(WCHAR);
1496
1497 Status = move_across_subvols(Vcb, fcb, tfofcb->subvol, tfofcb->inode, &utf8, crc32, &now, rollback);
1498
1499 if (!NT_SUCCESS(Status)) {
1500 ERR("move_across_subvols returned %08x\n", Status);
1501 goto end;
1502 }
1503 } else {
1504 UINT64 oldindex;
1505 INODE_ITEM* ii;
1506
1507 // delete old DIR_ITEM entry
1508
1509 Status = delete_dir_item(Vcb, fcb->subvol, fcb->par->inode, oldcrc32, &fcb->utf8, rollback);
1510 if (!NT_SUCCESS(Status)) {
1511 ERR("delete_dir_item returned %08x\n", Status);
1512 return Status;
1513 }
1514
1515 // FIXME - make sure fcb's filepart matches the case on disk
1516
1517 // create new DIR_ITEM entry
1518
1519 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + utf8.Length, ALLOC_TAG);
1520 if (!di) {
1521 ERR("out of memory\n");
1522 return STATUS_INSUFFICIENT_RESOURCES;
1523 }
1524
1525 di->key.obj_id = fcb->inode;
1526 di->key.obj_type = TYPE_INODE_ITEM;
1527 di->key.offset = 0;
1528 di->transid = Vcb->superblock.generation;
1529 di->m = 0;
1530 di->n = utf8.Length;
1531 di->type = fcb->type;
1532 RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
1533
1534 Status = add_dir_item(Vcb, parsubvol, parinode, crc32, di, sizeof(DIR_ITEM) - 1 + utf8.Length, rollback);
1535 if (!NT_SUCCESS(Status)) {
1536 ERR("add_dir_item returned %08x\n", Status);
1537 return Status;
1538 }
1539
1540 oldindex = 0;
1541
1542 Status = delete_inode_ref(Vcb, fcb->subvol, fcb->inode, fcb->par->inode, &fcb->utf8, &oldindex, rollback);
1543 if (!NT_SUCCESS(Status)) {
1544 ERR("delete_inode_ref returned %08x\n", Status);
1545 return Status;
1546 }
1547
1548 // delete old DIR_INDEX entry
1549
1550 if (oldindex != 0) {
1551 searchkey.obj_id = fcb->par->inode;
1552 searchkey.obj_type = TYPE_DIR_INDEX;
1553 searchkey.offset = oldindex;
1554
1555 Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
1556 if (!NT_SUCCESS(Status)) {
1557 ERR("error - find_item returned %08x\n", Status);
1558 goto end;
1559 }
1560
1561 if (!keycmp(&tp.item->key, &searchkey))
1562 delete_tree_item(Vcb, &tp, rollback);
1563 else {
1564 WARN("couldn't find DIR_INDEX\n");
1565 }
1566
1567 free_traverse_ptr(&tp);
1568 } else {
1569 WARN("couldn't get index from INODE_REF\n");
1570 }
1571
1572 // create new DIR_INDEX entry
1573
1574 if (parsubvol != fcb->par->subvol || parinode != fcb->par->inode) {
1575 searchkey.obj_id = parinode;
1576 searchkey.obj_type = TYPE_DIR_INDEX + 1;
1577 searchkey.offset = 0;
1578
1579 Status = find_item(Vcb, parsubvol, &tp, &searchkey, FALSE);
1580 if (!NT_SUCCESS(Status)) {
1581 ERR("error - find_item returned %08x\n", Status);
1582 goto end;
1583 }
1584
1585 dirpos = 2;
1586
1587 do {
1588 TRACE("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1589
1590 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
1591 dirpos = tp.item->key.offset + 1;
1592 break;
1593 }
1594
1595 if (find_prev_item(Vcb, &tp, &next_tp, FALSE)) {
1596 free_traverse_ptr(&tp);
1597 tp = next_tp;
1598 } else
1599 break;
1600 } while (tp.item->key.obj_id >= parinode && tp.item->key.obj_type >= TYPE_DIR_INDEX);
1601
1602 free_traverse_ptr(&tp);
1603 } else
1604 dirpos = oldindex;
1605
1606 disize = (ULONG)(sizeof(DIR_ITEM) - 1 + utf8.Length);
1607 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
1608 if (!di) {
1609 ERR("out of memory\n");
1610 return STATUS_INSUFFICIENT_RESOURCES;
1611 }
1612
1613 di->key.obj_id = fcb->inode;
1614 di->key.obj_type = TYPE_INODE_ITEM;
1615 di->key.offset = 0;
1616 di->transid = Vcb->superblock.generation;
1617 di->m = 0;
1618 di->n = (UINT16)utf8.Length;
1619 di->type = fcb->type;
1620 RtlCopyMemory(di->name, utf8.Buffer, utf8.Length);
1621
1622 if (!insert_tree_item(Vcb, parsubvol, parinode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback))
1623 ERR("error - failed to insert item\n");
1624
1625 // create new INODE_REF entry
1626
1627 Status = add_inode_ref(Vcb, parsubvol, fcb->inode, parinode, dirpos, &utf8, rollback);
1628 if (!NT_SUCCESS(Status)) {
1629 ERR("add_inode_ref returned %08x\n", Status);
1630 return Status;
1631 }
1632
1633 fcb->inode_item.transid = Vcb->superblock.generation;
1634 fcb->inode_item.sequence++;
1635 fcb->inode_item.st_ctime = now;
1636
1637 searchkey.obj_id = fcb->inode;
1638 searchkey.obj_type = TYPE_INODE_ITEM;
1639 searchkey.offset = 0xffffffffffffffff;
1640
1641 Status = find_item(Vcb, parsubvol, &tp, &searchkey, FALSE);
1642 if (!NT_SUCCESS(Status)) {
1643 ERR("error - find_item returned %08x\n", Status);
1644 goto end;
1645 }
1646
1647 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
1648 delete_tree_item(Vcb, &tp, rollback);
1649
1650 free_traverse_ptr(&tp);
1651
1652 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1653 if (!ii) {
1654 ERR("out of memory\n");
1655 return STATUS_INSUFFICIENT_RESOURCES;
1656 }
1657
1658 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
1659
1660 if (!insert_tree_item(Vcb, parsubvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
1661 WARN("insert_tree_item failed\n");
1662 }
1663 }
1664
1665 // update directory INODE_ITEMs
1666
1667 fcb->par->inode_item.transid = Vcb->superblock.generation;
1668 fcb->par->inode_item.sequence++;
1669 fcb->par->inode_item.st_ctime = now;
1670 fcb->par->inode_item.st_mtime = now;
1671
1672 TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
1673 if (!tfofcb || (fcb->par->inode == tfofcb->inode && fcb->par->subvol == tfofcb->subvol)) {
1674 fcb->par->inode_item.st_size += 2 * (utf8.Length - fcb->utf8.Length);
1675 } else {
1676 fcb->par->inode_item.st_size -= 2 * fcb->utf8.Length;
1677 TRACE("tfofcb->inode_item.st_size was %llx\n", tfofcb->inode_item.st_size);
1678 tfofcb->inode_item.st_size += 2 * utf8.Length;
1679 TRACE("tfofcb->inode_item.st_size now %llx\n", tfofcb->inode_item.st_size);
1680 tfofcb->inode_item.transid = Vcb->superblock.generation;
1681 tfofcb->inode_item.sequence++;
1682 tfofcb->inode_item.st_ctime = now;
1683 tfofcb->inode_item.st_mtime = now;
1684 }
1685 TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
1686
1687 if (oldfcb && oldfcb->par != fcb->par) {
1688 TRACE("oldfcb->par->inode_item.st_size was %llx\n", oldfcb->par->inode_item.st_size);
1689 oldfcb->par->inode_item.st_size -= 2 * oldfcb->utf8.Length;
1690 TRACE("oldfcb->par->inode_item.st_size now %llx\n", oldfcb->par->inode_item.st_size);
1691 }
1692
1693 searchkey.obj_id = fcb->par->inode;
1694 searchkey.obj_type = TYPE_INODE_ITEM;
1695 searchkey.offset = 0xffffffffffffffff;
1696
1697 Status = find_item(Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
1698 if (!NT_SUCCESS(Status)) {
1699 ERR("error - find_item returned %08x\n", Status);
1700 return Status;
1701 }
1702
1703 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
1704 delete_tree_item(Vcb, &tp, rollback);
1705
1706 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1707 if (!ii) {
1708 ERR("out of memory\n");
1709 free_traverse_ptr(&tp);
1710 return STATUS_INSUFFICIENT_RESOURCES;
1711 }
1712
1713 RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
1714
1715 if (!insert_tree_item(Vcb, fcb->par->subvol, fcb->par->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback))
1716 WARN("insert_tree_item failed\n");
1717
1718 free_traverse_ptr(&tp);
1719
1720 if (tfofcb && (fcb->par->inode != tfofcb->inode || fcb->par->subvol != tfofcb->subvol)) {
1721 searchkey.obj_id = tfofcb->inode;
1722 searchkey.obj_type = TYPE_INODE_ITEM;
1723 searchkey.offset = 0xffffffffffffffff;
1724
1725 Status = find_item(Vcb, tfofcb->subvol, &tp, &searchkey, FALSE);
1726 if (!NT_SUCCESS(Status)) {
1727 ERR("error - find_item returned %08x\n", Status);
1728 return Status;
1729 }
1730
1731 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
1732 delete_tree_item(Vcb, &tp, rollback);
1733
1734 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1735 if (!ii) {
1736 ERR("out of memory\n");
1737 free_traverse_ptr(&tp);
1738 return STATUS_INSUFFICIENT_RESOURCES;
1739 }
1740
1741 RtlCopyMemory(ii, &tfofcb->inode_item, sizeof(INODE_ITEM));
1742
1743 if (!insert_tree_item(Vcb, tfofcb->subvol, tfofcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback))
1744 WARN("insert_tree_item failed\n");
1745
1746 free_traverse_ptr(&tp);
1747 }
1748
1749 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
1750 fcb->subvol->root_item.ctime = now;
1751
1752 // FIXME - handle overwrite by rename here
1753 FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
1754 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
1755 across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME, NULL);
1756
1757 // FIXME - change full_filename and name_offset of open children
1758
1759 if (fnlen != fcb->filepart.Length / sizeof(WCHAR) || RtlCompareMemory(fn, fcb->filepart.Buffer, fcb->filepart.Length) != fcb->filepart.Length) {
1760 RtlFreeUnicodeString(&fcb->filepart);
1761 fcb->filepart.Length = fcb->filepart.MaximumLength = (USHORT)(fnlen * sizeof(WCHAR));
1762 fcb->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->filepart.Length, ALLOC_TAG);
1763
1764 if (!fcb->filepart.Buffer) {
1765 ERR("out of memory\n");
1766
1767 Status = STATUS_INSUFFICIENT_RESOURCES;
1768 goto end;
1769 }
1770
1771 RtlCopyMemory(fcb->filepart.Buffer, fn, fcb->filepart.Length);
1772 }
1773
1774 if (tfo && tfofcb != fcb->par) {
1775 oldparfcb = fcb->par;
1776 fcb->par = tfofcb;
1777
1778 fcb->par->refcount++;
1779
1780 RemoveEntryList(&fcb->list_entry);
1781 InsertTailList(&fcb->par->children, &fcb->list_entry);
1782
1783 #ifdef DEBUG_FCB_REFCOUNTS
1784 WARN("fcb %p: refcount now %i (%.*S)\n", fcb->par, fcb->par->refcount, fcb->par->full_filename.Length / sizeof(WCHAR), fcb->par->full_filename.Buffer);
1785 #endif
1786 free_fcb(oldparfcb);
1787 }
1788
1789 ExFreePool(fcb->utf8.Buffer);
1790 fcb->utf8 = utf8;
1791 utf8.Buffer = NULL;
1792
1793 // change fcb->full_filename
1794
1795 fcb->full_filename.MaximumLength = fcb->par->full_filename.Length + fcb->filepart.Length;
1796 if (fcb->par->par) fcb->full_filename.MaximumLength += sizeof(WCHAR);
1797 ExFreePool(fcb->full_filename.Buffer);
1798
1799 fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->full_filename.MaximumLength, ALLOC_TAG);
1800 if (!fcb->full_filename.Buffer) {
1801 ERR("out of memory\n");
1802
1803 Status = STATUS_INSUFFICIENT_RESOURCES;
1804 goto end;
1805 }
1806
1807 RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
1808 fcb->full_filename.Length = fcb->par->full_filename.Length;
1809
1810 if (fcb->par->par) {
1811 fcb->full_filename.Buffer[fcb->full_filename.Length / sizeof(WCHAR)] = '\\';
1812 fcb->full_filename.Length += sizeof(WCHAR);
1813 }
1814 fcb->name_offset = fcb->full_filename.Length / sizeof(WCHAR);
1815
1816 RtlAppendUnicodeStringToString(&fcb->full_filename, &fcb->filepart);
1817
1818 FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
1819 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
1820 across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME, NULL);
1821
1822 Status = STATUS_SUCCESS;
1823
1824 end:
1825 if (utf8.Buffer)
1826 ExFreePool(utf8.Buffer);
1827
1828 if (oldfcb)
1829 free_fcb(oldfcb);
1830
1831 return Status;
1832 }
1833
1834 static NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) {
1835 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
1836 fcb* fcb = FileObject->FsContext;
1837 LARGE_INTEGER time;
1838 BTRFS_TIME now;
1839 KEY searchkey;
1840 traverse_ptr tp;
1841 INODE_ITEM* ii;
1842 CC_FILE_SIZES ccfs;
1843 UINT8* data = NULL;
1844 UINT16 datalen;
1845 NTSTATUS Status;
1846
1847 TRACE("setting new end to %llx bytes (currently %x)\n", feofi->EndOfFile.QuadPart, fcb->adssize);
1848
1849 if (feofi->EndOfFile.QuadPart < fcb->adssize) {
1850 if (advance_only)
1851 return STATUS_SUCCESS;
1852
1853 TRACE("truncating stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
1854
1855 if (feofi->EndOfFile.QuadPart > 0) {
1856 if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
1857 ERR("get_xattr failed\n");
1858 return STATUS_INTERNAL_ERROR;
1859 }
1860 }
1861
1862 Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data, feofi->EndOfFile.QuadPart, rollback);
1863 if (!NT_SUCCESS(Status)) {
1864 ERR("set_xattr returned %08x\n", Status);
1865 return Status;
1866 }
1867
1868 fcb->adssize = feofi->EndOfFile.QuadPart;
1869
1870 if (data)
1871 ExFreePool(data);
1872 } else if (feofi->EndOfFile.QuadPart > fcb->adssize) {
1873 UINT16 maxlen;
1874 UINT8* data2;
1875
1876 TRACE("extending stream to %llx bytes\n", feofi->EndOfFile.QuadPart);
1877
1878 // find maximum length of xattr
1879 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
1880
1881 searchkey.obj_id = fcb->inode;
1882 searchkey.obj_type = TYPE_XATTR_ITEM;
1883 searchkey.offset = fcb->adshash;
1884
1885 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1886 if (!NT_SUCCESS(Status)) {
1887 ERR("error - find_item returned %08x\n", Status);
1888 return Status;
1889 }
1890
1891 if (keycmp(&tp.item->key, &searchkey)) {
1892 ERR("error - could not find key for xattr\n");
1893 free_traverse_ptr(&tp);
1894 return STATUS_INTERNAL_ERROR;
1895 }
1896
1897 if (tp.item->size < datalen) {
1898 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, datalen);
1899 free_traverse_ptr(&tp);
1900 return STATUS_INTERNAL_ERROR;
1901 }
1902
1903 maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
1904
1905 free_traverse_ptr(&tp);
1906
1907 if (feofi->EndOfFile.QuadPart > maxlen) {
1908 ERR("error - xattr too long (%llu > %u)\n", feofi->EndOfFile.QuadPart, maxlen);
1909 return STATUS_DISK_FULL;
1910 }
1911
1912 if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
1913 ERR("get_xattr failed\n");
1914 return STATUS_INTERNAL_ERROR;
1915 }
1916
1917 data2 = ExAllocatePoolWithTag(PagedPool, feofi->EndOfFile.QuadPart, ALLOC_TAG);
1918 if (!data2) {
1919 ERR("out of memory\n");
1920 ExFreePool(data);
1921 return STATUS_INSUFFICIENT_RESOURCES;
1922 }
1923
1924 RtlCopyMemory(data2, data, datalen);
1925 ExFreePool(data);
1926
1927 RtlZeroMemory(&data2[datalen], feofi->EndOfFile.QuadPart - datalen);
1928
1929 Status = set_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, data2, feofi->EndOfFile.QuadPart, rollback);
1930 if (!NT_SUCCESS(Status)) {
1931 ERR("set_xattr returned %08x\n", Status);
1932 return Status;
1933 }
1934
1935 fcb->adssize = feofi->EndOfFile.QuadPart;
1936
1937 ExFreePool(data2);
1938 }
1939
1940 ccfs.AllocationSize = fcb->Header.AllocationSize;
1941 ccfs.FileSize = fcb->Header.FileSize;
1942 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
1943
1944 CcSetFileSizes(FileObject, &ccfs);
1945
1946 KeQuerySystemTime(&time);
1947 win_time_to_unix(time, &now);
1948
1949 fcb->par->inode_item.transid = Vcb->superblock.generation;
1950 fcb->par->inode_item.sequence++;
1951 fcb->par->inode_item.st_ctime = now;
1952
1953 searchkey.obj_id = fcb->inode;
1954 searchkey.obj_type = TYPE_INODE_ITEM;
1955 searchkey.offset = 0;
1956
1957 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
1958 if (!NT_SUCCESS(Status)) {
1959 ERR("error - find_item returned %08x\n", Status);
1960 return Status;
1961 }
1962
1963 if (!keycmp(&tp.item->key, &searchkey))
1964 delete_tree_item(Vcb, &tp, rollback);
1965 else
1966 WARN("couldn't find existing INODE_ITEM\n");
1967
1968 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
1969 if (!ii) {
1970 ERR("out of memory\n");
1971 free_traverse_ptr(&tp);
1972 return STATUS_INSUFFICIENT_RESOURCES;
1973 }
1974
1975 RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
1976 insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
1977
1978 free_traverse_ptr(&tp);
1979
1980 fcb->par->subvol->root_item.ctransid = Vcb->superblock.generation;
1981 fcb->par->subvol->root_item.ctime = now;
1982
1983 return STATUS_SUCCESS;
1984 }
1985
1986 static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback) {
1987 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
1988 fcb* fcb = FileObject->FsContext;
1989 NTSTATUS Status;
1990 LARGE_INTEGER time;
1991 KEY searchkey;
1992 traverse_ptr tp;
1993 INODE_ITEM* ii;
1994 CC_FILE_SIZES ccfs;
1995
1996 if (fcb->deleted)
1997 return STATUS_FILE_CLOSED;
1998
1999 if (fcb->ads)
2000 return stream_set_end_of_file_information(Vcb, Irp, FileObject, advance_only, rollback);
2001
2002 TRACE("filename %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
2003 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
2004 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2005 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2006
2007 // int3;
2008 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
2009
2010 // if (feofi->EndOfFile.QuadPart==0x36c000)
2011 // int3;
2012
2013 if (feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
2014 if (advance_only)
2015 return STATUS_SUCCESS;
2016
2017 TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2018
2019 Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, rollback);
2020 if (!NT_SUCCESS(Status)) {
2021 ERR("error - truncate_file failed\n");
2022 return Status;
2023 }
2024 } else if (feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
2025 if (Irp->Flags & IRP_PAGING_IO) {
2026 TRACE("paging IO tried to extend file size\n");
2027 return STATUS_SUCCESS;
2028 }
2029
2030 TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2031
2032 // FIXME - pass flag to say that new extents should be prealloc rather than sparse
2033 Status = extend_file(fcb, feofi->EndOfFile.QuadPart, rollback);
2034 if (!NT_SUCCESS(Status)) {
2035 ERR("error - extend_file failed\n");
2036 return Status;
2037 }
2038 }
2039
2040 ccfs.AllocationSize = fcb->Header.AllocationSize;
2041 ccfs.FileSize = fcb->Header.FileSize;
2042 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2043
2044 CcSetFileSizes(FileObject, &ccfs);
2045 TRACE("setting FileSize for %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, ccfs.FileSize);
2046
2047 KeQuerySystemTime(&time);
2048
2049 win_time_to_unix(time, &fcb->inode_item.st_mtime);
2050
2051 searchkey.obj_id = fcb->inode;
2052 searchkey.obj_type = TYPE_INODE_ITEM;
2053 searchkey.offset = 0;
2054
2055 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
2056 if (!NT_SUCCESS(Status)) {
2057 ERR("error - find_item returned %08x\n", Status);
2058 return Status;
2059 }
2060
2061 if (!keycmp(&tp.item->key, &searchkey))
2062 delete_tree_item(Vcb, &tp, rollback);
2063 else
2064 WARN("couldn't find existing INODE_ITEM\n");
2065
2066 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
2067 if (!ii) {
2068 ERR("out of memory\n");
2069 return STATUS_INSUFFICIENT_RESOURCES;
2070 }
2071
2072 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
2073 insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
2074
2075 free_traverse_ptr(&tp);
2076
2077 return STATUS_SUCCESS;
2078 }
2079
2080 // static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2081 // FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2082 // fcb* fcb = FileObject->FsContext;
2083 //
2084 // FIXME("FIXME\n");
2085 // ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
2086 // ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
2087 //
2088 // return STATUS_NOT_IMPLEMENTED;
2089 // }
2090
2091 static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2092 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2093 #ifdef DEBUG_LONG_MESSAGES
2094 fcb* fcb = FileObject->FsContext;
2095
2096 TRACE("setting the position on %.*S to %llx\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fpi->CurrentByteOffset.QuadPart);
2097 #endif
2098
2099 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2100
2101 FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
2102
2103 return STATUS_SUCCESS;
2104 }
2105
2106 NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2107 NTSTATUS Status;
2108 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2109 device_extension* Vcb = DeviceObject->DeviceExtension;
2110 fcb* fcb = IrpSp->FileObject->FsContext;
2111 BOOL top_level;
2112 LIST_ENTRY rollback;
2113
2114 InitializeListHead(&rollback);
2115
2116 FsRtlEnterFileSystem();
2117
2118 top_level = is_top_level(Irp);
2119
2120 if (Vcb->readonly) {
2121 Status = STATUS_MEDIA_WRITE_PROTECTED;
2122 goto end;
2123 }
2124
2125 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
2126 Status = STATUS_ACCESS_DENIED;
2127 goto end;
2128 }
2129
2130 Irp->IoStatus.Information = 0;
2131
2132 Status = STATUS_NOT_IMPLEMENTED;
2133
2134 TRACE("set information\n");
2135
2136 acquire_tree_lock(Vcb, TRUE);
2137
2138 switch (IrpSp->Parameters.SetFile.FileInformationClass) {
2139 case FileAllocationInformation:
2140 TRACE("FileAllocationInformation\n");
2141 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, &rollback);
2142 break;
2143
2144 case FileBasicInformation:
2145 TRACE("FileBasicInformation\n");
2146 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject, &rollback);
2147 break;
2148
2149 case FileDispositionInformation:
2150 TRACE("FileDispositionInformation\n");
2151 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
2152 break;
2153
2154 case FileEndOfFileInformation:
2155 TRACE("FileEndOfFileInformation\n");
2156 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, &rollback);
2157 break;
2158
2159 case FileLinkInformation:
2160 FIXME("STUB: FileLinkInformation\n");
2161 break;
2162
2163 case FilePositionInformation:
2164 TRACE("FilePositionInformation\n");
2165 Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
2166 break;
2167
2168 case FileRenameInformation:
2169 TRACE("FileRenameInformation\n");
2170 // FIXME - make this work with streams
2171 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists, &rollback);
2172 break;
2173
2174 case FileValidDataLengthInformation:
2175 FIXME("STUB: FileValidDataLengthInformation\n");
2176 break;
2177
2178 #if (NTDDI_VERSION >= NTDDI_VISTA)
2179 case FileNormalizedNameInformation:
2180 FIXME("STUB: FileNormalizedNameInformation\n");
2181 break;
2182 #endif
2183
2184
2185 #if (NTDDI_VERSION >= NTDDI_WIN7)
2186 case FileStandardLinkInformation:
2187 FIXME("STUB: FileStandardLinkInformation\n");
2188 break;
2189
2190 case FileRemoteProtocolInformation:
2191 FIXME("STUB: FileRemoteProtocolInformation\n");
2192 break;
2193 #endif
2194
2195 default:
2196 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
2197 }
2198
2199 if (NT_SUCCESS(Status))
2200 Status = consider_write(Vcb);
2201
2202 if (NT_SUCCESS(Status))
2203 clear_rollback(&rollback);
2204 else
2205 do_rollback(Vcb, &rollback);
2206
2207 release_tree_lock(Vcb, TRUE);
2208
2209 end:
2210 Irp->IoStatus.Status = Status;
2211
2212 IoCompleteRequest( Irp, IO_NO_INCREMENT );
2213
2214 if (top_level)
2215 IoSetTopLevelIrp(NULL);
2216
2217 FsRtlExitFileSystem();
2218
2219 return Status;
2220 }
2221
2222 static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb) {
2223 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
2224
2225 *length -= sizeof(FILE_BASIC_INFORMATION);
2226
2227 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2228 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2229 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2230 fbi->ChangeTime.QuadPart = 0;
2231 fbi->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
2232
2233 return STATUS_SUCCESS;
2234 }
2235
2236 static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, LONG* length) {
2237 INODE_ITEM* ii;
2238
2239 if (*length < sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
2240 WARN("overflow\n");
2241 return STATUS_BUFFER_OVERFLOW;
2242 }
2243
2244 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
2245
2246 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
2247
2248 if (fcb->ads)
2249 ii = &fcb->par->inode_item;
2250 else
2251 ii = &fcb->inode_item;
2252
2253
2254 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2255 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2256 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2257 fnoi->ChangeTime.QuadPart = 0;
2258
2259 if (fcb->ads) {
2260 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adssize;
2261 fnoi->FileAttributes = fcb->par->atts;
2262 } else {
2263 fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
2264 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2265 fnoi->FileAttributes = fcb->atts;
2266 }
2267
2268 return STATUS_SUCCESS;
2269 }
2270
2271 static NTSTATUS STDCALL fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, LONG* length) {
2272 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
2273
2274 *length -= sizeof(FILE_STANDARD_INFORMATION);
2275
2276 if (fcb->ads) {
2277 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adssize;
2278 fsi->NumberOfLinks = fcb->par->inode_item.st_nlink;
2279 fsi->Directory = S_ISDIR(fcb->par->inode_item.st_mode);
2280 } else {
2281 fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
2282 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2283 fsi->NumberOfLinks = fcb->inode_item.st_nlink;
2284 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
2285 }
2286
2287 TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
2288
2289 fsi->DeletePending = fcb->delete_on_close;
2290
2291 return STATUS_SUCCESS;
2292 }
2293
2294 static NTSTATUS STDCALL fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, UINT64 inode, LONG* length) {
2295 *length -= sizeof(FILE_INTERNAL_INFORMATION);
2296
2297 fii->IndexNumber.QuadPart = inode;
2298
2299 return STATUS_SUCCESS;
2300 }
2301
2302 static NTSTATUS STDCALL fill_in_file_ea_information(FILE_EA_INFORMATION* eai, LONG* length) {
2303 *length -= sizeof(FILE_EA_INFORMATION);
2304
2305 eai->EaSize = 0;
2306
2307 return STATUS_SUCCESS;
2308 }
2309
2310 static NTSTATUS STDCALL fill_in_file_access_information(FILE_ACCESS_INFORMATION* fai, LONG* length) {
2311 *length -= sizeof(FILE_ACCESS_INFORMATION);
2312
2313 fai->AccessFlags = GENERIC_READ;
2314
2315 return STATUS_NOT_IMPLEMENTED;
2316 }
2317
2318 static NTSTATUS STDCALL fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
2319 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
2320
2321 *length -= sizeof(FILE_POSITION_INFORMATION);
2322
2323 fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
2324
2325 return STATUS_SUCCESS;
2326 }
2327
2328 static NTSTATUS STDCALL fill_in_file_mode_information(FILE_MODE_INFORMATION* fmi, ccb* ccb, LONG* length) {
2329 RtlZeroMemory(fmi, sizeof(FILE_MODE_INFORMATION));
2330
2331 *length -= sizeof(FILE_MODE_INFORMATION);
2332
2333 if (ccb->options & FILE_WRITE_THROUGH)
2334 fmi->Mode |= FILE_WRITE_THROUGH;
2335
2336 if (ccb->options & FILE_SEQUENTIAL_ONLY)
2337 fmi->Mode |= FILE_SEQUENTIAL_ONLY;
2338
2339 if (ccb->options & FILE_NO_INTERMEDIATE_BUFFERING)
2340 fmi->Mode |= FILE_NO_INTERMEDIATE_BUFFERING;
2341
2342 if (ccb->options & FILE_SYNCHRONOUS_IO_ALERT)
2343 fmi->Mode |= FILE_SYNCHRONOUS_IO_ALERT;
2344
2345 if (ccb->options & FILE_SYNCHRONOUS_IO_NONALERT)
2346 fmi->Mode |= FILE_SYNCHRONOUS_IO_NONALERT;
2347
2348 if (ccb->options & FILE_DELETE_ON_CLOSE)
2349 fmi->Mode |= FILE_DELETE_ON_CLOSE;
2350
2351 return STATUS_SUCCESS;
2352 }
2353
2354 static NTSTATUS STDCALL fill_in_file_alignment_information(FILE_ALIGNMENT_INFORMATION* fai, device_extension* Vcb, LONG* length) {
2355 RtlZeroMemory(fai, sizeof(FILE_ALIGNMENT_INFORMATION));
2356
2357 *length -= sizeof(FILE_ALIGNMENT_INFORMATION);
2358
2359 fai->AlignmentRequirement = Vcb->devices[0].devobj->AlignmentRequirement;
2360
2361 return STATUS_SUCCESS;
2362 }
2363
2364 static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, LONG* length) {
2365 #ifdef _DEBUG
2366 ULONG retlen = 0;
2367 #endif
2368 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
2369 ULONG datasuflen = wcslen(datasuf) * sizeof(WCHAR);
2370
2371 RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION));
2372
2373 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
2374
2375 TRACE("maximum length is %u\n", *length);
2376 fni->FileNameLength = 0;
2377
2378 fni->FileName[0] = 0;
2379
2380 if (*length >= (LONG)fcb->full_filename.Length) {
2381 RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, fcb->full_filename.Length);
2382 #ifdef _DEBUG
2383 retlen = fcb->full_filename.Length;
2384 #endif
2385 *length -= fcb->full_filename.Length;
2386 } else {
2387 if (*length > 0) {
2388 RtlCopyMemory(fni->FileName, fcb->full_filename.Buffer, *length);
2389 #ifdef _DEBUG
2390 retlen = *length;
2391 #endif
2392 }
2393 *length = -1;
2394 }
2395
2396 fni->FileNameLength = fcb->full_filename.Length;
2397
2398 if (fcb->ads) {
2399 if (*length >= (LONG)datasuflen) {
2400 RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, datasuflen);
2401 #ifdef _DEBUG
2402 retlen += datasuflen;
2403 #endif
2404 *length -= datasuflen;
2405 } else {
2406 if (*length > 0) {
2407 RtlCopyMemory(&fni->FileName[fcb->full_filename.Length / sizeof(WCHAR)], datasuf, *length);
2408 #ifdef _DEBUG
2409 retlen += *length;
2410 #endif
2411 }
2412 *length = -1;
2413 }
2414 }
2415
2416 TRACE("%.*S\n", retlen / sizeof(WCHAR), fni->FileName);
2417
2418 return STATUS_SUCCESS;
2419 }
2420
2421 static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, LONG* length) {
2422 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
2423
2424 ati->FileAttributes = fcb->ads ? fcb->par->atts : fcb->atts;
2425 ati->ReparseTag = 0;
2426
2427 return STATUS_SUCCESS;
2428 }
2429
2430 typedef struct {
2431 UNICODE_STRING name;
2432 UINT64 size;
2433 } stream_info;
2434
2435 static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fcb* fcb, LONG* length) {
2436 ULONG reqsize;
2437 UINT64 i, num_streams;
2438 stream_info* streams;
2439 FILE_STREAM_INFORMATION* entry;
2440 NTSTATUS Status;
2441 KEY searchkey;
2442 traverse_ptr tp, next_tp;
2443 BOOL b;
2444
2445 static WCHAR datasuf[] = {':','$','D','A','T','A',0};
2446 static char xapref[] = "user.";
2447 UNICODE_STRING suf;
2448
2449 suf.Buffer = datasuf;
2450 suf.Length = suf.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
2451
2452 num_streams = 1;
2453
2454 searchkey.obj_id = fcb->inode;
2455 searchkey.obj_type = TYPE_XATTR_ITEM;
2456 searchkey.offset = 0;
2457
2458 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
2459 if (!NT_SUCCESS(Status)) {
2460 ERR("error - find_item returned %08x\n", Status);
2461 return Status;
2462 }
2463
2464 do {
2465 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
2466 if (tp.item->size < sizeof(DIR_ITEM)) {
2467 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
2468 } else {
2469 ULONG len = tp.item->size;
2470 DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
2471
2472 do {
2473 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
2474 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2475 break;
2476 }
2477
2478 if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref)) {
2479 if (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n) {
2480 num_streams++;
2481 }
2482 }
2483
2484 len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
2485 xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
2486 } while (len > 0);
2487 }
2488 }
2489
2490 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
2491 if (b) {
2492 free_traverse_ptr(&tp);
2493 tp = next_tp;
2494
2495 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
2496 break;
2497 }
2498 } while (b);
2499
2500 free_traverse_ptr(&tp);
2501
2502 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
2503 if (!NT_SUCCESS(Status)) {
2504 ERR("error - find_item returned %08x\n", Status);
2505 return Status;
2506 }
2507
2508 streams = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info) * num_streams, ALLOC_TAG);
2509 if (!streams) {
2510 ERR("out of memory\n");
2511 return STATUS_INSUFFICIENT_RESOURCES;
2512 }
2513
2514 reqsize = 0;
2515
2516 streams[0].name.Length = streams[0].name.MaximumLength = 0;
2517 streams[0].name.Buffer = NULL;
2518 streams[0].size = fcb->inode_item.st_size;
2519 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[0].name.Length;
2520
2521 i = 1;
2522 do {
2523 if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
2524 if (tp.item->size < sizeof(DIR_ITEM)) {
2525 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
2526 } else {
2527 ULONG len = tp.item->size;
2528 DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
2529 ULONG stringlen;
2530
2531 do {
2532 if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
2533 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2534 break;
2535 }
2536
2537 if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref) &&
2538 (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n)) {
2539 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
2540 if (!NT_SUCCESS(Status)) {
2541 UINT64 j;
2542
2543 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
2544
2545 for (j = i; j < num_streams; j++)
2546 streams[j].name.Buffer = NULL;
2547
2548 goto end;
2549 }
2550
2551 streams[i].name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
2552 if (!streams[i].name.Buffer) {
2553 UINT64 j;
2554
2555 ERR("out of memory\n");
2556
2557 for (j = i+1; j < num_streams; j++)
2558 streams[j].name.Buffer = NULL;
2559
2560 Status = STATUS_INSUFFICIENT_RESOURCES;
2561 goto end;
2562 }
2563
2564 Status = RtlUTF8ToUnicodeN(streams[i].name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
2565
2566 if (!NT_SUCCESS(Status)) {
2567 UINT64 j;
2568
2569 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
2570
2571 ExFreePool(streams[i].name.Buffer);
2572 for (j = i; j < num_streams; j++)
2573 streams[j].name.Buffer = NULL;
2574
2575 goto end;
2576 }
2577
2578 streams[i].name.Length = streams[i].name.MaximumLength = stringlen;
2579
2580 streams[i].size = xa->m;
2581 reqsize = sector_align(reqsize, sizeof(LONGLONG));
2582 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length;
2583
2584 TRACE("streams[%llu].name = %.*S (length = %u)\n", i, streams[i].name.Length / sizeof(WCHAR), streams[i].name.Buffer, streams[i].name.Length / sizeof(WCHAR));
2585
2586 i++;
2587 }
2588
2589 len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
2590 xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
2591 } while (len > 0);
2592 }
2593 }
2594
2595 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
2596 if (b) {
2597 free_traverse_ptr(&tp);
2598 tp = next_tp;
2599
2600 if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
2601 break;
2602 }
2603 } while (b);
2604
2605 free_traverse_ptr(&tp);
2606
2607 TRACE("length = %i, reqsize = %u\n", *length, reqsize);
2608
2609 if (reqsize > *length) {
2610 Status = STATUS_BUFFER_OVERFLOW;
2611 goto end;
2612 }
2613
2614 entry = fsi;
2615 for (i = 0; i < num_streams; i++) {
2616 entry->StreamNameLength = streams[i].name.Length + suf.Length + sizeof(WCHAR);
2617 entry->StreamSize.QuadPart = streams[i].size;
2618
2619 if (i == 0)
2620 entry->StreamAllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
2621 else
2622 entry->StreamAllocationSize.QuadPart = streams[i].size;
2623
2624 entry->StreamName[0] = ':';
2625
2626 if (streams[i].name.Length > 0)
2627 RtlCopyMemory(&entry->StreamName[1], streams[i].name.Buffer, streams[i].name.Length);
2628
2629 RtlCopyMemory(&entry->StreamName[1 + (streams[i].name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
2630
2631 if (i == num_streams - 1)
2632 entry->NextEntryOffset = 0;
2633 else {
2634 entry->NextEntryOffset = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + streams[i].name.Length, sizeof(LONGLONG));
2635
2636 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + entry->NextEntryOffset);
2637 }
2638 }
2639
2640 *length -= reqsize;
2641
2642 Status = STATUS_SUCCESS;
2643
2644 end:
2645 for (i = 0; i < num_streams; i++) {
2646 if (streams[i].name.Buffer)
2647 ExFreePool(streams[i].name.Buffer);
2648 }
2649
2650 ExFreePool(streams);
2651
2652 return Status;
2653 }
2654
2655 static NTSTATUS STDCALL fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, LONG* length) {
2656 TRACE("FileStandardLinkInformation\n");
2657
2658 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
2659
2660 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
2661 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
2662 fsli->DeletePending = fcb->delete_on_close;
2663 fsli->Directory = fcb->type == BTRFS_TYPE_DIRECTORY ? TRUE : FALSE;
2664
2665 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
2666
2667 return STATUS_SUCCESS;
2668 }
2669
2670 static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
2671 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2672 LONG length = IrpSp->Parameters.QueryFile.Length;
2673 fcb* fcb = FileObject->FsContext;
2674 ccb* ccb = FileObject->FsContext2;
2675 NTSTATUS Status;
2676
2677 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
2678 TRACE("fcb = %p\n", fcb);
2679
2680 if (fcb == Vcb->volume_fcb)
2681 return STATUS_INVALID_PARAMETER;
2682
2683 switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
2684 case FileAllInformation:
2685 {
2686 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
2687 INODE_ITEM* ii;
2688
2689 TRACE("FileAllInformation\n");
2690
2691 if (fcb->ads)
2692 ii = &fcb->par->inode_item;
2693 else
2694 ii = &fcb->inode_item;
2695
2696 if (length > 0)
2697 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb);
2698
2699 if (length > 0)
2700 fill_in_file_standard_information(&fai->StandardInformation, fcb, &length);
2701
2702 if (length > 0)
2703 fill_in_file_internal_information(&fai->InternalInformation, fcb->inode, &length);
2704
2705 if (length > 0)
2706 fill_in_file_ea_information(&fai->EaInformation, &length);
2707
2708 if (length > 0)
2709 fill_in_file_access_information(&fai->AccessInformation, &length);
2710
2711 if (length > 0)
2712 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
2713
2714 if (length > 0)
2715 fill_in_file_mode_information(&fai->ModeInformation, ccb, &length);
2716
2717 if (length > 0)
2718 fill_in_file_alignment_information(&fai->AlignmentInformation, Vcb, &length);
2719
2720 if (length > 0)
2721 fill_in_file_name_information(&fai->NameInformation, fcb, &length);
2722
2723 Status = STATUS_SUCCESS;
2724
2725 break;
2726 }
2727
2728 case FileAttributeTagInformation:
2729 {
2730 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
2731
2732 TRACE("FileAttributeTagInformation\n");
2733
2734 Status = fill_in_file_attribute_information(ati, fcb, &length);
2735
2736 break;
2737 }
2738
2739 case FileBasicInformation:
2740 {
2741 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
2742 INODE_ITEM* ii;
2743
2744 TRACE("FileBasicInformation\n");
2745
2746 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
2747 WARN("overflow\n");
2748 Status = STATUS_BUFFER_OVERFLOW;
2749 goto exit;
2750 }
2751
2752 if (fcb->ads)
2753 ii = &fcb->par->inode_item;
2754 else
2755 ii = &fcb->inode_item;
2756
2757 Status = fill_in_file_basic_information(fbi, ii, &length, fcb);
2758 break;
2759 }
2760
2761 case FileCompressionInformation:
2762 FIXME("STUB: FileCompressionInformation\n");
2763 Status = STATUS_INVALID_PARAMETER;
2764 goto exit;
2765
2766 case FileEaInformation:
2767 {
2768 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
2769
2770 TRACE("FileEaInformation\n");
2771
2772 Status = fill_in_file_ea_information(eai, &length);
2773
2774 break;
2775 }
2776
2777 case FileInternalInformation:
2778 {
2779 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
2780
2781 TRACE("FileInternalInformation\n");
2782
2783 Status = fill_in_file_internal_information(fii, fcb->inode, &length);
2784
2785 break;
2786 }
2787
2788 case FileNameInformation:
2789 {
2790 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
2791
2792 TRACE("FileNameInformation\n");
2793
2794 Status = fill_in_file_name_information(fni, fcb, &length);
2795
2796 break;
2797 }
2798
2799 case FileNetworkOpenInformation:
2800 {
2801 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
2802
2803 TRACE("FileNetworkOpenInformation\n");
2804
2805 Status = fill_in_file_network_open_information(fnoi, fcb, &length);
2806
2807 break;
2808 }
2809
2810 case FilePositionInformation:
2811 {
2812 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
2813
2814 TRACE("FilePositionInformation\n");
2815
2816 Status = fill_in_file_position_information(fpi, FileObject, &length);
2817
2818 break;
2819 }
2820
2821 case FileStandardInformation:
2822 {
2823 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
2824
2825 TRACE("FileStandardInformation\n");
2826
2827 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
2828 WARN("overflow\n");
2829 Status = STATUS_BUFFER_OVERFLOW;
2830 goto exit;
2831 }
2832
2833 Status = fill_in_file_standard_information(fsi, fcb, &length);
2834
2835 break;
2836 }
2837
2838 case FileStreamInformation:
2839 {
2840 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
2841
2842 TRACE("FileStreamInformation\n");
2843
2844 Status = fill_in_file_stream_information(fsi, fcb, &length);
2845
2846 break;
2847 }
2848
2849 #if (NTDDI_VERSION >= NTDDI_VISTA)
2850 case FileHardLinkInformation:
2851 FIXME("STUB: FileHardLinkInformation\n");
2852 Status = STATUS_INVALID_PARAMETER;
2853 goto exit;
2854
2855 case FileNormalizedNameInformation:
2856 {
2857 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
2858
2859 TRACE("FileNormalizedNameInformation\n");
2860
2861 Status = fill_in_file_name_information(fni, fcb, &length);
2862
2863 break;
2864 }
2865 #endif
2866
2867
2868 #if (NTDDI_VERSION >= NTDDI_WIN7)
2869 case FileStandardLinkInformation:
2870 {
2871 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
2872
2873 TRACE("FileStandardLinkInformation\n");
2874
2875 Status = fill_in_file_standard_link_information(fsli, fcb, &length);
2876
2877 break;
2878 }
2879
2880 case FileRemoteProtocolInformation:
2881 FIXME("STUB: FileRemoteProtocolInformation\n");
2882 Status = STATUS_INVALID_PARAMETER;
2883 goto exit;
2884 #endif
2885
2886 default:
2887 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
2888 Status = STATUS_INVALID_PARAMETER;
2889 goto exit;
2890 }
2891
2892 if (length < 0) {
2893 length = 0;
2894 Status = STATUS_BUFFER_OVERFLOW;
2895 }
2896
2897 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
2898
2899 exit:
2900 TRACE("query_info returning %08x\n", Status);
2901
2902 return Status;
2903 }
2904
2905 NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2906 PIO_STACK_LOCATION IrpSp;
2907 NTSTATUS Status;
2908 fcb* fcb;
2909 device_extension* Vcb = DeviceObject->DeviceExtension;
2910 BOOL top_level;
2911
2912 FsRtlEnterFileSystem();
2913
2914 top_level = is_top_level(Irp);
2915
2916 Irp->IoStatus.Information = 0;
2917
2918 TRACE("query information\n");
2919
2920 IrpSp = IoGetCurrentIrpStackLocation(Irp);
2921
2922 acquire_tree_lock(Vcb, FALSE);
2923
2924 fcb = IrpSp->FileObject->FsContext;
2925 TRACE("fcb = %p\n", fcb);
2926 TRACE("fcb->subvol = %p\n", fcb->subvol);
2927
2928 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
2929
2930 TRACE("returning %08x\n", Status);
2931
2932 Irp->IoStatus.Status = Status;
2933
2934 IoCompleteRequest( Irp, IO_NO_INCREMENT );
2935
2936 release_tree_lock(Vcb, FALSE);
2937
2938 if (top_level)
2939 IoSetTopLevelIrp(NULL);
2940
2941 FsRtlExitFileSystem();
2942
2943 return Status;
2944 }