[BTRFS] Upgrade to 1.2.1
[reactos.git] / drivers / filesystems / btrfs / fileinfo.c
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19
20 #if (NTDDI_VERSION >= NTDDI_WIN10)
21 // not currently in mingw - introduced with Windows 10
22 #ifndef _MSC_VER
23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
24 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
25
26 typedef struct _FILE_STAT_LX_INFORMATION {
27 LARGE_INTEGER FileId;
28 LARGE_INTEGER CreationTime;
29 LARGE_INTEGER LastAccessTime;
30 LARGE_INTEGER LastWriteTime;
31 LARGE_INTEGER ChangeTime;
32 LARGE_INTEGER AllocationSize;
33 LARGE_INTEGER EndOfFile;
34 ULONG FileAttributes;
35 ULONG ReparseTag;
36 ULONG NumberOfLinks;
37 ACCESS_MASK EffectiveAccess;
38 ULONG LxFlags;
39 ULONG LxUid;
40 ULONG LxGid;
41 ULONG LxMode;
42 ULONG LxDeviceIdMajor;
43 ULONG LxDeviceIdMinor;
44 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
45
46 #define LX_FILE_METADATA_HAS_UID 0x01
47 #define LX_FILE_METADATA_HAS_GID 0x02
48 #define LX_FILE_METADATA_HAS_MODE 0x04
49 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08
50 #define LX_FILE_CASE_SENSITIVE_DIR 0x10
51
52 #endif
53 #endif
54
55 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
56 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
57 fcb* fcb = FileObject->FsContext;
58 ccb* ccb = FileObject->FsContext2;
59 file_ref* fileref = ccb ? ccb->fileref : NULL;
60 ULONG defda, filter = 0;
61 BOOL inode_item_changed = FALSE;
62 NTSTATUS Status;
63
64 if (fcb->ads) {
65 if (fileref && fileref->parent)
66 fcb = fileref->parent->fcb;
67 else {
68 ERR("stream did not have fileref\n");
69 return STATUS_INTERNAL_ERROR;
70 }
71 }
72
73 if (!ccb) {
74 ERR("ccb was NULL\n");
75 return STATUS_INVALID_PARAMETER;
76 }
77
78 TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes);
79
80 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
81
82 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
83 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
84 Status = STATUS_INVALID_PARAMETER;
85 goto end;
86 }
87
88 if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) &&
89 (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
90 Status = STATUS_ACCESS_DENIED;
91 goto end;
92 }
93
94 // don't allow readonly subvol to be made r/w if send operation running on it
95 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY &&
96 fcb->subvol->send_ops > 0) {
97 Status = STATUS_DEVICE_NOT_READY;
98 goto end;
99 }
100
101 // times of -2 are some sort of undocumented behaviour to do with LXSS
102
103 if (fbi->CreationTime.QuadPart == -2)
104 fbi->CreationTime.QuadPart = 0;
105
106 if (fbi->LastAccessTime.QuadPart == -2)
107 fbi->LastAccessTime.QuadPart = 0;
108
109 if (fbi->LastWriteTime.QuadPart == -2)
110 fbi->LastWriteTime.QuadPart = 0;
111
112 if (fbi->ChangeTime.QuadPart == -2)
113 fbi->ChangeTime.QuadPart = 0;
114
115 if (fbi->CreationTime.QuadPart == -1)
116 ccb->user_set_creation_time = TRUE;
117 else if (fbi->CreationTime.QuadPart != 0) {
118 win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
119 inode_item_changed = TRUE;
120 filter |= FILE_NOTIFY_CHANGE_CREATION;
121
122 ccb->user_set_creation_time = TRUE;
123 }
124
125 if (fbi->LastAccessTime.QuadPart == -1)
126 ccb->user_set_access_time = TRUE;
127 else if (fbi->LastAccessTime.QuadPart != 0) {
128 win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
129 inode_item_changed = TRUE;
130 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
131
132 ccb->user_set_access_time = TRUE;
133 }
134
135 if (fbi->LastWriteTime.QuadPart == -1)
136 ccb->user_set_write_time = TRUE;
137 else if (fbi->LastWriteTime.QuadPart != 0) {
138 win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
139 inode_item_changed = TRUE;
140 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
141
142 ccb->user_set_write_time = TRUE;
143 }
144
145 if (fbi->ChangeTime.QuadPart == -1)
146 ccb->user_set_change_time = TRUE;
147 else if (fbi->ChangeTime.QuadPart != 0) {
148 win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
149 inode_item_changed = TRUE;
150 // no filter for this
151
152 ccb->user_set_change_time = TRUE;
153 }
154
155 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
156 if (fbi->FileAttributes != 0) {
157 LARGE_INTEGER time;
158 BTRFS_TIME now;
159
160 fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
161
162 defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.',
163 TRUE, Irp);
164
165 if (fcb->type == BTRFS_TYPE_DIRECTORY)
166 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
167 else if (fcb->type == BTRFS_TYPE_SYMLINK)
168 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
169
170 fcb->atts_changed = TRUE;
171
172 if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)
173 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
174
175 if (defda == fbi->FileAttributes)
176 fcb->atts_deleted = TRUE;
177 else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY))
178 fcb->atts_deleted = TRUE;
179
180 fcb->atts = fbi->FileAttributes;
181
182 KeQuerySystemTime(&time);
183 win_time_to_unix(time, &now);
184
185 if (!ccb->user_set_change_time)
186 fcb->inode_item.st_ctime = now;
187
188 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
189 fcb->subvol->root_item.ctime = now;
190
191 if (fcb->inode == SUBVOL_ROOT_INODE) {
192 if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)
193 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY;
194 else
195 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY;
196 }
197
198 inode_item_changed = TRUE;
199
200 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
201 }
202
203 if (inode_item_changed) {
204 fcb->inode_item.transid = Vcb->superblock.generation;
205 fcb->inode_item.sequence++;
206 fcb->inode_item_changed = TRUE;
207
208 mark_fcb_dirty(fcb);
209 }
210
211 if (filter != 0)
212 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
213
214 Status = STATUS_SUCCESS;
215
216 end:
217 ExReleaseResourceLite(fcb->Header.Resource);
218
219 return Status;
220 }
221
222 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
223 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
224 fcb* fcb = FileObject->FsContext;
225 ccb* ccb = FileObject->FsContext2;
226 file_ref* fileref = ccb ? ccb->fileref : NULL;
227 ULONG atts;
228 NTSTATUS Status;
229
230 if (!fileref)
231 return STATUS_INVALID_PARAMETER;
232
233 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
234
235 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
236
237 if (fcb->ads) {
238 if (fileref->parent)
239 atts = fileref->parent->fcb->atts;
240 else {
241 ERR("no fileref for stream\n");
242 Status = STATUS_INTERNAL_ERROR;
243 goto end;
244 }
245 } else
246 atts = fcb->atts;
247
248 TRACE("atts = %x\n", atts);
249
250 if (atts & FILE_ATTRIBUTE_READONLY) {
251 TRACE("not allowing readonly file to be deleted\n");
252 Status = STATUS_CANNOT_DELETE;
253 goto end;
254 }
255
256 // FIXME - can we skip this bit for subvols?
257 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
258 TRACE("directory not empty\n");
259 Status = STATUS_DIRECTORY_NOT_EMPTY;
260 goto end;
261 }
262
263 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
264 TRACE("trying to delete file which is being mapped as an image\n");
265 Status = STATUS_CANNOT_DELETE;
266 goto end;
267 }
268
269 ccb->fileref->delete_on_close = fdi->DeleteFile;
270
271 FileObject->DeletePending = fdi->DeleteFile;
272
273 Status = STATUS_SUCCESS;
274
275 end:
276 ExReleaseResourceLite(fcb->Header.Resource);
277
278 // send notification that directory is about to be deleted
279 if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
280 FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
281 NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
282 }
283
284 return Status;
285 }
286
287 BOOL has_open_children(file_ref* fileref) {
288 LIST_ENTRY* le = fileref->children.Flink;
289
290 if (IsListEmpty(&fileref->children))
291 return FALSE;
292
293 while (le != &fileref->children) {
294 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
295
296 if (c->open_count > 0)
297 return TRUE;
298
299 if (has_open_children(c))
300 return TRUE;
301
302 le = le->Flink;
303 }
304
305 return FALSE;
306 }
307
308 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
309 device_extension* Vcb = oldfcb->Vcb;
310 fcb* fcb;
311 LIST_ENTRY* le;
312
313 // FIXME - we can skip a lot of this if the inode is about to be deleted
314
315 fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
316 if (!fcb) {
317 ERR("out of memory\n");
318 return STATUS_INSUFFICIENT_RESOURCES;
319 }
320
321 fcb->Vcb = Vcb;
322
323 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
324 fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
325 fcb->Header.FileSize = oldfcb->Header.FileSize;
326 fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength;
327
328 fcb->type = oldfcb->type;
329
330 if (oldfcb->ads) {
331 fcb->ads = TRUE;
332 fcb->adshash = oldfcb->adshash;
333 fcb->adsmaxlen = oldfcb->adsmaxlen;
334
335 if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
336 fcb->adsxattr.Length = oldfcb->adsxattr.Length;
337 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
338 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
339
340 if (!fcb->adsxattr.Buffer) {
341 ERR("out of memory\n");
342 free_fcb(fcb);
343 return STATUS_INSUFFICIENT_RESOURCES;
344 }
345
346 RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
347 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
348 }
349
350 if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
351 fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
352 fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
353
354 if (!fcb->adsdata.Buffer) {
355 ERR("out of memory\n");
356 free_fcb(fcb);
357 return STATUS_INSUFFICIENT_RESOURCES;
358 }
359
360 RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
361 }
362
363 goto end;
364 }
365
366 RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM));
367 fcb->inode_item_changed = TRUE;
368
369 if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) {
370 fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
371 if (!fcb->sd) {
372 ERR("out of memory\n");
373 free_fcb(fcb);
374 return STATUS_INSUFFICIENT_RESOURCES;
375 }
376
377 RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd));
378 }
379
380 fcb->atts = oldfcb->atts;
381
382 le = oldfcb->extents.Flink;
383 while (le != &oldfcb->extents) {
384 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
385
386 if (!ext->ignore) {
387 extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
388
389 if (!ext2) {
390 ERR("out of memory\n");
391 free_fcb(fcb);
392 return STATUS_INSUFFICIENT_RESOURCES;
393 }
394
395 ext2->offset = ext->offset;
396 ext2->datalen = ext->datalen;
397
398 if (ext2->datalen > 0)
399 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
400
401 ext2->unique = FALSE;
402 ext2->ignore = FALSE;
403 ext2->inserted = TRUE;
404
405 if (ext->csum) {
406 ULONG len;
407 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
408
409 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
410 len = (ULONG)ed2->num_bytes;
411 else
412 len = (ULONG)ed2->size;
413
414 len = len * sizeof(UINT32) / Vcb->superblock.sector_size;
415
416 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
417 if (!ext2->csum) {
418 ERR("out of memory\n");
419 free_fcb(fcb);
420 return STATUS_INSUFFICIENT_RESOURCES;
421 }
422
423 RtlCopyMemory(ext2->csum, ext->csum, len);
424 } else
425 ext2->csum = NULL;
426
427 InsertTailList(&fcb->extents, &ext2->list_entry);
428 }
429
430 le = le->Flink;
431 }
432
433 le = oldfcb->hardlinks.Flink;
434 while (le != &oldfcb->hardlinks) {
435 hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2;
436
437 hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
438
439 if (!hl2) {
440 ERR("out of memory\n");
441 free_fcb(fcb);
442 return STATUS_INSUFFICIENT_RESOURCES;
443 }
444
445 hl2->parent = hl->parent;
446 hl2->index = hl->index;
447
448 hl2->name.Length = hl2->name.MaximumLength = hl->name.Length;
449 hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG);
450
451 if (!hl2->name.Buffer) {
452 ERR("out of memory\n");
453 ExFreePool(hl2);
454 free_fcb(fcb);
455 return STATUS_INSUFFICIENT_RESOURCES;
456 }
457
458 RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length);
459
460 hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length;
461 hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG);
462
463 if (!hl2->utf8.Buffer) {
464 ERR("out of memory\n");
465 ExFreePool(hl2->name.Buffer);
466 ExFreePool(hl2);
467 free_fcb(fcb);
468 return STATUS_INSUFFICIENT_RESOURCES;
469 }
470
471 RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
472
473 InsertTailList(&fcb->hardlinks, &hl2->list_entry);
474
475 le = le->Flink;
476 }
477
478 if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
479 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
480
481 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
482 if (!fcb->reparse_xattr.Buffer) {
483 ERR("out of memory\n");
484 free_fcb(fcb);
485 return STATUS_INSUFFICIENT_RESOURCES;
486 }
487
488 RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
489 }
490
491 if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) {
492 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length;
493
494 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
495 if (!fcb->ea_xattr.Buffer) {
496 ERR("out of memory\n");
497 free_fcb(fcb);
498 return STATUS_INSUFFICIENT_RESOURCES;
499 }
500
501 RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
502 }
503
504 fcb->prop_compression = oldfcb->prop_compression;
505
506 le = oldfcb->xattrs.Flink;
507 while (le != &oldfcb->xattrs) {
508 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
509
510 if (xa->valuelen > 0) {
511 xattr* xa2;
512
513 xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG);
514
515 if (!xa2) {
516 ERR("out of memory\n");
517 free_fcb(fcb);
518 return STATUS_INSUFFICIENT_RESOURCES;
519 }
520
521 xa2->namelen = xa->namelen;
522 xa2->valuelen = xa->valuelen;
523 xa2->dirty = xa->dirty;
524 memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen);
525
526 InsertTailList(&fcb->xattrs, &xa2->list_entry);
527 }
528
529 le = le->Flink;
530 }
531
532 end:
533 *pfcb = fcb;
534
535 return STATUS_SUCCESS;
536 }
537
538 typedef struct _move_entry {
539 file_ref* fileref;
540 fcb* dummyfcb;
541 file_ref* dummyfileref;
542 struct _move_entry* parent;
543 LIST_ENTRY list_entry;
544 } move_entry;
545
546 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
547 NTSTATUS Status;
548 LIST_ENTRY* le;
549
550 ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, TRUE);
551
552 le = me->fileref->fcb->dir_children_index.Flink;
553
554 while (le != &me->fileref->fcb->dir_children_index) {
555 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
556 file_ref* fr;
557 move_entry* me2;
558
559 Status = open_fileref_child(Vcb, me->fileref, &dc->name, TRUE, TRUE, dc->index == 0 ? TRUE : FALSE, PagedPool, &fr, Irp);
560
561 if (!NT_SUCCESS(Status)) {
562 ERR("open_fileref_child returned %08x\n", Status);
563 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
564 return Status;
565 }
566
567 me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
568 if (!me2) {
569 ERR("out of memory\n");
570 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
571 return STATUS_INSUFFICIENT_RESOURCES;
572 }
573
574 me2->fileref = fr;
575 me2->dummyfcb = NULL;
576 me2->dummyfileref = NULL;
577 me2->parent = me;
578
579 InsertHeadList(&me->list_entry, &me2->list_entry);
580
581 le = le->Flink;
582 }
583
584 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
585
586 return STATUS_SUCCESS;
587 }
588
589 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
590 UINT8 c;
591
592 c = dc->hash >> 24;
593
594 if (fcb->hash_ptrs[c] == &dc->list_entry_hash) {
595 if (dc->list_entry_hash.Flink == &fcb->dir_children_hash)
596 fcb->hash_ptrs[c] = NULL;
597 else {
598 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash);
599
600 if (dc2->hash >> 24 == c)
601 fcb->hash_ptrs[c] = &dc2->list_entry_hash;
602 else
603 fcb->hash_ptrs[c] = NULL;
604 }
605 }
606
607 RemoveEntryList(&dc->list_entry_hash);
608
609 c = dc->hash_uc >> 24;
610
611 if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) {
612 if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc)
613 fcb->hash_ptrs_uc[c] = NULL;
614 else {
615 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc);
616
617 if (dc2->hash_uc >> 24 == c)
618 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc;
619 else
620 fcb->hash_ptrs_uc[c] = NULL;
621 }
622 }
623
624 RemoveEntryList(&dc->list_entry_hash_uc);
625 }
626
627 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) {
628 NTSTATUS Status;
629 fcb* fcb;
630 SECURITY_SUBJECT_CONTEXT subjcont;
631 PSID owner;
632 BOOLEAN defaulted;
633 LARGE_INTEGER time;
634 BTRFS_TIME now;
635
636 fcb = create_fcb(Vcb, PagedPool);
637 if (!fcb) {
638 ERR("out of memory\n");
639 return STATUS_INSUFFICIENT_RESOURCES;
640 }
641
642 KeQuerySystemTime(&time);
643 win_time_to_unix(time, &now);
644
645 fcb->Vcb = Vcb;
646
647 fcb->subvol = r;
648 fcb->inode = InterlockedIncrement64(&r->lastinode);
649 fcb->type = BTRFS_TYPE_DIRECTORY;
650
651 fcb->inode_item.generation = Vcb->superblock.generation;
652 fcb->inode_item.transid = Vcb->superblock.generation;
653 fcb->inode_item.st_nlink = 1;
654 fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, TRUE);
655 fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now;
656 fcb->inode_item.st_gid = GID_NOBODY;
657
658 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, FALSE, TRUE, NULL);
659
660 SeCaptureSubjectContext(&subjcont);
661
662 Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
663
664 if (!NT_SUCCESS(Status)) {
665 reap_fcb(fcb);
666 ERR("SeAssignSecurity returned %08x\n", Status);
667 return Status;
668 }
669
670 if (!fcb->sd) {
671 reap_fcb(fcb);
672 ERR("SeAssignSecurity returned NULL security descriptor\n");
673 return STATUS_INTERNAL_ERROR;
674 }
675
676 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
677 if (!NT_SUCCESS(Status)) {
678 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
679 fcb->inode_item.st_uid = UID_NOBODY;
680 fcb->sd_dirty = TRUE;
681 } else {
682 fcb->inode_item.st_uid = sid_to_uid(owner);
683 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
684 }
685
686 find_gid(fcb, parfcb, &subjcont);
687
688 fcb->inode_item_changed = TRUE;
689
690 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
691 fcb->Header.AllocationSize.QuadPart = 0;
692 fcb->Header.FileSize.QuadPart = 0;
693 fcb->Header.ValidDataLength.QuadPart = 0;
694
695 fcb->created = TRUE;
696
697 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
698 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
699
700 fcb->prop_compression = parfcb->prop_compression;
701 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
702
703 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
704 if (!fcb->hash_ptrs) {
705 ERR("out of memory\n");
706 return STATUS_INSUFFICIENT_RESOURCES;
707 }
708
709 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
710
711 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
712 if (!fcb->hash_ptrs_uc) {
713 ERR("out of memory\n");
714 return STATUS_INSUFFICIENT_RESOURCES;
715 }
716
717 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
718
719 acquire_fcb_lock_exclusive(Vcb);
720 InsertTailList(&r->fcbs, &fcb->list_entry);
721 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
722 r->fcbs_version++;
723 release_fcb_lock(Vcb);
724
725 mark_fcb_dirty(fcb);
726
727 *pfcb = fcb;
728
729 return STATUS_SUCCESS;
730 }
731
732 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
733 NTSTATUS Status;
734 LIST_ENTRY move_list, *le;
735 move_entry* me;
736 LARGE_INTEGER time;
737 BTRFS_TIME now;
738 file_ref* origparent;
739
740 // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
741
742 InitializeListHead(&move_list);
743
744 KeQuerySystemTime(&time);
745 win_time_to_unix(time, &now);
746
747 acquire_fcb_lock_exclusive(fileref->fcb->Vcb);
748
749 me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
750
751 if (!me) {
752 ERR("out of memory\n");
753 Status = STATUS_INSUFFICIENT_RESOURCES;
754 goto end;
755 }
756
757 origparent = fileref->parent;
758
759 me->fileref = fileref;
760 increase_fileref_refcount(me->fileref);
761 me->dummyfcb = NULL;
762 me->dummyfileref = NULL;
763 me->parent = NULL;
764
765 InsertTailList(&move_list, &me->list_entry);
766
767 le = move_list.Flink;
768 while (le != &move_list) {
769 me = CONTAINING_RECORD(le, move_entry, list_entry);
770
771 ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
772
773 if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
774 Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
775
776 if (!NT_SUCCESS(Status)) {
777 ERR("add_children_to_move_list returned %08x\n", Status);
778 goto end;
779 }
780 }
781
782 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
783
784 le = le->Flink;
785 }
786
787 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
788
789 // loop through list and create new inodes
790
791 le = move_list.Flink;
792 while (le != &move_list) {
793 me = CONTAINING_RECORD(le, move_entry, list_entry);
794
795 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) {
796 if (!me->dummyfcb) {
797 ULONG defda;
798 BOOL inserted = FALSE;
799 LIST_ENTRY* le3;
800
801 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
802
803 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb);
804 if (!NT_SUCCESS(Status)) {
805 ERR("duplicate_fcb returned %08x\n", Status);
806 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
807 goto end;
808 }
809
810 me->dummyfcb->subvol = me->fileref->fcb->subvol;
811 me->dummyfcb->inode = me->fileref->fcb->inode;
812
813 if (!me->dummyfcb->ads) {
814 me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty;
815 me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed;
816 me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted;
817 me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed;
818 me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed;
819 me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed;
820 }
821
822 me->dummyfcb->created = me->fileref->fcb->created;
823 me->dummyfcb->deleted = me->fileref->fcb->deleted;
824 mark_fcb_dirty(me->dummyfcb);
825
826 if (!me->fileref->fcb->ads) {
827 LIST_ENTRY* le2;
828
829 me->fileref->fcb->subvol = destdir->fcb->subvol;
830 me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
831 me->fileref->fcb->inode_item.st_nlink = 1;
832
833 defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
834 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
835 TRUE, Irp);
836
837 me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
838 me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
839 me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents);
840 me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer;
841 me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
842 me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs);
843 me->fileref->fcb->inode_item_changed = TRUE;
844
845 le2 = me->fileref->fcb->xattrs.Flink;
846 while (le2 != &me->fileref->fcb->xattrs) {
847 xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry);
848
849 xa->dirty = TRUE;
850
851 le2 = le2->Flink;
852 }
853
854 if (le == move_list.Flink) { // first entry
855 me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
856 me->fileref->fcb->inode_item.sequence++;
857
858 if (!ccb->user_set_change_time)
859 me->fileref->fcb->inode_item.st_ctime = now;
860 }
861
862 le2 = me->fileref->fcb->extents.Flink;
863 while (le2 != &me->fileref->fcb->extents) {
864 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
865
866 if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
867 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
868
869 if (ed2->size != 0) {
870 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
871
872 if (!c) {
873 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
874 } else {
875 Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
876 ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
877
878 if (!NT_SUCCESS(Status)) {
879 ERR("update_changed_extent_ref returned %08x\n", Status);
880 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
881 goto end;
882 }
883 }
884
885 }
886 }
887
888 le2 = le2->Flink;
889 }
890 } else {
891 me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol;
892 me->fileref->fcb->inode = me->parent->fileref->fcb->inode;
893 }
894
895 me->fileref->fcb->created = TRUE;
896
897 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry);
898 RemoveEntryList(&me->fileref->fcb->list_entry);
899
900 le3 = destdir->fcb->subvol->fcbs.Flink;
901 while (le3 != &destdir->fcb->subvol->fcbs) {
902 fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry);
903
904 if (fcb->inode > me->fileref->fcb->inode) {
905 InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry);
906 inserted = TRUE;
907 break;
908 }
909
910 le3 = le3->Flink;
911 }
912
913 if (!inserted)
914 InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry);
915
916 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
917
918 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
919 hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
920
921 if (hl->name.Buffer)
922 ExFreePool(hl->name.Buffer);
923
924 if (hl->utf8.Buffer)
925 ExFreePool(hl->utf8.Buffer);
926
927 ExFreePool(hl);
928 }
929
930 me->fileref->fcb->inode_item_changed = TRUE;
931 mark_fcb_dirty(me->fileref->fcb);
932
933 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) {
934 LIST_ENTRY* le2 = le->Flink;
935
936 while (le2 != &move_list) {
937 move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry);
938
939 if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) {
940 me2->dummyfcb = me->dummyfcb;
941 InterlockedIncrement(&me->dummyfcb->refcount);
942 }
943
944 le2 = le2->Flink;
945 }
946 }
947
948 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
949 } else {
950 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
951 me->fileref->fcb->inode_item.st_nlink++;
952 me->fileref->fcb->inode_item_changed = TRUE;
953 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
954 }
955 }
956
957 le = le->Flink;
958 }
959
960 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
961 fileref->fcb->subvol->root_item.ctime = now;
962
963 // loop through list and create new filerefs
964
965 le = move_list.Flink;
966 while (le != &move_list) {
967 hardlink* hl;
968 BOOL name_changed = FALSE;
969
970 me = CONTAINING_RECORD(le, move_entry, list_entry);
971
972 me->dummyfileref = create_fileref(fileref->fcb->Vcb);
973 if (!me->dummyfileref) {
974 ERR("out of memory\n");
975 Status = STATUS_INSUFFICIENT_RESOURCES;
976 goto end;
977 }
978
979 if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) {
980 root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol;
981
982 Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb);
983 if (!NT_SUCCESS(Status)) {
984 ERR("create_directory_fcb returnd %08x\n", Status);
985 goto end;
986 }
987
988 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
989 me->fileref->dc->key.obj_type = TYPE_INODE_ITEM;
990
991 me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb;
992 } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) {
993 me->dummyfileref->fcb = me->fileref->fcb;
994
995 me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id;
996 } else
997 me->dummyfileref->fcb = me->dummyfcb;
998
999 InterlockedIncrement(&me->dummyfileref->fcb->refcount);
1000
1001 me->dummyfileref->oldutf8 = me->fileref->oldutf8;
1002 me->dummyfileref->oldindex = me->fileref->dc->index;
1003
1004 if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length))
1005 name_changed = TRUE;
1006
1007 if ((le == move_list.Flink || me->fileref->fcb->inode == SUBVOL_ROOT_INODE) && !me->dummyfileref->oldutf8.Buffer) {
1008 me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
1009 if (!me->dummyfileref->oldutf8.Buffer) {
1010 ERR("out of memory\n");
1011 Status = STATUS_INSUFFICIENT_RESOURCES;
1012 goto end;
1013 }
1014
1015 RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1016
1017 me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
1018 }
1019
1020 me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
1021 me->dummyfileref->deleted = me->fileref->deleted;
1022
1023 me->dummyfileref->created = me->fileref->created;
1024 me->fileref->created = TRUE;
1025
1026 me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
1027 increase_fileref_refcount(me->dummyfileref->parent);
1028
1029 ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1030 InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
1031 ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
1032
1033 me->dummyfileref->debug_desc = me->fileref->debug_desc;
1034
1035 if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
1036 me->dummyfileref->fcb->fileref = me->dummyfileref;
1037
1038 if (!me->parent) {
1039 RemoveEntryList(&me->fileref->list_entry);
1040
1041 increase_fileref_refcount(destdir);
1042
1043 if (me->fileref->dc) {
1044 // remove from old parent
1045 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1046 RemoveEntryList(&me->fileref->dc->list_entry_index);
1047 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1048 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1049
1050 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
1051 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1052 me->fileref->parent->fcb->inode_item.sequence++;
1053 me->fileref->parent->fcb->inode_item.st_ctime = now;
1054 me->fileref->parent->fcb->inode_item.st_mtime = now;
1055 me->fileref->parent->fcb->inode_item_changed = TRUE;
1056 mark_fcb_dirty(me->fileref->parent->fcb);
1057
1058 if (name_changed) {
1059 ExFreePool(me->fileref->dc->utf8.Buffer);
1060 ExFreePool(me->fileref->dc->name.Buffer);
1061 ExFreePool(me->fileref->dc->name_uc.Buffer);
1062
1063 me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1064 if (!me->fileref->dc->utf8.Buffer) {
1065 ERR("out of memory\n");
1066 Status = STATUS_INSUFFICIENT_RESOURCES;
1067 goto end;
1068 }
1069
1070 me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
1071 RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1072
1073 me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
1074 if (!me->fileref->dc->name.Buffer) {
1075 ERR("out of memory\n");
1076 Status = STATUS_INSUFFICIENT_RESOURCES;
1077 goto end;
1078 }
1079
1080 me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
1081 RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
1082
1083 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1084 if (!NT_SUCCESS(Status)) {
1085 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1086 goto end;
1087 }
1088
1089 me->fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1090 me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
1091 }
1092
1093 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
1094 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1095
1096 // add to new parent
1097
1098 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, TRUE);
1099
1100 if (IsListEmpty(&destdir->fcb->dir_children_index))
1101 me->fileref->dc->index = 2;
1102 else {
1103 dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1104
1105 me->fileref->dc->index = max(2, dc2->index + 1);
1106 }
1107
1108 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1109 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
1110 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
1111 }
1112
1113 free_fileref(me->fileref->parent);
1114 me->fileref->parent = destdir;
1115
1116 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1117 InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
1118 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1119
1120 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1121 me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
1122 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1123 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1124 me->fileref->parent->fcb->inode_item.sequence++;
1125 me->fileref->parent->fcb->inode_item.st_ctime = now;
1126 me->fileref->parent->fcb->inode_item.st_mtime = now;
1127 me->fileref->parent->fcb->inode_item_changed = TRUE;
1128 mark_fcb_dirty(me->fileref->parent->fcb);
1129 } else {
1130 if (me->fileref->dc) {
1131 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1132 RemoveEntryList(&me->fileref->dc->list_entry_index);
1133
1134 if (!me->fileref->fcb->ads)
1135 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1136
1137 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1138
1139 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, TRUE);
1140
1141 if (me->fileref->fcb->ads)
1142 InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1143 else {
1144 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE)
1145 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1146
1147 if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index))
1148 me->fileref->dc->index = 2;
1149 else {
1150 dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1151
1152 me->fileref->dc->index = max(2, dc2->index + 1);
1153 }
1154
1155 InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1156 insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
1157 }
1158
1159 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
1160 }
1161 }
1162
1163 if (!me->dummyfileref->fcb->ads) {
1164 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1165 if (!NT_SUCCESS(Status)) {
1166 ERR("delete_fileref returned %08x\n", Status);
1167 goto end;
1168 }
1169 }
1170
1171 if (me->fileref->fcb->inode_item.st_nlink > 1) {
1172 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1173 if (!hl) {
1174 ERR("out of memory\n");
1175 Status = STATUS_INSUFFICIENT_RESOURCES;
1176 goto end;
1177 }
1178
1179 hl->parent = me->fileref->parent->fcb->inode;
1180 hl->index = me->fileref->dc->index;
1181
1182 hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
1183 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1184 if (!hl->utf8.Buffer) {
1185 ERR("out of memory\n");
1186 Status = STATUS_INSUFFICIENT_RESOURCES;
1187 ExFreePool(hl);
1188 goto end;
1189 }
1190
1191 RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1192
1193 hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
1194 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1195 if (!hl->name.Buffer) {
1196 ERR("out of memory\n");
1197 Status = STATUS_INSUFFICIENT_RESOURCES;
1198 ExFreePool(hl->utf8.Buffer);
1199 ExFreePool(hl);
1200 goto end;
1201 }
1202
1203 RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1204
1205 InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
1206 }
1207
1208 mark_fileref_dirty(me->fileref);
1209
1210 le = le->Flink;
1211 }
1212
1213 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1214
1215 le = move_list.Flink;
1216 while (le != &move_list) {
1217 me = CONTAINING_RECORD(le, move_entry, list_entry);
1218
1219 if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
1220 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
1221 if (!NT_SUCCESS(Status)) {
1222 ERR("delete_fileref returned %08x\n", Status);
1223 goto end;
1224 }
1225 }
1226
1227 le = le->Flink;
1228 }
1229
1230 destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation;
1231 destdir->fcb->subvol->root_item.ctime = now;
1232
1233 me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
1234 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1235 send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1236 send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1237
1238 Status = STATUS_SUCCESS;
1239
1240 end:
1241 while (!IsListEmpty(&move_list)) {
1242 le = RemoveHeadList(&move_list);
1243 me = CONTAINING_RECORD(le, move_entry, list_entry);
1244
1245 if (me->dummyfcb)
1246 free_fcb(me->dummyfcb);
1247
1248 if (me->dummyfileref)
1249 free_fileref(me->dummyfileref);
1250
1251 free_fileref(me->fileref);
1252
1253 ExFreePool(me);
1254 }
1255
1256 destdir->fcb->subvol->fcbs_version++;
1257 fileref->fcb->subvol->fcbs_version++;
1258
1259 release_fcb_lock(fileref->fcb->Vcb);
1260
1261 return Status;
1262 }
1263
1264 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
1265 BOOL inserted;
1266 LIST_ENTRY* le;
1267 UINT8 c, d;
1268
1269 c = dc->hash >> 24;
1270
1271 inserted = FALSE;
1272
1273 d = c;
1274 do {
1275 le = fcb->hash_ptrs[d];
1276
1277 if (d == 0)
1278 break;
1279
1280 d--;
1281 } while (!le);
1282
1283 if (!le)
1284 le = fcb->dir_children_hash.Flink;
1285
1286 while (le != &fcb->dir_children_hash) {
1287 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
1288
1289 if (dc2->hash > dc->hash) {
1290 InsertHeadList(le->Blink, &dc->list_entry_hash);
1291 inserted = TRUE;
1292 break;
1293 }
1294
1295 le = le->Flink;
1296 }
1297
1298 if (!inserted)
1299 InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash);
1300
1301 if (!fcb->hash_ptrs[c])
1302 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1303 else {
1304 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash);
1305
1306 if (dc2->hash > dc->hash)
1307 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1308 }
1309
1310 c = dc->hash_uc >> 24;
1311
1312 inserted = FALSE;
1313
1314 d = c;
1315 do {
1316 le = fcb->hash_ptrs_uc[d];
1317
1318 if (d == 0)
1319 break;
1320
1321 d--;
1322 } while (!le);
1323
1324 if (!le)
1325 le = fcb->dir_children_hash_uc.Flink;
1326
1327 while (le != &fcb->dir_children_hash_uc) {
1328 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
1329
1330 if (dc2->hash_uc > dc->hash_uc) {
1331 InsertHeadList(le->Blink, &dc->list_entry_hash_uc);
1332 inserted = TRUE;
1333 break;
1334 }
1335
1336 le = le->Flink;
1337 }
1338
1339 if (!inserted)
1340 InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc);
1341
1342 if (!fcb->hash_ptrs_uc[c])
1343 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1344 else {
1345 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc);
1346
1347 if (dc2->hash_uc > dc->hash_uc)
1348 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1349 }
1350 }
1351
1352 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
1353 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1354 FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
1355 fcb *fcb = FileObject->FsContext;
1356 ccb* ccb = FileObject->FsContext2;
1357 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
1358 WCHAR* fn;
1359 ULONG fnlen, utf8len, origutf8len;
1360 UNICODE_STRING fnus;
1361 ANSI_STRING utf8;
1362 NTSTATUS Status;
1363 LARGE_INTEGER time;
1364 BTRFS_TIME now;
1365 LIST_ENTRY rollback, *le;
1366 hardlink* hl;
1367 SECURITY_SUBJECT_CONTEXT subjcont;
1368 ACCESS_MASK access;
1369
1370 InitializeListHead(&rollback);
1371
1372 TRACE("tfo = %p\n", tfo);
1373 TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
1374 TRACE("RootDirectory = %p\n", fri->RootDirectory);
1375 TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
1376
1377 fn = fri->FileName;
1378 fnlen = fri->FileNameLength / sizeof(WCHAR);
1379
1380 if (!tfo) {
1381 if (!fileref || !fileref->parent) {
1382 ERR("no fileref set and no directory given\n");
1383 return STATUS_INVALID_PARAMETER;
1384 }
1385 } else {
1386 LONG i;
1387
1388 while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
1389 fnlen--;
1390
1391 if (fnlen == 0)
1392 return STATUS_INVALID_PARAMETER;
1393
1394 for (i = fnlen - 1; i >= 0; i--) {
1395 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
1396 fn = &fri->FileName[i+1];
1397 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1;
1398 break;
1399 }
1400 }
1401 }
1402
1403 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
1404 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
1405 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
1406
1407 if (fcb->ads) {
1408 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
1409 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
1410 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
1411 Status = STATUS_INVALID_PARAMETER;
1412 goto end;
1413 }
1414
1415 fnus.Buffer = fn;
1416 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
1417
1418 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
1419
1420 origutf8len = fileref->dc->utf8.Length;
1421
1422 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1423 if (!NT_SUCCESS(Status))
1424 goto end;
1425
1426 utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
1427 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1428 if (!utf8.Buffer) {
1429 ERR("out of memory\n");
1430 Status = STATUS_INSUFFICIENT_RESOURCES;
1431 goto end;
1432 }
1433
1434 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
1435 if (!NT_SUCCESS(Status))
1436 goto end;
1437
1438 if (tfo && tfo->FsContext2) {
1439 struct _ccb* relatedccb = tfo->FsContext2;
1440
1441 related = relatedccb->fileref;
1442 increase_fileref_refcount(related);
1443 } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') {
1444 related = fileref->parent;
1445 increase_fileref_refcount(related);
1446 }
1447
1448 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
1449
1450 if (NT_SUCCESS(Status)) {
1451 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
1452
1453 if (fileref != oldfileref && !oldfileref->deleted) {
1454 if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
1455 Status = STATUS_OBJECT_NAME_COLLISION;
1456 goto end;
1457 } else if ((oldfileref->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
1458 WARN("trying to overwrite open file\n");
1459 Status = STATUS_ACCESS_DENIED;
1460 goto end;
1461 }
1462
1463 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
1464 WARN("trying to overwrite directory\n");
1465 Status = STATUS_ACCESS_DENIED;
1466 goto end;
1467 }
1468 }
1469
1470 if (fileref == oldfileref || oldfileref->deleted) {
1471 free_fileref(oldfileref);
1472 oldfileref = NULL;
1473 }
1474 }
1475
1476 if (!related) {
1477 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
1478
1479 if (!NT_SUCCESS(Status)) {
1480 ERR("open_fileref returned %08x\n", Status);
1481 goto end;
1482 }
1483 }
1484
1485 if (related->fcb == Vcb->dummy_fcb) {
1486 Status = STATUS_ACCESS_DENIED;
1487 goto end;
1488 }
1489
1490 SeCaptureSubjectContext(&subjcont);
1491
1492 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
1493 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1494 SeReleaseSubjectContext(&subjcont);
1495 TRACE("SeAccessCheck failed, returning %08x\n", Status);
1496 goto end;
1497 }
1498
1499 SeReleaseSubjectContext(&subjcont);
1500
1501 if (has_open_children(fileref)) {
1502 WARN("trying to rename file with open children\n");
1503 Status = STATUS_ACCESS_DENIED;
1504 goto end;
1505 }
1506
1507 if (oldfileref) {
1508 SeCaptureSubjectContext(&subjcont);
1509
1510 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
1511 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
1512 SeReleaseSubjectContext(&subjcont);
1513 TRACE("SeAccessCheck failed, returning %08x\n", Status);
1514 goto end;
1515 }
1516
1517 SeReleaseSubjectContext(&subjcont);
1518
1519 Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
1520 if (!NT_SUCCESS(Status)) {
1521 ERR("delete_fileref returned %08x\n", Status);
1522 goto end;
1523 }
1524 }
1525
1526 if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) {
1527 Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
1528 if (!NT_SUCCESS(Status)) {
1529 ERR("move_across_subvols returned %08x\n", Status);
1530 }
1531 goto end;
1532 }
1533
1534 if (related == fileref->parent) { // keeping file in same directory
1535 UNICODE_STRING oldfn, newfn;
1536 USHORT name_offset;
1537 ULONG reqlen, oldutf8len;
1538
1539 oldfn.Length = oldfn.MaximumLength = 0;
1540
1541 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1542 if (Status != STATUS_BUFFER_OVERFLOW) {
1543 ERR("fileref_get_filename returned %08x\n", Status);
1544 goto end;
1545 }
1546
1547 oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1548 if (!oldfn.Buffer) {
1549 ERR("out of memory\n");
1550 Status = STATUS_INSUFFICIENT_RESOURCES;
1551 goto end;
1552 }
1553
1554 oldfn.MaximumLength = (UINT16)reqlen;
1555
1556 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
1557 if (!NT_SUCCESS(Status)) {
1558 ERR("fileref_get_filename returned %08x\n", Status);
1559 ExFreePool(oldfn.Buffer);
1560 goto end;
1561 }
1562
1563 oldutf8len = fileref->dc->utf8.Length;
1564
1565 if (!fileref->created && !fileref->oldutf8.Buffer) {
1566 fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1567 if (!fileref->oldutf8.Buffer) {
1568 ERR("out of memory\n");
1569 Status = STATUS_INSUFFICIENT_RESOURCES;
1570 goto end;
1571 }
1572
1573 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1574 RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1575 }
1576
1577 TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer);
1578
1579 mark_fileref_dirty(fileref);
1580
1581 if (fileref->dc) {
1582 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1583
1584 ExFreePool(fileref->dc->utf8.Buffer);
1585 ExFreePool(fileref->dc->name.Buffer);
1586 ExFreePool(fileref->dc->name_uc.Buffer);
1587
1588 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1589 if (!fileref->dc->utf8.Buffer) {
1590 ERR("out of memory\n");
1591 Status = STATUS_INSUFFICIENT_RESOURCES;
1592 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1593 ExFreePool(oldfn.Buffer);
1594 goto end;
1595 }
1596
1597 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1598 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1599
1600 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1601 if (!fileref->dc->name.Buffer) {
1602 ERR("out of memory\n");
1603 Status = STATUS_INSUFFICIENT_RESOURCES;
1604 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1605 ExFreePool(oldfn.Buffer);
1606 goto end;
1607 }
1608
1609 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1610 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1611
1612 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1613 if (!NT_SUCCESS(Status)) {
1614 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1615 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1616 ExFreePool(oldfn.Buffer);
1617 goto end;
1618 }
1619
1620 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
1621
1622 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1623 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1624
1625 insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc);
1626
1627 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1628 }
1629
1630 newfn.Length = newfn.MaximumLength = 0;
1631
1632 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1633 if (Status != STATUS_BUFFER_OVERFLOW) {
1634 ERR("fileref_get_filename returned %08x\n", Status);
1635 ExFreePool(oldfn.Buffer);
1636 goto end;
1637 }
1638
1639 newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1640 if (!newfn.Buffer) {
1641 ERR("out of memory\n");
1642 Status = STATUS_INSUFFICIENT_RESOURCES;
1643 ExFreePool(oldfn.Buffer);
1644 goto end;
1645 }
1646
1647 newfn.MaximumLength = (UINT16)reqlen;
1648
1649 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
1650 if (!NT_SUCCESS(Status)) {
1651 ERR("fileref_get_filename returned %08x\n", Status);
1652 ExFreePool(oldfn.Buffer);
1653 ExFreePool(newfn.Buffer);
1654 goto end;
1655 }
1656
1657 KeQuerySystemTime(&time);
1658 win_time_to_unix(time, &now);
1659
1660 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1661 fcb->inode_item.transid = Vcb->superblock.generation;
1662 fcb->inode_item.sequence++;
1663
1664 if (!ccb->user_set_change_time)
1665 fcb->inode_item.st_ctime = now;
1666
1667 fcb->inode_item_changed = TRUE;
1668 mark_fcb_dirty(fcb);
1669 }
1670
1671 // update parent's INODE_ITEM
1672
1673 related->fcb->inode_item.transid = Vcb->superblock.generation;
1674 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1675 related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len);
1676 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1677 related->fcb->inode_item.sequence++;
1678 related->fcb->inode_item.st_ctime = now;
1679 related->fcb->inode_item.st_mtime = now;
1680
1681 related->fcb->inode_item_changed = TRUE;
1682 mark_fcb_dirty(related->fcb);
1683 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1684
1685 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL,
1686 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL);
1687 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL,
1688 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL);
1689
1690 ExFreePool(oldfn.Buffer);
1691 ExFreePool(newfn.Buffer);
1692
1693 Status = STATUS_SUCCESS;
1694 goto end;
1695 }
1696
1697 // We move files by moving the existing fileref to the new directory, and
1698 // replacing it with a dummy fileref with the same original values, but marked as deleted.
1699
1700 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
1701
1702 fr2 = create_fileref(Vcb);
1703
1704 fr2->fcb = fileref->fcb;
1705 fr2->fcb->refcount++;
1706
1707 fr2->oldutf8 = fileref->oldutf8;
1708 fr2->oldindex = fileref->dc->index;
1709 fr2->delete_on_close = fileref->delete_on_close;
1710 fr2->deleted = TRUE;
1711 fr2->created = fileref->created;
1712 fr2->parent = fileref->parent;
1713 fr2->dc = NULL;
1714
1715 if (!fr2->oldutf8.Buffer) {
1716 fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
1717 if (!fr2->oldutf8.Buffer) {
1718 ERR("out of memory\n");
1719 Status = STATUS_INSUFFICIENT_RESOURCES;
1720 goto end;
1721 }
1722
1723 RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1724
1725 fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
1726 }
1727
1728 if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
1729 fr2->fcb->fileref = fr2;
1730
1731 if (fileref->fcb->inode == SUBVOL_ROOT_INODE)
1732 fileref->fcb->subvol->parent = related->fcb->subvol->id;
1733
1734 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
1735 fileref->oldutf8.Buffer = NULL;
1736 fileref->deleted = FALSE;
1737 fileref->created = TRUE;
1738 fileref->parent = related;
1739
1740 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
1741 InsertHeadList(&fileref->list_entry, &fr2->list_entry);
1742 RemoveEntryList(&fileref->list_entry);
1743 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
1744
1745 mark_fileref_dirty(fr2);
1746 mark_fileref_dirty(fileref);
1747
1748 if (fileref->dc) {
1749 // remove from old parent
1750 ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, TRUE);
1751 RemoveEntryList(&fileref->dc->list_entry_index);
1752 remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
1753 ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
1754
1755 if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
1756 // handle changed name
1757
1758 ExFreePool(fileref->dc->utf8.Buffer);
1759 ExFreePool(fileref->dc->name.Buffer);
1760 ExFreePool(fileref->dc->name_uc.Buffer);
1761
1762 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
1763 if (!fileref->dc->utf8.Buffer) {
1764 ERR("out of memory\n");
1765 Status = STATUS_INSUFFICIENT_RESOURCES;
1766 goto end;
1767 }
1768
1769 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
1770 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
1771
1772 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
1773 if (!fileref->dc->name.Buffer) {
1774 ERR("out of memory\n");
1775 Status = STATUS_INSUFFICIENT_RESOURCES;
1776 goto end;
1777 }
1778
1779 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
1780 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
1781
1782 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
1783 if (!NT_SUCCESS(Status)) {
1784 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
1785 goto end;
1786 }
1787
1788 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
1789 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
1790 }
1791
1792 // add to new parent
1793 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
1794
1795 if (IsListEmpty(&related->fcb->dir_children_index))
1796 fileref->dc->index = 2;
1797 else {
1798 dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1799
1800 fileref->dc->index = max(2, dc2->index + 1);
1801 }
1802
1803 InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
1804 insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
1805 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
1806 }
1807
1808 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
1809 InsertTailList(&related->children, &fileref->list_entry);
1810 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
1811
1812 if (fcb->inode_item.st_nlink > 1) {
1813 // add new hardlink entry to fcb
1814
1815 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1816 if (!hl) {
1817 ERR("out of memory\n");
1818 Status = STATUS_INSUFFICIENT_RESOURCES;
1819 goto end;
1820 }
1821
1822 hl->parent = related->fcb->inode;
1823 hl->index = fileref->dc->index;
1824
1825 hl->name.Length = hl->name.MaximumLength = fnus.Length;
1826 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1827
1828 if (!hl->name.Buffer) {
1829 ERR("out of memory\n");
1830 ExFreePool(hl);
1831 Status = STATUS_INSUFFICIENT_RESOURCES;
1832 goto end;
1833 }
1834
1835 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
1836
1837 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
1838 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1839
1840 if (!hl->utf8.Buffer) {
1841 ERR("out of memory\n");
1842 ExFreePool(hl->name.Buffer);
1843 ExFreePool(hl);
1844 Status = STATUS_INSUFFICIENT_RESOURCES;
1845 goto end;
1846 }
1847
1848 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
1849
1850 InsertTailList(&fcb->hardlinks, &hl->list_entry);
1851 }
1852
1853 // delete old hardlink entry from fcb
1854
1855 le = fcb->hardlinks.Flink;
1856 while (le != &fcb->hardlinks) {
1857 hl = CONTAINING_RECORD(le, hardlink, list_entry);
1858
1859 if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
1860 RemoveEntryList(&hl->list_entry);
1861
1862 if (hl->utf8.Buffer)
1863 ExFreePool(hl->utf8.Buffer);
1864
1865 if (hl->name.Buffer)
1866 ExFreePool(hl->name.Buffer);
1867
1868 ExFreePool(hl);
1869 break;
1870 }
1871
1872 le = le->Flink;
1873 }
1874
1875 // update inode's INODE_ITEM
1876
1877 KeQuerySystemTime(&time);
1878 win_time_to_unix(time, &now);
1879
1880 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
1881 fcb->inode_item.transid = Vcb->superblock.generation;
1882 fcb->inode_item.sequence++;
1883
1884 if (!ccb->user_set_change_time)
1885 fcb->inode_item.st_ctime = now;
1886
1887 fcb->inode_item_changed = TRUE;
1888 mark_fcb_dirty(fcb);
1889 }
1890
1891 // update new parent's INODE_ITEM
1892
1893 related->fcb->inode_item.transid = Vcb->superblock.generation;
1894 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1895 related->fcb->inode_item.st_size += 2 * utf8len;
1896 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size);
1897 related->fcb->inode_item.sequence++;
1898 related->fcb->inode_item.st_ctime = now;
1899 related->fcb->inode_item.st_mtime = now;
1900
1901 related->fcb->inode_item_changed = TRUE;
1902 mark_fcb_dirty(related->fcb);
1903
1904 // update old parent's INODE_ITEM
1905
1906 fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
1907 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1908 fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
1909 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
1910 fr2->parent->fcb->inode_item.sequence++;
1911 fr2->parent->fcb->inode_item.st_ctime = now;
1912 fr2->parent->fcb->inode_item.st_mtime = now;
1913
1914 free_fileref(fr2);
1915
1916 fr2->parent->fcb->inode_item_changed = TRUE;
1917 mark_fcb_dirty(fr2->parent->fcb);
1918
1919 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1920 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1921 send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1922
1923 Status = STATUS_SUCCESS;
1924
1925 end:
1926 if (oldfileref)
1927 free_fileref(oldfileref);
1928
1929 if (!NT_SUCCESS(Status) && related)
1930 free_fileref(related);
1931
1932 if (!NT_SUCCESS(Status) && fr2)
1933 free_fileref(fr2);
1934
1935 if (NT_SUCCESS(Status))
1936 clear_rollback(&rollback);
1937 else
1938 do_rollback(Vcb, &rollback);
1939
1940 ExReleaseResourceLite(fcb->Header.Resource);
1941 ExReleaseResourceLite(&Vcb->fileref_lock);
1942 ExReleaseResourceLite(&Vcb->tree_lock);
1943
1944 return Status;
1945 }
1946
1947 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, UINT16 end, fcb* fcb, file_ref* fileref, BOOL advance_only) {
1948 LARGE_INTEGER time;
1949 BTRFS_TIME now;
1950
1951 TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adsdata.Length);
1952
1953 if (!fileref || !fileref->parent) {
1954 ERR("no fileref for stream\n");
1955 return STATUS_INTERNAL_ERROR;
1956 }
1957
1958 if (end < fcb->adsdata.Length) {
1959 if (advance_only)
1960 return STATUS_SUCCESS;
1961
1962 TRACE("truncating stream to %llx bytes\n", end);
1963
1964 fcb->adsdata.Length = end;
1965 } else if (end > fcb->adsdata.Length) {
1966 TRACE("extending stream to %llx bytes\n", end);
1967
1968 if (end > fcb->adsmaxlen) {
1969 ERR("error - xattr too long (%u > %u)\n", end, fcb->adsmaxlen);
1970 return STATUS_DISK_FULL;
1971 }
1972
1973 if (end > fcb->adsdata.MaximumLength) {
1974 char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
1975 if (!data) {
1976 ERR("out of memory\n");
1977 ExFreePool(data);
1978 return STATUS_INSUFFICIENT_RESOURCES;
1979 }
1980
1981 if (fcb->adsdata.Buffer) {
1982 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length);
1983 ExFreePool(fcb->adsdata.Buffer);
1984 }
1985
1986 fcb->adsdata.Buffer = data;
1987 fcb->adsdata.MaximumLength = end;
1988 }
1989
1990 RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length);
1991
1992 fcb->adsdata.Length = end;
1993 }
1994
1995 mark_fcb_dirty(fcb);
1996
1997 fcb->Header.AllocationSize.QuadPart = end;
1998 fcb->Header.FileSize.QuadPart = end;
1999 fcb->Header.ValidDataLength.QuadPart = end;
2000
2001 KeQuerySystemTime(&time);
2002 win_time_to_unix(time, &now);
2003
2004 fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
2005 fileref->parent->fcb->inode_item.sequence++;
2006 fileref->parent->fcb->inode_item.st_ctime = now;
2007
2008 fileref->parent->fcb->inode_item_changed = TRUE;
2009 mark_fcb_dirty(fileref->parent->fcb);
2010
2011 fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2012 fileref->parent->fcb->subvol->root_item.ctime = now;
2013
2014 return STATUS_SUCCESS;
2015 }
2016
2017 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, BOOL prealloc) {
2018 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
2019 fcb* fcb = FileObject->FsContext;
2020 ccb* ccb = FileObject->FsContext2;
2021 file_ref* fileref = ccb ? ccb->fileref : NULL;
2022 NTSTATUS Status;
2023 LARGE_INTEGER time;
2024 CC_FILE_SIZES ccfs;
2025 LIST_ENTRY rollback;
2026 BOOL set_size = FALSE;
2027 ULONG filter;
2028
2029 if (!fileref) {
2030 ERR("fileref is NULL\n");
2031 return STATUS_INVALID_PARAMETER;
2032 }
2033
2034 InitializeListHead(&rollback);
2035
2036 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2037
2038 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2039
2040 if (fileref ? fileref->deleted : fcb->deleted) {
2041 Status = STATUS_FILE_CLOSED;
2042 goto end;
2043 }
2044
2045 if (fcb->ads) {
2046 if (feofi->EndOfFile.QuadPart > 0xffff) {
2047 Status = STATUS_DISK_FULL;
2048 goto end;
2049 }
2050
2051 if (feofi->EndOfFile.QuadPart < 0) {
2052 Status = STATUS_INVALID_PARAMETER;
2053 goto end;
2054 }
2055
2056 Status = stream_set_end_of_file_information(Vcb, (UINT16)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only);
2057
2058 if (NT_SUCCESS(Status)) {
2059 ccfs.AllocationSize = fcb->Header.AllocationSize;
2060 ccfs.FileSize = fcb->Header.FileSize;
2061 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2062 set_size = TRUE;
2063 }
2064
2065 filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
2066
2067 if (!ccb->user_set_write_time) {
2068 KeQuerySystemTime(&time);
2069 win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime);
2070 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2071
2072 fileref->parent->fcb->inode_item_changed = TRUE;
2073 mark_fcb_dirty(fileref->parent->fcb);
2074 }
2075
2076 send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
2077
2078 goto end;
2079 }
2080
2081 TRACE("file: %S\n", file_desc(FileObject));
2082 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE");
2083 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
2084 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2085
2086 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
2087
2088 if ((UINT64)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
2089 if (advance_only) {
2090 Status = STATUS_SUCCESS;
2091 goto end;
2092 }
2093
2094 TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2095
2096 if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
2097 Status = STATUS_USER_MAPPED_FILE;
2098 goto end;
2099 }
2100
2101 Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
2102 if (!NT_SUCCESS(Status)) {
2103 ERR("error - truncate_file failed\n");
2104 goto end;
2105 }
2106 } else if ((UINT64)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
2107 if (Irp->Flags & IRP_PAGING_IO) {
2108 TRACE("paging IO tried to extend file size\n");
2109 Status = STATUS_SUCCESS;
2110 goto end;
2111 }
2112
2113 TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart);
2114
2115 Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
2116 if (!NT_SUCCESS(Status)) {
2117 ERR("error - extend_file failed\n");
2118 goto end;
2119 }
2120 } else if ((UINT64)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
2121 Status = STATUS_SUCCESS;
2122 goto end;
2123 }
2124
2125 ccfs.AllocationSize = fcb->Header.AllocationSize;
2126 ccfs.FileSize = fcb->Header.FileSize;
2127 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
2128 set_size = TRUE;
2129
2130 filter = FILE_NOTIFY_CHANGE_SIZE;
2131
2132 if (!ccb->user_set_write_time) {
2133 KeQuerySystemTime(&time);
2134 win_time_to_unix(time, &fcb->inode_item.st_mtime);
2135 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2136 }
2137
2138 fcb->inode_item_changed = TRUE;
2139 mark_fcb_dirty(fcb);
2140 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2141
2142 Status = STATUS_SUCCESS;
2143
2144 end:
2145 if (NT_SUCCESS(Status))
2146 clear_rollback(&rollback);
2147 else
2148 do_rollback(Vcb, &rollback);
2149
2150 ExReleaseResourceLite(fcb->Header.Resource);
2151
2152 if (set_size) {
2153 _SEH2_TRY {
2154 CcSetFileSizes(FileObject, &ccfs);
2155 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2156 Status = _SEH2_GetExceptionCode();
2157 } _SEH2_END;
2158
2159 if (!NT_SUCCESS(Status))
2160 ERR("CcSetFileSizes threw exception %08x\n", Status);
2161 }
2162
2163 ExReleaseResourceLite(&Vcb->tree_lock);
2164
2165 return Status;
2166 }
2167
2168 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
2169 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
2170
2171 TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
2172
2173 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
2174
2175 FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
2176
2177 return STATUS_SUCCESS;
2178 }
2179
2180 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
2181 FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
2182 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
2183 ccb* ccb = FileObject->FsContext2;
2184 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2185 WCHAR* fn;
2186 ULONG fnlen, utf8len;
2187 UNICODE_STRING fnus;
2188 ANSI_STRING utf8;
2189 NTSTATUS Status;
2190 LARGE_INTEGER time;
2191 BTRFS_TIME now;
2192 LIST_ENTRY rollback;
2193 hardlink* hl;
2194 ACCESS_MASK access;
2195 SECURITY_SUBJECT_CONTEXT subjcont;
2196 dir_child* dc = NULL;
2197
2198 InitializeListHead(&rollback);
2199
2200 // FIXME - check fli length
2201 // FIXME - don't ignore fli->RootDirectory
2202
2203 TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
2204 TRACE("RootDirectory = %p\n", fli->RootDirectory);
2205 TRACE("FileNameLength = %x\n", fli->FileNameLength);
2206 TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
2207
2208 fn = fli->FileName;
2209 fnlen = fli->FileNameLength / sizeof(WCHAR);
2210
2211 if (!tfo) {
2212 if (!fileref || !fileref->parent) {
2213 ERR("no fileref set and no directory given\n");
2214 return STATUS_INVALID_PARAMETER;
2215 }
2216
2217 parfcb = fileref->parent->fcb;
2218 tfofcb = NULL;
2219 } else {
2220 LONG i;
2221
2222 tfofcb = tfo->FsContext;
2223 parfcb = tfofcb;
2224
2225 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
2226 fnlen--;
2227
2228 if (fnlen == 0)
2229 return STATUS_INVALID_PARAMETER;
2230
2231 for (i = fnlen - 1; i >= 0; i--) {
2232 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
2233 fn = &fli->FileName[i+1];
2234 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
2235 break;
2236 }
2237 }
2238 }
2239
2240 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2241 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE);
2242 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2243
2244 if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2245 WARN("tried to create hard link on directory\n");
2246 Status = STATUS_FILE_IS_A_DIRECTORY;
2247 goto end;
2248 }
2249
2250 if (fcb->ads) {
2251 WARN("tried to create hard link on stream\n");
2252 Status = STATUS_INVALID_PARAMETER;
2253 goto end;
2254 }
2255
2256 if (fcb->inode_item.st_nlink >= 65535) {
2257 Status = STATUS_TOO_MANY_LINKS;
2258 goto end;
2259 }
2260
2261 fnus.Buffer = fn;
2262 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR));
2263
2264 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
2265
2266 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2267 if (!NT_SUCCESS(Status))
2268 goto end;
2269
2270 utf8.MaximumLength = utf8.Length = (UINT16)utf8len;
2271 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2272 if (!utf8.Buffer) {
2273 ERR("out of memory\n");
2274 Status = STATUS_INSUFFICIENT_RESOURCES;
2275 goto end;
2276 }
2277
2278 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2279 if (!NT_SUCCESS(Status))
2280 goto end;
2281
2282 if (tfo && tfo->FsContext2) {
2283 struct _ccb* relatedccb = tfo->FsContext2;
2284
2285 related = relatedccb->fileref;
2286 increase_fileref_refcount(related);
2287 }
2288
2289 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2290
2291 if (NT_SUCCESS(Status)) {
2292 if (!oldfileref->deleted) {
2293 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
2294
2295 if (!fli->ReplaceIfExists) {
2296 Status = STATUS_OBJECT_NAME_COLLISION;
2297 goto end;
2298 } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) {
2299 WARN("trying to overwrite open file\n");
2300 Status = STATUS_ACCESS_DENIED;
2301 goto end;
2302 } else if (fileref == oldfileref) {
2303 Status = STATUS_ACCESS_DENIED;
2304 goto end;
2305 }
2306
2307 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2308 WARN("trying to overwrite directory\n");
2309 Status = STATUS_ACCESS_DENIED;
2310 goto end;
2311 }
2312 } else {
2313 free_fileref(oldfileref);
2314 oldfileref = NULL;
2315 }
2316 }
2317
2318 if (!related) {
2319 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2320
2321 if (!NT_SUCCESS(Status)) {
2322 ERR("open_fileref returned %08x\n", Status);
2323 goto end;
2324 }
2325 }
2326
2327 SeCaptureSubjectContext(&subjcont);
2328
2329 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL,
2330 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2331 SeReleaseSubjectContext(&subjcont);
2332 TRACE("SeAccessCheck failed, returning %08x\n", Status);
2333 goto end;
2334 }
2335
2336 SeReleaseSubjectContext(&subjcont);
2337
2338 if (fcb->subvol != parfcb->subvol) {
2339 WARN("can't create hard link over subvolume boundary\n");
2340 Status = STATUS_INVALID_PARAMETER;
2341 goto end;
2342 }
2343
2344 if (oldfileref) {
2345 SeCaptureSubjectContext(&subjcont);
2346
2347 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
2348 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2349 SeReleaseSubjectContext(&subjcont);
2350 TRACE("SeAccessCheck failed, returning %08x\n", Status);
2351 goto end;
2352 }
2353
2354 SeReleaseSubjectContext(&subjcont);
2355
2356 Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
2357 if (!NT_SUCCESS(Status)) {
2358 ERR("delete_fileref returned %08x\n", Status);
2359 goto end;
2360 }
2361 }
2362
2363 fr2 = create_fileref(Vcb);
2364
2365 fr2->fcb = fcb;
2366 fcb->refcount++;
2367
2368 fr2->created = TRUE;
2369 fr2->parent = related;
2370
2371 Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc);
2372 if (!NT_SUCCESS(Status))
2373 WARN("add_dir_child returned %08x\n", Status);
2374
2375 fr2->dc = dc;
2376 dc->fileref = fr2;
2377
2378 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
2379 InsertTailList(&related->children, &fr2->list_entry);
2380 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
2381
2382 // add hardlink for existing fileref, if it's not there already
2383 if (IsListEmpty(&fcb->hardlinks)) {
2384 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2385 if (!hl) {
2386 ERR("out of memory\n");
2387 Status = STATUS_INSUFFICIENT_RESOURCES;
2388 goto end;
2389 }
2390
2391 hl->parent = fileref->parent->fcb->inode;
2392 hl->index = fileref->dc->index;
2393
2394 hl->name.Length = hl->name.MaximumLength = fnus.Length;
2395 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2396
2397 if (!hl->name.Buffer) {
2398 ERR("out of memory\n");
2399 ExFreePool(hl);
2400 Status = STATUS_INSUFFICIENT_RESOURCES;
2401 goto end;
2402 }
2403
2404 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2405
2406 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
2407 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2408
2409 if (!hl->utf8.Buffer) {
2410 ERR("out of memory\n");
2411 ExFreePool(hl->name.Buffer);
2412 ExFreePool(hl);
2413 Status = STATUS_INSUFFICIENT_RESOURCES;
2414 goto end;
2415 }
2416
2417 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2418
2419 InsertTailList(&fcb->hardlinks, &hl->list_entry);
2420 }
2421
2422 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
2423 if (!hl) {
2424 ERR("out of memory\n");
2425 Status = STATUS_INSUFFICIENT_RESOURCES;
2426 goto end;
2427 }
2428
2429 hl->parent = related->fcb->inode;
2430 hl->index = dc->index;
2431
2432 hl->name.Length = hl->name.MaximumLength = fnus.Length;
2433 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
2434
2435 if (!hl->name.Buffer) {
2436 ERR("out of memory\n");
2437 ExFreePool(hl);
2438 Status = STATUS_INSUFFICIENT_RESOURCES;
2439 goto end;
2440 }
2441
2442 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
2443
2444 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
2445 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
2446
2447 if (!hl->utf8.Buffer) {
2448 ERR("out of memory\n");
2449 ExFreePool(hl->name.Buffer);
2450 ExFreePool(hl);
2451 Status = STATUS_INSUFFICIENT_RESOURCES;
2452 goto end;
2453 }
2454
2455 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
2456 ExFreePool(utf8.Buffer);
2457
2458 InsertTailList(&fcb->hardlinks, &hl->list_entry);
2459
2460 mark_fileref_dirty(fr2);
2461 free_fileref(fr2);
2462
2463 // update inode's INODE_ITEM
2464
2465 KeQuerySystemTime(&time);
2466 win_time_to_unix(time, &now);
2467
2468 fcb->inode_item.transid = Vcb->superblock.generation;
2469 fcb->inode_item.sequence++;
2470 fcb->inode_item.st_nlink++;
2471
2472 if (!ccb->user_set_change_time)
2473 fcb->inode_item.st_ctime = now;
2474
2475 fcb->inode_item_changed = TRUE;
2476 mark_fcb_dirty(fcb);
2477
2478 // update parent's INODE_ITEM
2479
2480 parfcb->inode_item.transid = Vcb->superblock.generation;
2481 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2482 parfcb->inode_item.st_size += 2 * utf8len;
2483 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size);
2484 parfcb->inode_item.sequence++;
2485 parfcb->inode_item.st_ctime = now;
2486
2487 parfcb->inode_item_changed = TRUE;
2488 mark_fcb_dirty(parfcb);
2489
2490 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
2491
2492 Status = STATUS_SUCCESS;
2493
2494 end:
2495 if (oldfileref)
2496 free_fileref(oldfileref);
2497
2498 if (!NT_SUCCESS(Status) && related)
2499 free_fileref(related);
2500
2501 if (!NT_SUCCESS(Status) && fr2)
2502 free_fileref(fr2);
2503
2504 if (NT_SUCCESS(Status))
2505 clear_rollback(&rollback);
2506 else
2507 do_rollback(Vcb, &rollback);
2508
2509 ExReleaseResourceLite(fcb->Header.Resource);
2510 ExReleaseResourceLite(&Vcb->fileref_lock);
2511 ExReleaseResourceLite(&Vcb->tree_lock);
2512
2513 return Status;
2514 }
2515
2516 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
2517 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
2518 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2519 fcb* fcb = FileObject->FsContext;
2520 ccb* ccb = FileObject->FsContext2;
2521 file_ref* fileref = ccb ? ccb->fileref : NULL;
2522 NTSTATUS Status;
2523 LARGE_INTEGER time;
2524 CC_FILE_SIZES ccfs;
2525 LIST_ENTRY rollback;
2526 BOOL set_size = FALSE;
2527 ULONG filter;
2528
2529 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
2530 ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
2531 return STATUS_INVALID_PARAMETER;
2532 }
2533
2534 if (!fileref) {
2535 ERR("fileref is NULL\n");
2536 return STATUS_INVALID_PARAMETER;
2537 }
2538
2539 InitializeListHead(&rollback);
2540
2541 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
2542
2543 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
2544
2545 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
2546 Status = STATUS_INVALID_PARAMETER;
2547 goto end;
2548 }
2549
2550 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
2551 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart,
2552 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
2553 Status = STATUS_INVALID_PARAMETER;
2554 goto end;
2555 }
2556
2557 if (fileref ? fileref->deleted : fcb->deleted) {
2558 Status = STATUS_FILE_CLOSED;
2559 goto end;
2560 }
2561
2562 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
2563 // and we set it to the max anyway.
2564
2565 ccfs.AllocationSize = fcb->Header.AllocationSize;
2566 ccfs.FileSize = fcb->Header.FileSize;
2567 ccfs.ValidDataLength = fvdli->ValidDataLength;
2568 set_size = TRUE;
2569
2570 filter = FILE_NOTIFY_CHANGE_SIZE;
2571
2572 if (!ccb->user_set_write_time) {
2573 KeQuerySystemTime(&time);
2574 win_time_to_unix(time, &fcb->inode_item.st_mtime);
2575 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2576 }
2577
2578 fcb->inode_item_changed = TRUE;
2579 mark_fcb_dirty(fcb);
2580
2581 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
2582
2583 Status = STATUS_SUCCESS;
2584
2585 end:
2586 if (NT_SUCCESS(Status))
2587 clear_rollback(&rollback);
2588 else
2589 do_rollback(Vcb, &rollback);
2590
2591 ExReleaseResourceLite(fcb->Header.Resource);
2592
2593 if (set_size) {
2594 _SEH2_TRY {
2595 CcSetFileSizes(FileObject, &ccfs);
2596 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2597 Status = _SEH2_GetExceptionCode();
2598 } _SEH2_END;
2599
2600 if (!NT_SUCCESS(Status))
2601 ERR("CcSetFileSizes threw exception %08x\n", Status);
2602 else
2603 fcb->Header.AllocationSize = ccfs.AllocationSize;
2604 }
2605
2606 ExReleaseResourceLite(&Vcb->tree_lock);
2607
2608 return Status;
2609 }
2610
2611 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
2612 _Function_class_(DRIVER_DISPATCH)
2613 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2614 NTSTATUS Status;
2615 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2616 device_extension* Vcb = DeviceObject->DeviceExtension;
2617 fcb* fcb = IrpSp->FileObject->FsContext;
2618 ccb* ccb = IrpSp->FileObject->FsContext2;
2619 BOOL top_level;
2620
2621 FsRtlEnterFileSystem();
2622
2623 top_level = is_top_level(Irp);
2624
2625 Irp->IoStatus.Information = 0;
2626
2627 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2628 Status = vol_set_information(DeviceObject, Irp);
2629 goto end;
2630 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2631 Status = STATUS_INVALID_PARAMETER;
2632 goto end;
2633 }
2634
2635 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
2636 Status = STATUS_ACCESS_DENIED;
2637 goto end;
2638 }
2639
2640 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
2641 Status = STATUS_MEDIA_WRITE_PROTECTED;
2642 goto end;
2643 }
2644
2645 if (!fcb) {
2646 ERR("no fcb\n");
2647 Status = STATUS_INVALID_PARAMETER;
2648 goto end;
2649 }
2650
2651 if (!ccb) {
2652 ERR("no ccb\n");
2653 Status = STATUS_INVALID_PARAMETER;
2654 goto end;
2655 }
2656
2657 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
2658 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
2659 Status = STATUS_ACCESS_DENIED;
2660 goto end;
2661 }
2662
2663 Status = STATUS_NOT_IMPLEMENTED;
2664
2665 TRACE("set information\n");
2666
2667 switch (IrpSp->Parameters.SetFile.FileInformationClass) {
2668 case FileAllocationInformation:
2669 {
2670 TRACE("FileAllocationInformation\n");
2671
2672 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
2673 WARN("insufficient privileges\n");
2674 Status = STATUS_ACCESS_DENIED;
2675 break;
2676 }
2677
2678 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE);
2679 break;
2680 }
2681
2682 case FileBasicInformation:
2683 {
2684 TRACE("FileBasicInformation\n");
2685
2686 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
2687 WARN("insufficient privileges\n");
2688 Status = STATUS_ACCESS_DENIED;
2689 break;
2690 }
2691
2692 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
2693
2694 break;
2695 }
2696
2697 case FileDispositionInformation:
2698 {
2699 TRACE("FileDispositionInformation\n");
2700
2701 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
2702 WARN("insufficient privileges\n");
2703 Status = STATUS_ACCESS_DENIED;
2704 break;
2705 }
2706
2707 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
2708
2709 break;
2710 }
2711
2712 case FileEndOfFileInformation:
2713 {
2714 TRACE("FileEndOfFileInformation\n");
2715
2716 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2717 WARN("insufficient privileges\n");
2718 Status = STATUS_ACCESS_DENIED;
2719 break;
2720 }
2721
2722 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE);
2723
2724 break;
2725 }
2726
2727 case FileLinkInformation:
2728 TRACE("FileLinkInformation\n");
2729 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2730 break;
2731
2732 case FilePositionInformation:
2733 TRACE("FilePositionInformation\n");
2734 Status = set_position_information(IrpSp->FileObject, Irp);
2735 break;
2736
2737 case FileRenameInformation:
2738 TRACE("FileRenameInformation\n");
2739 // FIXME - make this work with streams
2740 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
2741 break;
2742
2743 case FileValidDataLengthInformation:
2744 {
2745 TRACE("FileValidDataLengthInformation\n");
2746
2747 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
2748 WARN("insufficient privileges\n");
2749 Status = STATUS_ACCESS_DENIED;
2750 break;
2751 }
2752
2753 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
2754
2755 break;
2756 }
2757
2758 default:
2759 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
2760 }
2761
2762 end:
2763 Irp->IoStatus.Status = Status;
2764
2765 TRACE("returning %08x\n", Status);
2766
2767 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2768
2769 if (top_level)
2770 IoSetTopLevelIrp(NULL);
2771
2772 FsRtlExitFileSystem();
2773
2774 return Status;
2775 }
2776
2777 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
2778 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
2779
2780 *length -= sizeof(FILE_BASIC_INFORMATION);
2781
2782 if (fcb == fcb->Vcb->dummy_fcb) {
2783 LARGE_INTEGER time;
2784
2785 KeQuerySystemTime(&time);
2786 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
2787 } else {
2788 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2789 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2790 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2791 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2792 }
2793
2794 if (fcb->ads) {
2795 if (!fileref || !fileref->parent) {
2796 ERR("no fileref for stream\n");
2797 return STATUS_INTERNAL_ERROR;
2798 } else
2799 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2800 } else
2801 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2802
2803 return STATUS_SUCCESS;
2804 }
2805
2806 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
2807 INODE_ITEM* ii;
2808
2809 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
2810 WARN("overflow\n");
2811 return STATUS_BUFFER_OVERFLOW;
2812 }
2813
2814 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
2815
2816 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
2817
2818 if (fcb->ads) {
2819 if (!fileref || !fileref->parent) {
2820 ERR("no fileref for stream\n");
2821 return STATUS_INTERNAL_ERROR;
2822 }
2823
2824 ii = &fileref->parent->fcb->inode_item;
2825 } else
2826 ii = &fcb->inode_item;
2827
2828 if (fcb == fcb->Vcb->dummy_fcb) {
2829 LARGE_INTEGER time;
2830
2831 KeQuerySystemTime(&time);
2832 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
2833 } else {
2834 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
2835 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
2836 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
2837 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
2838 }
2839
2840 if (fcb->ads) {
2841 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
2842 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
2843 } else {
2844 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2845 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2846 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
2847 }
2848
2849 return STATUS_SUCCESS;
2850 }
2851
2852 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
2853 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
2854
2855 *length -= sizeof(FILE_STANDARD_INFORMATION);
2856
2857 if (fcb->ads) {
2858 if (!fileref || !fileref->parent) {
2859 ERR("no fileref for stream\n");
2860 return STATUS_INTERNAL_ERROR;
2861 }
2862
2863 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
2864 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
2865 fsi->Directory = FALSE;
2866 } else {
2867 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
2868 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
2869 fsi->NumberOfLinks = fcb->inode_item.st_nlink;
2870 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
2871 }
2872
2873 TRACE("length = %llu\n", fsi->EndOfFile.QuadPart);
2874
2875 fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE;
2876
2877 return STATUS_SUCCESS;
2878 }
2879
2880 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
2881 *length -= sizeof(FILE_INTERNAL_INFORMATION);
2882
2883 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
2884
2885 return STATUS_SUCCESS;
2886 }
2887
2888 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
2889 *length -= sizeof(FILE_EA_INFORMATION);
2890
2891 /* This value appears to be the size of the structure NTFS stores on disk, and not,
2892 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
2893 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
2894 * item. */
2895
2896 eai->EaSize = fcb->ealen;
2897
2898 return STATUS_SUCCESS;
2899 }
2900
2901 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
2902 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
2903
2904 *length -= sizeof(FILE_POSITION_INFORMATION);
2905
2906 fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
2907
2908 return STATUS_SUCCESS;
2909 }
2910
2911 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
2912 file_ref* fr;
2913 NTSTATUS Status;
2914 ULONG reqlen = 0;
2915 USHORT offset;
2916 BOOL overflow = FALSE;
2917
2918 // FIXME - we need a lock on filerefs' filepart
2919
2920 if (fileref == fileref->fcb->Vcb->root_fileref) {
2921 if (fn->MaximumLength >= sizeof(WCHAR)) {
2922 fn->Buffer[0] = '\\';
2923 fn->Length = sizeof(WCHAR);
2924
2925 if (name_offset)
2926 *name_offset = 0;
2927
2928 return STATUS_SUCCESS;
2929 } else {
2930 if (preqlen)
2931 *preqlen = sizeof(WCHAR);
2932 fn->Length = 0;
2933
2934 return STATUS_BUFFER_OVERFLOW;
2935 }
2936 }
2937
2938 fr = fileref;
2939 offset = 0;
2940
2941 while (fr->parent) {
2942 USHORT movelen;
2943
2944 if (!fr->dc)
2945 return STATUS_INTERNAL_ERROR;
2946
2947 if (!overflow) {
2948 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
2949 overflow = TRUE;
2950 }
2951
2952 if (overflow)
2953 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
2954 else
2955 movelen = fn->Length;
2956
2957 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
2958 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
2959 offset += fr->dc->name.Length + sizeof(WCHAR);
2960 }
2961
2962 if (fn->MaximumLength >= sizeof(WCHAR)) {
2963 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
2964 fn->Length += sizeof(WCHAR);
2965
2966 if (fn->MaximumLength > sizeof(WCHAR)) {
2967 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
2968 fn->Length += fr->dc->name.Length;
2969 }
2970
2971 if (fn->Length > fn->MaximumLength) {
2972 fn->Length = fn->MaximumLength;
2973 overflow = TRUE;
2974 }
2975 }
2976
2977 reqlen += sizeof(WCHAR) + fr->dc->name.Length;
2978
2979 fr = fr->parent;
2980 }
2981
2982 offset += sizeof(WCHAR);
2983
2984 if (overflow) {
2985 if (preqlen)
2986 *preqlen = reqlen;
2987 Status = STATUS_BUFFER_OVERFLOW;
2988 } else {
2989 if (name_offset)
2990 *name_offset = offset;
2991
2992 Status = STATUS_SUCCESS;
2993 }
2994
2995 return Status;
2996 }
2997
2998 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
2999 ULONG reqlen;
3000 UNICODE_STRING fn;
3001 NTSTATUS Status;
3002 static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
3003 UINT16 datasuflen = sizeof(datasuf) - sizeof(WCHAR);
3004
3005 if (!fileref) {
3006 ERR("called without fileref\n");
3007 return STATUS_INVALID_PARAMETER;
3008 }
3009
3010 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
3011
3012 TRACE("maximum length is %u\n", *length);
3013 fni->FileNameLength = 0;
3014
3015 fni->FileName[0] = 0;
3016
3017 fn.Buffer = fni->FileName;
3018 fn.Length = 0;
3019 fn.MaximumLength = (UINT16)*length;
3020
3021 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
3022 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
3023 ERR("fileref_get_filename returned %08x\n", Status);
3024 return Status;
3025 }
3026
3027 if (fcb->ads) {
3028 if (Status == STATUS_BUFFER_OVERFLOW)
3029 reqlen += datasuflen;
3030 else {
3031 if (fn.Length + datasuflen > fn.MaximumLength) {
3032 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
3033 reqlen += datasuflen;
3034 Status = STATUS_BUFFER_OVERFLOW;
3035 } else {
3036 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
3037 fn.Length += datasuflen;
3038 }
3039 }
3040 }
3041
3042 if (Status == STATUS_BUFFER_OVERFLOW) {
3043 *length = -1;
3044 fni->FileNameLength = reqlen;
3045 TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3046 } else {
3047 *length -= fn.Length;
3048 fni->FileNameLength = fn.Length;
3049 TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
3050 }
3051
3052 return Status;
3053 }
3054
3055 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
3056 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
3057
3058 if (fcb->ads) {
3059 if (!ccb->fileref || !ccb->fileref->parent) {
3060 ERR("no fileref for stream\n");
3061 return STATUS_INTERNAL_ERROR;
3062 }
3063
3064 ati->FileAttributes = ccb->fileref->parent->fcb->atts;
3065 } else
3066 ati->FileAttributes = fcb->atts;
3067
3068 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3069 ati->ReparseTag = 0;
3070 else
3071 ati->ReparseTag = get_reparse_tag_fcb(fcb);
3072
3073 return STATUS_SUCCESS;
3074 }
3075
3076 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
3077 LONG reqsize;
3078 LIST_ENTRY* le;
3079 FILE_STREAM_INFORMATION *entry, *lastentry;
3080 NTSTATUS Status;
3081
3082 static const WCHAR datasuf[] = L":$DATA";
3083 UNICODE_STRING suf;
3084
3085 if (!fileref) {
3086 ERR("fileref was NULL\n");
3087 return STATUS_INVALID_PARAMETER;
3088 }
3089
3090 suf.Buffer = (WCHAR*)datasuf;
3091 suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3092
3093 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
3094 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
3095 else
3096 reqsize = 0;
3097
3098 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
3099
3100 le = fileref->fcb->dir_children_index.Flink;
3101 while (le != &fileref->fcb->dir_children_index) {
3102 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3103
3104 if (dc->index == 0) {
3105 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
3106 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
3107 } else
3108 break;
3109
3110 le = le->Flink;
3111 }
3112
3113 TRACE("length = %i, reqsize = %u\n", *length, reqsize);
3114
3115 if (reqsize > *length) {
3116 Status = STATUS_BUFFER_OVERFLOW;
3117 goto end;
3118 }
3119
3120 entry = fsi;
3121 lastentry = NULL;
3122
3123 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
3124 ULONG off;
3125
3126 entry->NextEntryOffset = 0;
3127 entry->StreamNameLength = suf.Length + sizeof(WCHAR);
3128 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
3129 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
3130
3131 entry->StreamName[0] = ':';
3132 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
3133
3134 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
3135
3136 lastentry = entry;
3137 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3138 }
3139
3140 le = fileref->fcb->dir_children_index.Flink;
3141 while (le != &fileref->fcb->dir_children_index) {
3142 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3143
3144 if (dc->index == 0) {
3145 ULONG off;
3146
3147 entry->NextEntryOffset = 0;
3148 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
3149
3150 if (dc->fileref)
3151 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
3152 else
3153 entry->StreamSize.QuadPart = dc->size;
3154
3155 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
3156
3157 entry->StreamName[0] = ':';
3158
3159 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
3160 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
3161
3162 if (lastentry)
3163 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry);
3164
3165 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
3166
3167 lastentry = entry;
3168 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
3169 } else
3170 break;
3171
3172 le = le->Flink;
3173 }
3174
3175 *length -= reqsize;
3176
3177 Status = STATUS_SUCCESS;
3178
3179 end:
3180 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
3181
3182 return Status;
3183 }
3184
3185 #ifndef __REACTOS__
3186 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
3187 TRACE("FileStandardLinkInformation\n");
3188
3189 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
3190
3191 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
3192 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
3193 fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
3194 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE;
3195
3196 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
3197
3198 return STATUS_SUCCESS;
3199 }
3200
3201 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
3202 NTSTATUS Status;
3203 LIST_ENTRY* le;
3204 LONG bytes_needed;
3205 FILE_LINK_ENTRY_INFORMATION* feli;
3206 BOOL overflow = FALSE;
3207 fcb* fcb = fileref->fcb;
3208 ULONG len;
3209
3210 if (fcb->ads)
3211 return STATUS_INVALID_PARAMETER;
3212
3213 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
3214 return STATUS_INVALID_PARAMETER;
3215
3216 RtlZeroMemory(fli, *length);
3217
3218 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
3219 len = bytes_needed;
3220 feli = NULL;
3221
3222 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
3223
3224 if (fcb->inode == SUBVOL_ROOT_INODE) {
3225 ULONG namelen;
3226
3227 if (fcb == fcb->Vcb->root_fileref->fcb)
3228 namelen = sizeof(WCHAR);
3229 else
3230 namelen = fileref->dc->name.Length;
3231
3232 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
3233
3234 if (bytes_needed > *length)
3235 overflow = TRUE;
3236
3237 if (!overflow) {
3238 feli = &fli->Entry;
3239
3240 feli->NextEntryOffset = 0;
3241 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
3242
3243 if (fcb == fcb->Vcb->root_fileref->fcb) {
3244 feli->FileNameLength = 1;
3245 feli->FileName[0] = '.';
3246 } else {
3247 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3248 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3249 }
3250
3251 fli->EntriesReturned++;
3252
3253 len = bytes_needed;
3254 }
3255 } else {
3256 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
3257
3258 if (IsListEmpty(&fcb->hardlinks)) {
3259 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
3260
3261 if (bytes_needed > *length)
3262 overflow = TRUE;
3263
3264 if (!overflow) {
3265 feli = &fli->Entry;
3266
3267 feli->NextEntryOffset = 0;
3268 feli->ParentFileId = fileref->parent->fcb->inode;
3269 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
3270 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
3271
3272 fli->EntriesReturned++;
3273
3274 len = bytes_needed;
3275 }
3276 } else {
3277 le = fcb->hardlinks.Flink;
3278 while (le != &fcb->hardlinks) {
3279 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
3280 file_ref* parfr;
3281
3282 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
3283
3284 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
3285
3286 if (!NT_SUCCESS(Status)) {
3287 ERR("open_fileref_by_inode returned %08x\n", Status);
3288 } else if (!parfr->deleted) {
3289 LIST_ENTRY* le2;
3290 BOOL found = FALSE, deleted = FALSE;
3291 UNICODE_STRING* fn = NULL;
3292
3293 le2 = parfr->children.Flink;
3294 while (le2 != &parfr->children) {
3295 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
3296
3297 if (fr2->dc->index == hl->index) {
3298 found = TRUE;
3299 deleted = fr2->deleted;
3300
3301 if (!deleted)
3302 fn = &fr2->dc->name;
3303
3304 break;
3305 }
3306
3307 le2 = le2->Flink;
3308 }
3309
3310 if (!found)
3311 fn = &hl->name;
3312
3313 if (!deleted) {
3314 TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
3315
3316 if (feli)
3317 bytes_needed = (LONG)sector_align(bytes_needed, 8);
3318
3319 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
3320
3321 if (bytes_needed > *length)
3322 overflow = TRUE;
3323
3324 if (!overflow) {
3325 if (feli) {
3326 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
3327 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset);
3328 } else
3329 feli = &fli->Entry;
3330
3331 feli->NextEntryOffset = 0;
3332 feli->ParentFileId = parfr->fcb->inode;
3333 feli->FileNameLength = fn->Length / sizeof(WCHAR);
3334 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
3335
3336 fli->EntriesReturned++;
3337
3338 len = bytes_needed;
3339 }
3340 }
3341
3342 free_fileref(parfr);
3343 }
3344
3345 le = le->Flink;
3346 }
3347 }
3348
3349 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
3350 }
3351
3352 fli->BytesNeeded = bytes_needed;
3353
3354 *length -= len;
3355
3356 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
3357
3358 ExReleaseResourceLite(fcb->Header.Resource);
3359
3360 return Status;
3361 }
3362 #endif /* __REACTOS__ */
3363
3364 #if (NTDDI_VERSION >= NTDDI_WIN10)
3365 #ifdef __MINGW32__
3366 typedef struct _FILE_ID_128 {
3367 UCHAR Identifier[16];
3368 } FILE_ID_128, *PFILE_ID_128;
3369
3370 typedef struct _FILE_ID_INFORMATION {
3371 ULONGLONG VolumeSerialNumber;
3372 FILE_ID_128 FileId;
3373 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
3374 #endif
3375
3376 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
3377 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64));
3378 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
3379 RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
3380
3381 *length -= sizeof(FILE_ID_INFORMATION);
3382
3383 return STATUS_SUCCESS;
3384 }
3385 #endif
3386
3387 #ifndef __REACTOS__
3388 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
3389 INODE_ITEM* ii;
3390
3391 fsli->FileId.LowPart = (UINT32)fcb->inode;
3392 fsli->FileId.HighPart = (UINT32)fcb->subvol->id;
3393
3394 if (fcb->ads)
3395 ii = &ccb->fileref->parent->fcb->inode_item;
3396 else
3397 ii = &fcb->inode_item;
3398
3399 if (fcb == fcb->Vcb->dummy_fcb) {
3400 LARGE_INTEGER time;
3401
3402 KeQuerySystemTime(&time);
3403 fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
3404 } else {
3405 fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
3406 fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
3407 fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
3408 fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
3409 }
3410
3411 if (fcb->ads) {
3412 fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
3413 fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
3414 } else {
3415 fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
3416 fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
3417 fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
3418 }
3419
3420 if (fcb->type == BTRFS_TYPE_SOCKET)
3421 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
3422 else if (fcb->type == BTRFS_TYPE_FIFO)
3423 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
3424 else if (fcb->type == BTRFS_TYPE_CHARDEV)
3425 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
3426 else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
3427 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
3428 else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
3429 fsli->ReparseTag = 0;
3430 else
3431 fsli->ReparseTag = get_reparse_tag_fcb(fcb);
3432
3433 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
3434 fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
3435
3436 if (fcb->ads)
3437 fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
3438 else
3439 fsli->NumberOfLinks = fcb->inode_item.st_nlink;
3440
3441 fsli->EffectiveAccess = ccb->access;
3442 fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME - LX_FILE_CASE_SENSITIVE_DIR
3443 fsli->LxUid = ii->st_uid;
3444 fsli->LxGid = ii->st_gid;
3445 fsli->LxMode = ii->st_mode;
3446
3447 if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
3448 fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
3449 fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
3450 } else {
3451 fsli->LxDeviceIdMajor = 0;
3452 fsli->LxDeviceIdMinor = 0;
3453 }
3454
3455 *length -= sizeof(FILE_STAT_LX_INFORMATION);
3456
3457 return STATUS_SUCCESS;
3458 }
3459 #endif
3460
3461 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
3462 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3463 LONG length = IrpSp->Parameters.QueryFile.Length;
3464 fcb* fcb = FileObject->FsContext;
3465 ccb* ccb = FileObject->FsContext2;
3466 file_ref* fileref = ccb ? ccb->fileref : NULL;
3467 NTSTATUS Status;
3468
3469 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
3470 TRACE("fcb = %p\n", fcb);
3471
3472 if (fcb == Vcb->volume_fcb)
3473 return STATUS_INVALID_PARAMETER;
3474
3475 if (!ccb) {
3476 ERR("ccb is NULL\n");
3477 return STATUS_INVALID_PARAMETER;
3478 }
3479
3480 switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
3481 case FileAllInformation:
3482 {
3483 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
3484 INODE_ITEM* ii;
3485
3486 TRACE("FileAllInformation\n");
3487
3488 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3489 WARN("insufficient privileges\n");
3490 Status = STATUS_ACCESS_DENIED;
3491 goto exit;
3492 }
3493
3494 if (fcb->ads) {
3495 if (!fileref || !fileref->parent) {
3496 ERR("no fileref for stream\n");
3497 Status = STATUS_INTERNAL_ERROR;
3498 goto exit;
3499 }
3500
3501 ii = &fileref->parent->fcb->inode_item;
3502 } else
3503 ii = &fcb->inode_item;
3504
3505 // Access, mode, and alignment are all filled in by the kernel
3506
3507 if (length > 0)
3508 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
3509
3510 if (length > 0)
3511 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
3512
3513 if (length > 0)
3514 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
3515
3516 if (length > 0)
3517 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
3518
3519 length -= sizeof(FILE_ACCESS_INFORMATION);
3520
3521 if (length > 0)
3522 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
3523
3524 length -= sizeof(FILE_MODE_INFORMATION);
3525
3526 length -= sizeof(FILE_ALIGNMENT_INFORMATION);
3527
3528 if (length > 0)
3529 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
3530
3531 Status = STATUS_SUCCESS;
3532
3533 break;
3534 }
3535
3536 case FileAttributeTagInformation:
3537 {
3538 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
3539
3540 TRACE("FileAttributeTagInformation\n");
3541
3542 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3543 WARN("insufficient privileges\n");
3544 Status = STATUS_ACCESS_DENIED;
3545 goto exit;
3546 }
3547
3548 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3549 Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
3550 ExReleaseResourceLite(&Vcb->tree_lock);
3551
3552 break;
3553 }
3554
3555 case FileBasicInformation:
3556 {
3557 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
3558 INODE_ITEM* ii;
3559
3560 TRACE("FileBasicInformation\n");
3561
3562 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3563 WARN("insufficient privileges\n");
3564 Status = STATUS_ACCESS_DENIED;
3565 goto exit;
3566 }
3567
3568 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
3569 WARN("overflow\n");
3570 Status = STATUS_BUFFER_OVERFLOW;
3571 goto exit;
3572 }
3573
3574 if (fcb->ads) {
3575 if (!fileref || !fileref->parent) {
3576 ERR("no fileref for stream\n");
3577 Status = STATUS_INTERNAL_ERROR;
3578 goto exit;
3579 }
3580
3581 ii = &fileref->parent->fcb->inode_item;
3582 } else
3583 ii = &fcb->inode_item;
3584
3585 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
3586 break;
3587 }
3588
3589 case FileCompressionInformation:
3590 FIXME("STUB: FileCompressionInformation\n");
3591 Status = STATUS_INVALID_PARAMETER;
3592 goto exit;
3593
3594 case FileEaInformation:
3595 {
3596 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
3597
3598 TRACE("FileEaInformation\n");
3599
3600 Status = fill_in_file_ea_information(eai, fcb, &length);
3601
3602 break;
3603 }
3604
3605 case FileInternalInformation:
3606 {
3607 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3608
3609 TRACE("FileInternalInformation\n");
3610
3611 Status = fill_in_file_internal_information(fii, fcb, &length);
3612
3613 break;
3614 }
3615
3616 case FileNameInformation:
3617 {
3618 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3619
3620 TRACE("FileNameInformation\n");
3621
3622 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3623
3624 break;
3625 }
3626
3627 case FileNetworkOpenInformation:
3628 {
3629 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
3630
3631 TRACE("FileNetworkOpenInformation\n");
3632
3633 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
3634 WARN("insufficient privileges\n");
3635 Status = STATUS_ACCESS_DENIED;
3636 goto exit;
3637 }
3638
3639 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
3640
3641 break;
3642 }
3643
3644 case FilePositionInformation:
3645 {
3646 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
3647
3648 TRACE("FilePositionInformation\n");
3649
3650 Status = fill_in_file_position_information(fpi, FileObject, &length);
3651
3652 break;
3653 }
3654
3655 case FileStandardInformation:
3656 {
3657 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3658
3659 TRACE("FileStandardInformation\n");
3660
3661 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
3662 WARN("overflow\n");
3663 Status = STATUS_BUFFER_OVERFLOW;
3664 goto exit;
3665 }
3666
3667 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
3668
3669 break;
3670 }
3671
3672 case FileStreamInformation:
3673 {
3674 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
3675
3676 TRACE("FileStreamInformation\n");
3677
3678 Status = fill_in_file_stream_information(fsi, fileref, &length);
3679
3680 break;
3681 }
3682
3683 #if (NTDDI_VERSION >= NTDDI_VISTA)
3684 case FileHardLinkInformation:
3685 {
3686 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
3687
3688 TRACE("FileHardLinkInformation\n");
3689
3690 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
3691 Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
3692 ExReleaseResourceLite(&Vcb->tree_lock);
3693
3694 break;
3695 }
3696
3697 case FileNormalizedNameInformation:
3698 {
3699 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
3700
3701 TRACE("FileNormalizedNameInformation\n");
3702
3703 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
3704
3705 break;
3706 }
3707 #endif
3708
3709 #if (NTDDI_VERSION >= NTDDI_WIN7)
3710 case FileStandardLinkInformation:
3711 {
3712 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
3713
3714 TRACE("FileStandardLinkInformation\n");
3715
3716 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
3717
3718 break;
3719 }
3720
3721 case FileRemoteProtocolInformation:
3722 TRACE("FileRemoteProtocolInformation\n");
3723 Status = STATUS_INVALID_PARAMETER;
3724 goto exit;
3725 #endif
3726
3727 #if (NTDDI_VERSION >= NTDDI_WIN10)
3728 #ifndef _MSC_VER
3729 #pragma GCC diagnostic push
3730 #pragma GCC diagnostic ignored "-Wswitch"
3731 #endif
3732 case FileIdInformation:
3733 {
3734 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
3735
3736 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
3737 WARN("overflow\n");
3738 Status = STATUS_BUFFER_OVERFLOW;
3739 goto exit;
3740 }
3741
3742 TRACE("FileIdInformation\n");
3743
3744 Status = fill_in_file_id_information(fii, fcb, &length);
3745
3746 break;
3747 }
3748
3749 case FileStatLxInformation:
3750 {
3751 FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
3752
3753 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
3754 WARN("overflow\n");
3755 Status = STATUS_BUFFER_OVERFLOW;
3756 goto exit;
3757 }
3758
3759 TRACE("FileStatLxInformation\n");
3760
3761 Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
3762
3763 break;
3764 }
3765 #ifndef _MSC_VER
3766 #pragma GCC diagnostic pop
3767 #endif
3768 #endif
3769
3770 default:
3771 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
3772 Status = STATUS_INVALID_PARAMETER;
3773 goto exit;
3774 }
3775
3776 if (length < 0) {
3777 length = 0;
3778 Status = STATUS_BUFFER_OVERFLOW;
3779 }
3780
3781 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
3782
3783 exit:
3784 TRACE("query_info returning %08x\n", Status);
3785
3786 return Status;
3787 }
3788
3789 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
3790 _Function_class_(DRIVER_DISPATCH)
3791 NTSTATUS NTAPI drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3792 PIO_STACK_LOCATION IrpSp;
3793 NTSTATUS Status;
3794 fcb* fcb;
3795 device_extension* Vcb = DeviceObject->DeviceExtension;
3796 BOOL top_level;
3797
3798 FsRtlEnterFileSystem();
3799
3800 top_level = is_top_level(Irp);
3801
3802 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3803 Status = vol_query_information(DeviceObject, Irp);
3804 goto end;
3805 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3806 Status = STATUS_INVALID_PARAMETER;
3807 goto end;
3808 }
3809
3810 Irp->IoStatus.Information = 0;
3811
3812 TRACE("query information\n");
3813
3814 IrpSp = IoGetCurrentIrpStackLocation(Irp);
3815
3816 fcb = IrpSp->FileObject->FsContext;
3817 TRACE("fcb = %p\n", fcb);
3818 TRACE("fcb->subvol = %p\n", fcb->subvol);
3819
3820 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
3821
3822 end:
3823 TRACE("returning %08x\n", Status);
3824
3825 Irp->IoStatus.Status = Status;
3826
3827 IoCompleteRequest( Irp, IO_NO_INCREMENT );
3828
3829 if (top_level)
3830 IoSetTopLevelIrp(NULL);
3831
3832 FsRtlExitFileSystem();
3833
3834 return Status;
3835 }
3836
3837 _Dispatch_type_(IRP_MJ_QUERY_EA)
3838 _Function_class_(DRIVER_DISPATCH)
3839 NTSTATUS NTAPI drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3840 NTSTATUS Status;
3841 BOOL top_level;
3842 device_extension* Vcb = DeviceObject->DeviceExtension;
3843 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3844 PFILE_OBJECT FileObject = IrpSp->FileObject;
3845 fcb* fcb;
3846 ccb* ccb;
3847 FILE_FULL_EA_INFORMATION* ffei;
3848 ULONG retlen = 0;
3849
3850 FsRtlEnterFileSystem();
3851
3852 TRACE("(%p, %p)\n", DeviceObject, Irp);
3853
3854 top_level = is_top_level(Irp);
3855
3856 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3857 Status = vol_query_ea(DeviceObject, Irp);
3858 goto end;
3859 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3860 Status = STATUS_INVALID_PARAMETER;
3861 goto end;
3862 }
3863
3864 ffei = map_user_buffer(Irp, NormalPagePriority);
3865 if (!ffei) {
3866 ERR("could not get output buffer\n");
3867 Status = STATUS_INVALID_PARAMETER;
3868 goto end;
3869 }
3870
3871 if (!FileObject) {
3872 ERR("no file object\n");
3873 Status = STATUS_INVALID_PARAMETER;
3874 goto end;
3875 }
3876
3877 fcb = FileObject->FsContext;
3878
3879 if (!fcb) {
3880 ERR("no fcb\n");
3881 Status = STATUS_INVALID_PARAMETER;
3882 goto end;
3883 }
3884
3885 ccb = FileObject->FsContext2;
3886
3887 if (!ccb) {
3888 ERR("no ccb\n");
3889 Status = STATUS_INVALID_PARAMETER;
3890 goto end;
3891 }
3892
3893 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
3894 WARN("insufficient privileges\n");
3895 Status = STATUS_ACCESS_DENIED;
3896 goto end;
3897 }
3898
3899 if (fcb->ads)
3900 fcb = ccb->fileref->parent->fcb;
3901
3902 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
3903
3904 Status = STATUS_SUCCESS;
3905
3906 if (fcb->ea_xattr.Length == 0)
3907 goto end2;
3908
3909 if (IrpSp->Parameters.QueryEa.EaList) {
3910 FILE_FULL_EA_INFORMATION *ea, *out;
3911 FILE_GET_EA_INFORMATION* in;
3912
3913 in = IrpSp->Parameters.QueryEa.EaList;
3914 do {
3915 STRING s;
3916
3917 s.Length = s.MaximumLength = in->EaNameLength;
3918 s.Buffer = in->EaName;
3919
3920 RtlUpperString(&s, &s);
3921
3922 if (in->NextEntryOffset == 0)
3923 break;
3924
3925 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
3926 } while (TRUE);
3927
3928 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
3929 out = NULL;
3930
3931 do {
3932 BOOL found = FALSE;
3933
3934 in = IrpSp->Parameters.QueryEa.EaList;
3935 do {
3936 if (in->EaNameLength == ea->EaNameLength &&
3937 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
3938 found = TRUE;
3939 break;
3940 }
3941
3942 if (in->NextEntryOffset == 0)
3943 break;
3944
3945 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset);
3946 } while (TRUE);
3947
3948 if (found) {
3949 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
3950
3951 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
3952 Status = STATUS_BUFFER_OVERFLOW;
3953 retlen = 0;
3954 goto end2;
3955 }
3956
3957 retlen += padding;
3958
3959 if (out) {
3960 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
3961 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
3962 } else
3963 out = ffei;
3964
3965 out->NextEntryOffset = 0;
3966 out->Flags = ea->Flags;
3967 out->EaNameLength = ea->EaNameLength;
3968 out->EaValueLength = ea->EaValueLength;
3969 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
3970
3971 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
3972
3973 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
3974 break;
3975 }
3976
3977 if (ea->NextEntryOffset == 0)
3978 break;
3979
3980 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
3981 } while (TRUE);
3982 } else {
3983 FILE_FULL_EA_INFORMATION *ea, *out;
3984 ULONG index;
3985
3986 if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
3987 // The index is 1-based
3988 if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
3989 Status = STATUS_NONEXISTENT_EA_ENTRY;
3990 goto end2;
3991 } else
3992 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
3993 } else if (IrpSp->Flags & SL_RESTART_SCAN)
3994 index = ccb->ea_index = 0;
3995 else
3996 index = ccb->ea_index;
3997
3998 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
3999
4000 if (index > 0) {
4001 ULONG i;
4002
4003 for (i = 0; i < index; i++) {
4004 if (ea->NextEntryOffset == 0) // last item
4005 goto end2;
4006
4007 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4008 }
4009 }
4010
4011 out = NULL;
4012
4013 do {
4014 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
4015
4016 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
4017 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
4018 goto end2;
4019 }
4020
4021 retlen += padding;
4022
4023 if (out) {
4024 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
4025 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset);
4026 } else
4027 out = ffei;
4028
4029 out->NextEntryOffset = 0;
4030 out->Flags = ea->Flags;
4031 out->EaNameLength = ea->EaNameLength;
4032 out->EaValueLength = ea->EaValueLength;
4033 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
4034
4035 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
4036
4037 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
4038 ccb->ea_index++;
4039
4040 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
4041 break;
4042
4043 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4044 } while (TRUE);
4045 }
4046
4047 end2:
4048 ExReleaseResourceLite(fcb->Header.Resource);
4049
4050 end:
4051 TRACE("returning %08x\n", Status);
4052
4053 Irp->IoStatus.Status = Status;
4054 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
4055
4056 IoCompleteRequest( Irp, IO_NO_INCREMENT );
4057
4058 if (top_level)
4059 IoSetTopLevelIrp(NULL);
4060
4061 FsRtlExitFileSystem();
4062
4063 return Status;
4064 }
4065
4066 _Dispatch_type_(IRP_MJ_SET_EA)
4067 _Function_class_(DRIVER_DISPATCH)
4068 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4069 device_extension* Vcb = DeviceObject->DeviceExtension;
4070 NTSTATUS Status;
4071 BOOL top_level;
4072 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4073 PFILE_OBJECT FileObject = IrpSp->FileObject;
4074 fcb* fcb;
4075 ccb* ccb;
4076 file_ref* fileref;
4077 FILE_FULL_EA_INFORMATION* ffei;
4078 ULONG offset;
4079 LIST_ENTRY ealist;
4080 ea_item* item;
4081 FILE_FULL_EA_INFORMATION* ea;
4082 LIST_ENTRY* le;
4083 LARGE_INTEGER time;
4084 BTRFS_TIME now;
4085
4086 FsRtlEnterFileSystem();
4087
4088 TRACE("(%p, %p)\n", DeviceObject, Irp);
4089
4090 top_level = is_top_level(Irp);
4091
4092 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4093 Status = vol_set_ea(DeviceObject, Irp);
4094 goto end;
4095 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4096 Status = STATUS_INVALID_PARAMETER;
4097 goto end;
4098 }
4099
4100 if (Vcb->readonly) {
4101 Status = STATUS_MEDIA_WRITE_PROTECTED;
4102 goto end;
4103 }
4104
4105 ffei = map_user_buffer(Irp, NormalPagePriority);
4106 if (!ffei) {
4107 ERR("could not get output buffer\n");
4108 Status = STATUS_INVALID_PARAMETER;
4109 goto end;
4110 }
4111
4112 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
4113 if (!NT_SUCCESS(Status)) {
4114 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
4115 goto end;
4116 }
4117
4118 if (!FileObject) {
4119 ERR("no file object\n");
4120 Status = STATUS_INVALID_PARAMETER;
4121 goto end;
4122 }
4123
4124 fcb = FileObject->FsContext;
4125
4126 if (!fcb) {
4127 ERR("no fcb\n");
4128 Status = STATUS_INVALID_PARAMETER;
4129 goto end;
4130 }
4131
4132 ccb = FileObject->FsContext2;
4133
4134 if (!ccb) {
4135 ERR("no ccb\n");
4136 Status = STATUS_INVALID_PARAMETER;
4137 goto end;
4138 }
4139
4140 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
4141 WARN("insufficient privileges\n");
4142 Status = STATUS_ACCESS_DENIED;
4143 goto end;
4144 }
4145
4146 if (fcb->ads) {
4147 fileref = ccb->fileref->parent;
4148 fcb = fileref->fcb;
4149 } else
4150 fileref = ccb->fileref;
4151
4152 InitializeListHead(&ealist);
4153
4154 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4155
4156 if (fcb->ea_xattr.Length > 0) {
4157 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4158
4159 do {
4160 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4161 if (!item) {
4162 ERR("out of memory\n");
4163 Status = STATUS_INSUFFICIENT_RESOURCES;
4164 goto end2;
4165 }
4166
4167 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4168 item->name.Buffer = ea->EaName;
4169
4170 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4171 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4172
4173 item->flags = ea->Flags;
4174
4175 InsertTailList(&ealist, &item->list_entry);
4176
4177 if (ea->NextEntryOffset == 0)
4178 break;
4179
4180 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4181 } while (TRUE);
4182 }
4183
4184 ea = ffei;
4185
4186 do {
4187 STRING s;
4188 BOOL found = FALSE;
4189
4190 s.Length = s.MaximumLength = ea->EaNameLength;
4191 s.Buffer = ea->EaName;
4192
4193 RtlUpperString(&s, &s);
4194
4195 le = ealist.Flink;
4196 while (le != &ealist) {
4197 item = CONTAINING_RECORD(le, ea_item, list_entry);
4198
4199 if (item->name.Length == s.Length &&
4200 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
4201 item->flags = ea->Flags;
4202 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4203 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4204 found = TRUE;
4205 break;
4206 }
4207
4208 le = le->Flink;
4209 }
4210
4211 if (!found) {
4212 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
4213 if (!item) {
4214 ERR("out of memory\n");
4215 Status = STATUS_INSUFFICIENT_RESOURCES;
4216 goto end2;
4217 }
4218
4219 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
4220 item->name.Buffer = ea->EaName;
4221
4222 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
4223 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
4224
4225 item->flags = ea->Flags;
4226
4227 InsertTailList(&ealist, &item->list_entry);
4228 }
4229
4230 if (ea->NextEntryOffset == 0)
4231 break;
4232
4233 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4234 } while (TRUE);
4235
4236 // remove entries with zero-length value
4237 le = ealist.Flink;
4238 while (le != &ealist) {
4239 LIST_ENTRY* le2 = le->Flink;
4240
4241 item = CONTAINING_RECORD(le, ea_item, list_entry);
4242
4243 if (item->value.Length == 0) {
4244 RemoveEntryList(&item->list_entry);
4245 ExFreePool(item);
4246 }
4247
4248 le = le2;
4249 }
4250
4251 // handle LXSS values
4252 le = ealist.Flink;
4253 while (le != &ealist) {
4254 LIST_ENTRY* le2 = le->Flink;
4255
4256 item = CONTAINING_RECORD(le, ea_item, list_entry);
4257
4258 if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
4259 if (item->value.Length < sizeof(UINT32)) {
4260 ERR("uid value was shorter than expected\n");
4261 Status = STATUS_INVALID_PARAMETER;
4262 goto end2;
4263 }
4264
4265 if (Irp->RequestorMode == KernelMode) {
4266 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(UINT32));
4267 fcb->sd_dirty = TRUE;
4268 fcb->sd_deleted = FALSE;
4269 }
4270
4271 RemoveEntryList(&item->list_entry);
4272 ExFreePool(item);
4273 } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
4274 if (item->value.Length < sizeof(UINT32)) {
4275 ERR("gid value was shorter than expected\n");
4276 Status = STATUS_INVALID_PARAMETER;
4277 goto end2;
4278 }
4279
4280 if (Irp->RequestorMode == KernelMode)
4281 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(UINT32));
4282
4283 RemoveEntryList(&item->list_entry);
4284 ExFreePool(item);
4285 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
4286 if (item->value.Length < sizeof(UINT32)) {
4287 ERR("mode value was shorter than expected\n");
4288 Status = STATUS_INVALID_PARAMETER;
4289 goto end2;
4290 }
4291
4292 if (Irp->RequestorMode == KernelMode) {
4293 UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
4294 UINT32 val;
4295
4296 RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32));
4297
4298 fcb->inode_item.st_mode &= ~allowed;
4299 fcb->inode_item.st_mode |= val & allowed;
4300 }
4301
4302 RemoveEntryList(&item->list_entry);
4303 ExFreePool(item);
4304 }
4305
4306 le = le2;
4307 }
4308
4309 if (IsListEmpty(&ealist)) {
4310 fcb->ealen = 0;
4311
4312 if (fcb->ea_xattr.Buffer)
4313 ExFreePool(fcb->ea_xattr.Buffer);
4314
4315 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
4316 fcb->ea_xattr.Buffer = NULL;
4317 } else {
4318 UINT16 size = 0;
4319 char *buf, *oldbuf;
4320
4321 le = ealist.Flink;
4322 while (le != &ealist) {
4323 item = CONTAINING_RECORD(le, ea_item, list_entry);
4324
4325 if (size % 4 > 0)
4326 size += 4 - (size % 4);
4327
4328 size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
4329
4330 le = le->Flink;
4331 }
4332
4333 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
4334 if (!buf) {
4335 ERR("out of memory\n");
4336 Status = STATUS_INSUFFICIENT_RESOURCES;
4337 goto end2;
4338 }
4339
4340 oldbuf = fcb->ea_xattr.Buffer;
4341
4342 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
4343 fcb->ea_xattr.Buffer = buf;
4344
4345 fcb->ealen = 4;
4346 ea = NULL;
4347
4348 le = ealist.Flink;
4349 while (le != &ealist) {
4350 item = CONTAINING_RECORD(le, ea_item, list_entry);
4351
4352 if (ea) {
4353 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
4354
4355 if (ea->NextEntryOffset % 4 > 0)
4356 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
4357
4358 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
4359 } else
4360 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
4361
4362 ea->NextEntryOffset = 0;
4363 ea->Flags = item->flags;
4364 ea->EaNameLength = (UCHAR)item->name.Length;
4365 ea->EaValueLength = item->value.Length;
4366
4367 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
4368 ea->EaName[item->name.Length] = 0;
4369 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
4370
4371 fcb->ealen += 5 + item->name.Length + item->value.Length;
4372
4373 le = le->Flink;
4374 }
4375
4376 if (oldbuf)
4377 ExFreePool(oldbuf);
4378 }
4379
4380 fcb->ea_changed = TRUE;
4381
4382 KeQuerySystemTime(&time);
4383 win_time_to_unix(time, &now);
4384
4385 fcb->inode_item.transid = Vcb->superblock.generation;
4386 fcb->inode_item.sequence++;
4387
4388 if (!ccb->user_set_change_time)
4389 fcb->inode_item.st_ctime = now;
4390
4391 fcb->inode_item_changed = TRUE;
4392 mark_fcb_dirty(fcb);
4393
4394 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
4395
4396 Status = STATUS_SUCCESS;
4397
4398 end2:
4399 ExReleaseResourceLite(fcb->Header.Resource);
4400
4401 while (!IsListEmpty(&ealist)) {
4402 le = RemoveHeadList(&ealist);
4403
4404 item = CONTAINING_RECORD(le, ea_item, list_entry);
4405
4406 ExFreePool(item);
4407 }
4408
4409 end:
4410 TRACE("returning %08x\n", Status);
4411
4412 Irp->IoStatus.Status = Status;
4413 Irp->IoStatus.Information = 0;
4414
4415 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4416
4417 if (top_level)
4418 IoSetTopLevelIrp(NULL);
4419
4420 FsRtlExitFileSystem();
4421
4422 return Status;
4423 }