1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19 #include "crc32c.h"
20
21 #if (NTDDI_VERSION >= NTDDI_WIN10)
22 // not currently in mingw - introduced with Windows 10
23 #ifndef _MSC_VER
24 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
25 #define FileHardLinkFullIdInformation (enum _FILE_INFORMATION_CLASS)62
26 #define FileDispositionInformationEx (enum _FILE_INFORMATION_CLASS)64
27 #define FileRenameInformationEx (enum _FILE_INFORMATION_CLASS)65
28 #define FileStatInformation (enum _FILE_INFORMATION_CLASS)68
29 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
30 #define FileCaseSensitiveInformation (enum _FILE_INFORMATION_CLASS)71
31 #define FileLinkInformationEx (enum _FILE_INFORMATION_CLASS)72
32 #define FileStorageReserveIdInformation (enum _FILE_INFORMATION_CLASS)74
33
34 typedef struct _FILE_ID_INFORMATION {
35 ULONGLONG VolumeSerialNumber;
36 FILE_ID_128 FileId;
37 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
38
39 typedef struct _FILE_STAT_INFORMATION {
40 LARGE_INTEGER FileId;
41 LARGE_INTEGER CreationTime;
42 LARGE_INTEGER LastAccessTime;
43 LARGE_INTEGER LastWriteTime;
44 LARGE_INTEGER ChangeTime;
45 LARGE_INTEGER AllocationSize;
46 LARGE_INTEGER EndOfFile;
47 ULONG FileAttributes;
48 ULONG ReparseTag;
49 ULONG NumberOfLinks;
50 ACCESS_MASK EffectiveAccess;
51 } FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION;
52
53 typedef struct _FILE_STAT_LX_INFORMATION {
54 LARGE_INTEGER FileId;
55 LARGE_INTEGER CreationTime;
56 LARGE_INTEGER LastAccessTime;
57 LARGE_INTEGER LastWriteTime;
58 LARGE_INTEGER ChangeTime;
59 LARGE_INTEGER AllocationSize;
60 LARGE_INTEGER EndOfFile;
61 ULONG FileAttributes;
62 ULONG ReparseTag;
63 ULONG NumberOfLinks;
64 ACCESS_MASK EffectiveAccess;
65 ULONG LxFlags;
66 ULONG LxUid;
67 ULONG LxGid;
68 ULONG LxMode;
69 ULONG LxDeviceIdMajor;
70 ULONG LxDeviceIdMinor;
71 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
72
73 #define LX_FILE_METADATA_HAS_UID 0x01
74 #define LX_FILE_METADATA_HAS_GID 0x02
75 #define LX_FILE_METADATA_HAS_MODE 0x04
76 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08
77 #define LX_FILE_CASE_SENSITIVE_DIR 0x10
78
79 typedef struct _FILE_RENAME_INFORMATION_EX {
80 union {
81 BOOLEAN ReplaceIfExists;
82 ULONG Flags;
83 };
84 HANDLE RootDirectory;
85 ULONG FileNameLength;
86 WCHAR FileName[1];
87 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
88
89 typedef struct _FILE_DISPOSITION_INFORMATION_EX {
90 ULONG Flags;
91 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
92
93 typedef struct _FILE_LINK_INFORMATION_EX {
94 union {
95 BOOLEAN ReplaceIfExists;
96 ULONG Flags;
97 };
98 HANDLE RootDirectory;
99 ULONG FileNameLength;
100 WCHAR FileName[1];
101 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
102
103 typedef struct _FILE_CASE_SENSITIVE_INFORMATION {
104 ULONG Flags;
105 } FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION;
106
107 typedef struct _FILE_LINK_ENTRY_FULL_ID_INFORMATION {
108 ULONG NextEntryOffset;
109 FILE_ID_128 ParentFileId;
110 ULONG FileNameLength;
111 WCHAR FileName[1];
112 } FILE_LINK_ENTRY_FULL_ID_INFORMATION, *PFILE_LINK_ENTRY_FULL_ID_INFORMATION;
113
114 typedef struct _FILE_LINKS_FULL_ID_INFORMATION {
115 ULONG BytesNeeded;
116 ULONG EntriesReturned;
117 FILE_LINK_ENTRY_FULL_ID_INFORMATION Entry;
118 } FILE_LINKS_FULL_ID_INFORMATION, *PFILE_LINKS_FULL_ID_INFORMATION;
119
120 #define FILE_RENAME_REPLACE_IF_EXISTS 0x001
121 #define FILE_RENAME_POSIX_SEMANTICS 0x002
122 #define FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE 0x004
123 #define FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE 0x008
124 #define FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE 0x010
125 #define FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE 0x020
126 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE 0x040
127 #define FILE_RENAME_FORCE_RESIZE_TARGET_SR 0x080
128 #define FILE_RENAME_FORCE_RESIZE_SOURCE_SR 0x100
129
130 #define FILE_DISPOSITION_DELETE 0x1
131 #define FILE_DISPOSITION_POSIX_SEMANTICS 0x2
132 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x4
133 #define FILE_DISPOSITION_ON_CLOSE 0x8
134
135 #define FILE_LINK_REPLACE_IF_EXISTS 0x001
136 #define FILE_LINK_POSIX_SEMANTICS 0x002
137 #define FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE 0x008
138 #define FILE_LINK_NO_INCREASE_AVAILABLE_SPACE 0x010
139 #define FILE_LINK_NO_DECREASE_AVAILABLE_SPACE 0x020
140 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE 0x040
141 #define FILE_LINK_FORCE_RESIZE_TARGET_SR 0x080
142 #define FILE_LINK_FORCE_RESIZE_SOURCE_SR 0x100
143
144 #else
145
146 #define FILE_RENAME_INFORMATION_EX FILE_RENAME_INFORMATION
147 #define FILE_LINK_INFORMATION_EX FILE_LINK_INFORMATION
148
149 #endif
150 #endif
151
152 #ifdef __REACTOS__
153 typedef struct _FILE_RENAME_INFORMATION_EX {
154 union {
155 BOOLEAN ReplaceIfExists;
156 ULONG Flags;
157 };
158 HANDLE RootDirectory;
159 ULONG FileNameLength;
160 WCHAR FileName[1];
161 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
162
163 typedef struct _FILE_DISPOSITION_INFORMATION_EX {
164 ULONG Flags;
165 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
166
167 typedef struct _FILE_LINK_INFORMATION_EX {
168 union {
169 BOOLEAN ReplaceIfExists;
170 ULONG Flags;
171 };
172 HANDLE RootDirectory;
173 ULONG FileNameLength;
174 WCHAR FileName[1];
175 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
176
177 #define FILE_RENAME_REPLACE_IF_EXISTS 0x001
178 #define FILE_RENAME_POSIX_SEMANTICS 0x002
179 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE 0x040
180
181 #define FILE_DISPOSITION_DELETE 0x1
182 #define FILE_DISPOSITION_POSIX_SEMANTICS 0x2
183 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x4
184
185 #define FILE_LINK_REPLACE_IF_EXISTS 0x001
186 #define FILE_LINK_POSIX_SEMANTICS 0x002
187 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE 0x040
188 #endif
189
set_basic_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject)190 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
191 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
192 fcb* fcb = FileObject->FsContext;
193 ccb* ccb = FileObject->FsContext2;
194 file_ref* fileref = ccb ? ccb->fileref : NULL;
195 ULONG defda, filter = 0;
196 bool inode_item_changed = false;
197 NTSTATUS Status;
198
199 if (fcb->ads) {
200 if (fileref && fileref->parent)
201 fcb = fileref->parent->fcb;
202 else {
203 ERR("stream did not have fileref\n");
204 return STATUS_INTERNAL_ERROR;
205 }
206 }
207
208 if (!ccb) {
209 ERR("ccb was NULL\n");
210 return STATUS_INVALID_PARAMETER;
211 }
212
213 TRACE("file = %p, attributes = %lx\n", FileObject, fbi->FileAttributes);
214
215 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
216
217 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) {
218 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n");
219 Status = STATUS_INVALID_PARAMETER;
220 goto end;
221 }
222
223 if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) &&
224 (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
225 Status = STATUS_ACCESS_DENIED;
226 goto end;
227 }
228
229 // don't allow readonly subvol to be made r/w if send operation running on it
230 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY &&
231 fcb->subvol->send_ops > 0) {
232 Status = STATUS_DEVICE_NOT_READY;
233 goto end;
234 }
235
236 // times of -2 are some sort of undocumented behaviour to do with LXSS
237
238 if (fbi->CreationTime.QuadPart == -2)
239 fbi->CreationTime.QuadPart = 0;
240
241 if (fbi->LastAccessTime.QuadPart == -2)
242 fbi->LastAccessTime.QuadPart = 0;
243
244 if (fbi->LastWriteTime.QuadPart == -2)
245 fbi->LastWriteTime.QuadPart = 0;
246
247 if (fbi->ChangeTime.QuadPart == -2)
248 fbi->ChangeTime.QuadPart = 0;
249
250 if (fbi->CreationTime.QuadPart == -1)
251 ccb->user_set_creation_time = true;
252 else if (fbi->CreationTime.QuadPart != 0) {
253 win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
254 inode_item_changed = true;
255 filter |= FILE_NOTIFY_CHANGE_CREATION;
256
257 ccb->user_set_creation_time = true;
258 }
259
260 if (fbi->LastAccessTime.QuadPart == -1)
261 ccb->user_set_access_time = true;
262 else if (fbi->LastAccessTime.QuadPart != 0) {
263 win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
264 inode_item_changed = true;
265 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
266
267 ccb->user_set_access_time = true;
268 }
269
270 if (fbi->LastWriteTime.QuadPart == -1)
271 ccb->user_set_write_time = true;
272 else if (fbi->LastWriteTime.QuadPart != 0) {
273 win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
274 inode_item_changed = true;
275 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
276
277 ccb->user_set_write_time = true;
278 }
279
280 if (fbi->ChangeTime.QuadPart == -1)
281 ccb->user_set_change_time = true;
282 else if (fbi->ChangeTime.QuadPart != 0) {
283 win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
284 inode_item_changed = true;
285 // no filter for this
286
287 ccb->user_set_change_time = true;
288 }
289
290 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat
291 if (fbi->FileAttributes != 0) {
292 LARGE_INTEGER time;
293 BTRFS_TIME now;
294
295 fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL;
296
297 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] == '.',
298 true, Irp);
299
300 if (fcb->type == BTRFS_TYPE_DIRECTORY)
301 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
302 else if (fcb->type == BTRFS_TYPE_SYMLINK)
303 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
304
305 fcb->atts_changed = true;
306
307 if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)
308 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
309
310 if (defda == fbi->FileAttributes)
311 fcb->atts_deleted = true;
312 else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY))
313 fcb->atts_deleted = true;
314
315 fcb->atts = fbi->FileAttributes;
316
317 KeQuerySystemTime(&time);
318 win_time_to_unix(time, &now);
319
320 if (!ccb->user_set_change_time)
321 fcb->inode_item.st_ctime = now;
322
323 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
324 fcb->subvol->root_item.ctime = now;
325
326 if (fcb->inode == SUBVOL_ROOT_INODE) {
327 if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)
328 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY;
329 else
330 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY;
331 }
332
333 inode_item_changed = true;
334
335 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
336 }
337
338 if (inode_item_changed) {
339 fcb->inode_item.transid = Vcb->superblock.generation;
340 fcb->inode_item.sequence++;
341 fcb->inode_item_changed = true;
342
343 mark_fcb_dirty(fcb);
344 }
345
346 if (filter != 0)
347 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
348
349 Status = STATUS_SUCCESS;
350
351 end:
352 ExReleaseResourceLite(fcb->Header.Resource);
353
354 return Status;
355 }
356
set_disposition_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject,bool ex)357 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool ex) {
358 fcb* fcb = FileObject->FsContext;
359 ccb* ccb = FileObject->FsContext2;
360 file_ref* fileref = ccb ? ccb->fileref : NULL;
361 ULONG atts, flags;
362 NTSTATUS Status;
363
364 if (!fileref)
365 return STATUS_INVALID_PARAMETER;
366
367 if (ex) {
368 FILE_DISPOSITION_INFORMATION_EX* fdi = Irp->AssociatedIrp.SystemBuffer;
369
370 flags = fdi->Flags;
371 } else {
372 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
373
374 flags = fdi->DeleteFile ? FILE_DISPOSITION_DELETE : 0;
375 }
376
377 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
378
379 TRACE("changing delete_on_close to %s for fcb %p\n", flags & FILE_DISPOSITION_DELETE ? "true" : "false", fcb);
380
381 if (fcb->ads) {
382 if (fileref->parent)
383 atts = fileref->parent->fcb->atts;
384 else {
385 ERR("no fileref for stream\n");
386 Status = STATUS_INTERNAL_ERROR;
387 goto end;
388 }
389 } else
390 atts = fcb->atts;
391
392 TRACE("atts = %lx\n", atts);
393
394 if (atts & FILE_ATTRIBUTE_READONLY) {
395 TRACE("not allowing readonly file to be deleted\n");
396 Status = STATUS_CANNOT_DELETE;
397 goto end;
398 }
399
400 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
401 WARN("not allowing \\$Root to be deleted\n");
402 Status = STATUS_ACCESS_DENIED;
403 goto end;
404 }
405
406 // FIXME - can we skip this bit for subvols?
407 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) {
408 TRACE("directory not empty\n");
409 Status = STATUS_DIRECTORY_NOT_EMPTY;
410 goto end;
411 }
412
413 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
414 if (!ex || flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) {
415 TRACE("trying to delete file which is being mapped as an image\n");
416 Status = STATUS_CANNOT_DELETE;
417 goto end;
418 }
419 }
420
421 ccb->fileref->delete_on_close = flags & FILE_DISPOSITION_DELETE;
422
423 FileObject->DeletePending = flags & FILE_DISPOSITION_DELETE;
424
425 if (flags & FILE_DISPOSITION_DELETE && flags & FILE_DISPOSITION_POSIX_SEMANTICS)
426 ccb->fileref->posix_delete = true;
427
428 Status = STATUS_SUCCESS;
429
430 end:
431 ExReleaseResourceLite(fcb->Header.Resource);
432
433 // send notification that directory is about to be deleted
434 if (NT_SUCCESS(Status) && flags & FILE_DISPOSITION_DELETE && fcb->type == BTRFS_TYPE_DIRECTORY) {
435 FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
436 NULL, false, false, 0, NULL, NULL, NULL);
437 }
438
439 return Status;
440 }
441
has_open_children(file_ref * fileref)442 bool has_open_children(file_ref* fileref) {
443 LIST_ENTRY* le = fileref->children.Flink;
444
445 if (IsListEmpty(&fileref->children))
446 return false;
447
448 while (le != &fileref->children) {
449 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
450
451 if (c->open_count > 0)
452 return true;
453
454 if (has_open_children(c))
455 return true;
456
457 le = le->Flink;
458 }
459
460 return false;
461 }
462
duplicate_fcb(fcb * oldfcb,fcb ** pfcb)463 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
464 device_extension* Vcb = oldfcb->Vcb;
465 fcb* fcb;
466 LIST_ENTRY* le;
467
468 // FIXME - we can skip a lot of this if the inode is about to be deleted
469
470 fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
471 if (!fcb) {
472 ERR("out of memory\n");
473 return STATUS_INSUFFICIENT_RESOURCES;
474 }
475
476 fcb->Vcb = Vcb;
477
478 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
479 fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
480 fcb->Header.FileSize = oldfcb->Header.FileSize;
481 fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength;
482
483 fcb->type = oldfcb->type;
484
485 if (oldfcb->ads) {
486 fcb->ads = true;
487 fcb->adshash = oldfcb->adshash;
488 fcb->adsmaxlen = oldfcb->adsmaxlen;
489
490 if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
491 fcb->adsxattr.Length = oldfcb->adsxattr.Length;
492 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
493 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
494
495 if (!fcb->adsxattr.Buffer) {
496 ERR("out of memory\n");
497 free_fcb(fcb);
498 return STATUS_INSUFFICIENT_RESOURCES;
499 }
500
501 RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
502 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
503 }
504
505 if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
506 fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
507 fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
508
509 if (!fcb->adsdata.Buffer) {
510 ERR("out of memory\n");
511 free_fcb(fcb);
512 return STATUS_INSUFFICIENT_RESOURCES;
513 }
514
515 RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
516 }
517
518 goto end;
519 }
520
521 RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM));
522 fcb->inode_item_changed = true;
523
524 if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) {
525 fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
526 if (!fcb->sd) {
527 ERR("out of memory\n");
528 free_fcb(fcb);
529 return STATUS_INSUFFICIENT_RESOURCES;
530 }
531
532 RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd));
533 }
534
535 fcb->atts = oldfcb->atts;
536
537 le = oldfcb->extents.Flink;
538 while (le != &oldfcb->extents) {
539 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
540
541 if (!ext->ignore) {
542 extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
543
544 if (!ext2) {
545 ERR("out of memory\n");
546 free_fcb(fcb);
547 return STATUS_INSUFFICIENT_RESOURCES;
548 }
549
550 ext2->offset = ext->offset;
551 ext2->datalen = ext->datalen;
552
553 if (ext2->datalen > 0)
554 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
555
556 ext2->unique = false;
557 ext2->ignore = false;
558 ext2->inserted = true;
559
560 if (ext->csum) {
561 ULONG len;
562 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
563
564 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
565 len = (ULONG)ed2->num_bytes;
566 else
567 len = (ULONG)ed2->size;
568
569 len = (len * sizeof(uint32_t)) >> Vcb->sector_shift;
570
571 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
572 if (!ext2->csum) {
573 ERR("out of memory\n");
574 free_fcb(fcb);
575 return STATUS_INSUFFICIENT_RESOURCES;
576 }
577
578 RtlCopyMemory(ext2->csum, ext->csum, len);
579 } else
580 ext2->csum = NULL;
581
582 InsertTailList(&fcb->extents, &ext2->list_entry);
583 }
584
585 le = le->Flink;
586 }
587
588 le = oldfcb->hardlinks.Flink;
589 while (le != &oldfcb->hardlinks) {
590 hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2;
591
592 hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
593
594 if (!hl2) {
595 ERR("out of memory\n");
596 free_fcb(fcb);
597 return STATUS_INSUFFICIENT_RESOURCES;
598 }
599
600 hl2->parent = hl->parent;
601 hl2->index = hl->index;
602
603 hl2->name.Length = hl2->name.MaximumLength = hl->name.Length;
604 hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG);
605
606 if (!hl2->name.Buffer) {
607 ERR("out of memory\n");
608 ExFreePool(hl2);
609 free_fcb(fcb);
610 return STATUS_INSUFFICIENT_RESOURCES;
611 }
612
613 RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length);
614
615 hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length;
616 hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG);
617
618 if (!hl2->utf8.Buffer) {
619 ERR("out of memory\n");
620 ExFreePool(hl2->name.Buffer);
621 ExFreePool(hl2);
622 free_fcb(fcb);
623 return STATUS_INSUFFICIENT_RESOURCES;
624 }
625
626 RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
627
628 InsertTailList(&fcb->hardlinks, &hl2->list_entry);
629
630 le = le->Flink;
631 }
632
633 if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
634 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
635
636 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
637 if (!fcb->reparse_xattr.Buffer) {
638 ERR("out of memory\n");
639 free_fcb(fcb);
640 return STATUS_INSUFFICIENT_RESOURCES;
641 }
642
643 RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
644 }
645
646 if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) {
647 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length;
648
649 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG);
650 if (!fcb->ea_xattr.Buffer) {
651 ERR("out of memory\n");
652 free_fcb(fcb);
653 return STATUS_INSUFFICIENT_RESOURCES;
654 }
655
656 RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
657 }
658
659 fcb->prop_compression = oldfcb->prop_compression;
660
661 le = oldfcb->xattrs.Flink;
662 while (le != &oldfcb->xattrs) {
663 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry);
664
665 if (xa->valuelen > 0) {
666 xattr* xa2;
667
668 xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG);
669
670 if (!xa2) {
671 ERR("out of memory\n");
672 free_fcb(fcb);
673 return STATUS_INSUFFICIENT_RESOURCES;
674 }
675
676 xa2->namelen = xa->namelen;
677 xa2->valuelen = xa->valuelen;
678 xa2->dirty = xa->dirty;
679 memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen);
680
681 InsertTailList(&fcb->xattrs, &xa2->list_entry);
682 }
683
684 le = le->Flink;
685 }
686
687 end:
688 *pfcb = fcb;
689
690 return STATUS_SUCCESS;
691 }
692
693 typedef struct _move_entry {
694 file_ref* fileref;
695 fcb* dummyfcb;
696 file_ref* dummyfileref;
697 struct _move_entry* parent;
698 LIST_ENTRY list_entry;
699 } move_entry;
700
add_children_to_move_list(device_extension * Vcb,move_entry * me,PIRP Irp)701 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
702 NTSTATUS Status;
703 LIST_ENTRY* le;
704
705 ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, true);
706
707 le = me->fileref->fcb->dir_children_index.Flink;
708
709 while (le != &me->fileref->fcb->dir_children_index) {
710 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
711 file_ref* fr;
712 move_entry* me2;
713
714 Status = open_fileref_child(Vcb, me->fileref, &dc->name, true, true, dc->index == 0 ? true : false, PagedPool, &fr, Irp);
715
716 if (!NT_SUCCESS(Status)) {
717 ERR("open_fileref_child returned %08lx\n", Status);
718 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
719 return Status;
720 }
721
722 me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
723 if (!me2) {
724 ERR("out of memory\n");
725 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
726 return STATUS_INSUFFICIENT_RESOURCES;
727 }
728
729 me2->fileref = fr;
730 me2->dummyfcb = NULL;
731 me2->dummyfileref = NULL;
732 me2->parent = me;
733
734 InsertHeadList(&me->list_entry, &me2->list_entry);
735
736 le = le->Flink;
737 }
738
739 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
740
741 return STATUS_SUCCESS;
742 }
743
remove_dir_child_from_hash_lists(fcb * fcb,dir_child * dc)744 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
745 uint8_t c;
746
747 c = dc->hash >> 24;
748
749 if (fcb->hash_ptrs[c] == &dc->list_entry_hash) {
750 if (dc->list_entry_hash.Flink == &fcb->dir_children_hash)
751 fcb->hash_ptrs[c] = NULL;
752 else {
753 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash);
754
755 if (dc2->hash >> 24 == c)
756 fcb->hash_ptrs[c] = &dc2->list_entry_hash;
757 else
758 fcb->hash_ptrs[c] = NULL;
759 }
760 }
761
762 RemoveEntryList(&dc->list_entry_hash);
763
764 c = dc->hash_uc >> 24;
765
766 if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) {
767 if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc)
768 fcb->hash_ptrs_uc[c] = NULL;
769 else {
770 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc);
771
772 if (dc2->hash_uc >> 24 == c)
773 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc;
774 else
775 fcb->hash_ptrs_uc[c] = NULL;
776 }
777 }
778
779 RemoveEntryList(&dc->list_entry_hash_uc);
780 }
781
create_directory_fcb(device_extension * Vcb,root * r,fcb * parfcb,fcb ** pfcb)782 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) {
783 NTSTATUS Status;
784 fcb* fcb;
785 SECURITY_SUBJECT_CONTEXT subjcont;
786 PSID owner;
787 BOOLEAN defaulted;
788 LARGE_INTEGER time;
789 BTRFS_TIME now;
790
791 fcb = create_fcb(Vcb, PagedPool);
792 if (!fcb) {
793 ERR("out of memory\n");
794 return STATUS_INSUFFICIENT_RESOURCES;
795 }
796
797 KeQuerySystemTime(&time);
798 win_time_to_unix(time, &now);
799
800 fcb->Vcb = Vcb;
801
802 fcb->subvol = r;
803 fcb->inode = InterlockedIncrement64(&r->lastinode);
804 fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t));
805 fcb->type = BTRFS_TYPE_DIRECTORY;
806
807 fcb->inode_item.generation = Vcb->superblock.generation;
808 fcb->inode_item.transid = Vcb->superblock.generation;
809 fcb->inode_item.st_nlink = 1;
810 fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, true);
811 fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now;
812 fcb->inode_item.st_gid = GID_NOBODY;
813
814 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, false, true, NULL);
815
816 SeCaptureSubjectContext(&subjcont);
817
818 Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool);
819
820 if (!NT_SUCCESS(Status)) {
821 reap_fcb(fcb);
822 ERR("SeAssignSecurity returned %08lx\n", Status);
823 return Status;
824 }
825
826 if (!fcb->sd) {
827 reap_fcb(fcb);
828 ERR("SeAssignSecurity returned NULL security descriptor\n");
829 return STATUS_INTERNAL_ERROR;
830 }
831
832 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted);
833 if (!NT_SUCCESS(Status)) {
834 ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status);
835 fcb->inode_item.st_uid = UID_NOBODY;
836 fcb->sd_dirty = true;
837 } else {
838 fcb->inode_item.st_uid = sid_to_uid(owner);
839 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY;
840 }
841
842 find_gid(fcb, parfcb, &subjcont);
843
844 fcb->inode_item_changed = true;
845
846 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
847 fcb->Header.AllocationSize.QuadPart = 0;
848 fcb->Header.FileSize.QuadPart = 0;
849 fcb->Header.ValidDataLength.QuadPart = 0;
850
851 fcb->created = true;
852
853 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS)
854 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
855
856 fcb->prop_compression = parfcb->prop_compression;
857 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
858
859 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
860 if (!fcb->hash_ptrs) {
861 ERR("out of memory\n");
862 return STATUS_INSUFFICIENT_RESOURCES;
863 }
864
865 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
866
867 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
868 if (!fcb->hash_ptrs_uc) {
869 ERR("out of memory\n");
870 return STATUS_INSUFFICIENT_RESOURCES;
871 }
872
873 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
874
875 acquire_fcb_lock_exclusive(Vcb);
876 add_fcb_to_subvol(fcb);
877 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
878 r->fcbs_version++;
879 release_fcb_lock(Vcb);
880
881 mark_fcb_dirty(fcb);
882
883 *pfcb = fcb;
884
885 return STATUS_SUCCESS;
886 }
887
888 void add_fcb_to_subvol(_In_ _Requires_exclusive_lock_held_(_Curr_->Vcb->fcb_lock) fcb* fcb) {
889 LIST_ENTRY* lastle = NULL;
890 uint32_t hash = fcb->hash;
891
892 if (fcb->subvol->fcbs_ptrs[hash >> 24]) {
893 LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[hash >> 24];
894
895 while (le != &fcb->subvol->fcbs) {
896 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
897
898 if (fcb2->hash > hash) {
899 lastle = le->Blink;
900 break;
901 }
902
903 le = le->Flink;
904 }
905 }
906
907 if (!lastle) {
908 uint8_t c = hash >> 24;
909
910 if (c != 0xff) {
911 uint8_t d = c + 1;
912
913 do {
914 if (fcb->subvol->fcbs_ptrs[d]) {
915 lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
916 break;
917 }
918
919 d++;
920 } while (d != 0);
921 }
922 }
923
924 if (lastle) {
925 InsertHeadList(lastle, &fcb->list_entry);
926
927 if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
928 fcb->subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
929 } else {
930 InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
931
932 if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
933 fcb->subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
934 }
935 }
936
937 void remove_fcb_from_subvol(_In_ _Requires_exclusive_lock_held_(_Curr_->Vcb->fcb_lock) fcb* fcb) {
938 uint8_t c = fcb->hash >> 24;
939
940 if (fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
941 if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
942 fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
943 else
944 fcb->subvol->fcbs_ptrs[c] = NULL;
945 }
946
947 RemoveEntryList(&fcb->list_entry);
948 }
949
move_across_subvols(file_ref * fileref,ccb * ccb,file_ref * destdir,PANSI_STRING utf8,PUNICODE_STRING fnus,PIRP Irp,LIST_ENTRY * rollback)950 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
951 NTSTATUS Status;
952 LIST_ENTRY move_list, *le;
953 move_entry* me;
954 LARGE_INTEGER time;
955 BTRFS_TIME now;
956 file_ref* origparent;
957
958 // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly
959
960 InitializeListHead(&move_list);
961
962 KeQuerySystemTime(&time);
963 win_time_to_unix(time, &now);
964
965 acquire_fcb_lock_exclusive(fileref->fcb->Vcb);
966
967 me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
968
969 if (!me) {
970 ERR("out of memory\n");
971 Status = STATUS_INSUFFICIENT_RESOURCES;
972 goto end;
973 }
974
975 origparent = fileref->parent;
976
977 me->fileref = fileref;
978 increase_fileref_refcount(me->fileref);
979 me->dummyfcb = NULL;
980 me->dummyfileref = NULL;
981 me->parent = NULL;
982
983 InsertTailList(&move_list, &me->list_entry);
984
985 le = move_list.Flink;
986 while (le != &move_list) {
987 me = CONTAINING_RECORD(le, move_entry, list_entry);
988
989 ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, true);
990
991 if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
992 Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
993
994 if (!NT_SUCCESS(Status)) {
995 ERR("add_children_to_move_list returned %08lx\n", Status);
996 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
997 goto end;
998 }
999 }
1000
1001 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1002
1003 le = le->Flink;
1004 }
1005
1006 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
1007
1008 // loop through list and create new inodes
1009
1010 le = move_list.Flink;
1011 while (le != &move_list) {
1012 me = CONTAINING_RECORD(le, move_entry, list_entry);
1013
1014 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) {
1015 if (!me->dummyfcb) {
1016 ULONG defda;
1017
1018 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true);
1019
1020 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb);
1021 if (!NT_SUCCESS(Status)) {
1022 ERR("duplicate_fcb returned %08lx\n", Status);
1023 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1024 goto end;
1025 }
1026
1027 me->dummyfcb->subvol = me->fileref->fcb->subvol;
1028 me->dummyfcb->inode = me->fileref->fcb->inode;
1029 me->dummyfcb->hash = me->fileref->fcb->hash;
1030
1031 if (!me->dummyfcb->ads) {
1032 me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty;
1033 me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed;
1034 me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted;
1035 me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed;
1036 me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed;
1037 me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed;
1038 }
1039
1040 me->dummyfcb->created = me->fileref->fcb->created;
1041 me->dummyfcb->deleted = me->fileref->fcb->deleted;
1042 mark_fcb_dirty(me->dummyfcb);
1043
1044 if (!me->fileref->fcb->ads) {
1045 LIST_ENTRY* le2;
1046
1047 me->fileref->fcb->subvol = destdir->fcb->subvol;
1048 me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
1049 me->fileref->fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&me->fileref->fcb->inode, sizeof(uint64_t));
1050 me->fileref->fcb->inode_item.st_nlink = 1;
1051
1052 defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
1053 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
1054 true, Irp);
1055
1056 me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
1057 me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
1058 me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents);
1059 me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer;
1060 me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
1061 me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs);
1062 me->fileref->fcb->inode_item_changed = true;
1063
1064 le2 = me->fileref->fcb->xattrs.Flink;
1065 while (le2 != &me->fileref->fcb->xattrs) {
1066 xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry);
1067
1068 xa->dirty = true;
1069
1070 le2 = le2->Flink;
1071 }
1072
1073 if (le == move_list.Flink) { // first entry
1074 me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1075 me->fileref->fcb->inode_item.sequence++;
1076
1077 if (!ccb->user_set_change_time)
1078 me->fileref->fcb->inode_item.st_ctime = now;
1079 }
1080
1081 le2 = me->fileref->fcb->extents.Flink;
1082 while (le2 != &me->fileref->fcb->extents) {
1083 extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
1084
1085 if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
1086 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
1087
1088 if (ed2->size != 0) {
1089 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
1090
1091 if (!c) {
1092 ERR("get_chunk_from_address(%I64x) failed\n", ed2->address);
1093 } else {
1094 Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
1095 ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp);
1096
1097 if (!NT_SUCCESS(Status)) {
1098 ERR("update_changed_extent_ref returned %08lx\n", Status);
1099 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1100 goto end;
1101 }
1102 }
1103
1104 }
1105 }
1106
1107 le2 = le2->Flink;
1108 }
1109
1110 add_fcb_to_subvol(me->dummyfcb);
1111 remove_fcb_from_subvol(me->fileref->fcb);
1112 add_fcb_to_subvol(me->fileref->fcb);
1113 } else {
1114 me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol;
1115 me->fileref->fcb->inode = me->parent->fileref->fcb->inode;
1116 me->fileref->fcb->hash = me->parent->fileref->fcb->hash;
1117
1118 // put stream after parent in FCB list
1119 InsertHeadList(&me->parent->fileref->fcb->list_entry, &me->fileref->fcb->list_entry);
1120 }
1121
1122 me->fileref->fcb->created = true;
1123
1124 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
1125
1126 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
1127 hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
1128
1129 if (hl->name.Buffer)
1130 ExFreePool(hl->name.Buffer);
1131
1132 if (hl->utf8.Buffer)
1133 ExFreePool(hl->utf8.Buffer);
1134
1135 ExFreePool(hl);
1136 }
1137
1138 me->fileref->fcb->inode_item_changed = true;
1139 mark_fcb_dirty(me->fileref->fcb);
1140
1141 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) {
1142 LIST_ENTRY* le2 = le->Flink;
1143
1144 while (le2 != &move_list) {
1145 move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry);
1146
1147 if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) {
1148 me2->dummyfcb = me->dummyfcb;
1149 InterlockedIncrement(&me->dummyfcb->refcount);
1150 }
1151
1152 le2 = le2->Flink;
1153 }
1154 }
1155
1156 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1157 } else {
1158 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true);
1159 me->fileref->fcb->inode_item.st_nlink++;
1160 me->fileref->fcb->inode_item_changed = true;
1161 ExReleaseResourceLite(me->fileref->fcb->Header.Resource);
1162 }
1163 }
1164
1165 le = le->Flink;
1166 }
1167
1168 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
1169 fileref->fcb->subvol->root_item.ctime = now;
1170
1171 // loop through list and create new filerefs
1172
1173 le = move_list.Flink;
1174 while (le != &move_list) {
1175 hardlink* hl;
1176 bool name_changed = false;
1177
1178 me = CONTAINING_RECORD(le, move_entry, list_entry);
1179
1180 me->dummyfileref = create_fileref(fileref->fcb->Vcb);
1181 if (!me->dummyfileref) {
1182 ERR("out of memory\n");
1183 Status = STATUS_INSUFFICIENT_RESOURCES;
1184 goto end;
1185 }
1186
1187 if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) {
1188 root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol;
1189
1190 Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb);
1191 if (!NT_SUCCESS(Status)) {
1192 ERR("create_directory_fcb returned %08lx\n", Status);
1193 goto end;
1194 }
1195
1196 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1197 me->fileref->dc->key.obj_type = TYPE_INODE_ITEM;
1198
1199 me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb;
1200 } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) {
1201 me->dummyfileref->fcb = me->fileref->fcb;
1202
1203 me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id;
1204 } else
1205 me->dummyfileref->fcb = me->dummyfcb;
1206
1207 InterlockedIncrement(&me->dummyfileref->fcb->refcount);
1208
1209 me->dummyfileref->oldutf8 = me->fileref->oldutf8;
1210 me->dummyfileref->oldindex = me->fileref->dc->index;
1211
1212 if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length))
1213 name_changed = true;
1214
1215 if (!me->dummyfileref->oldutf8.Buffer) {
1216 me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
1217 if (!me->dummyfileref->oldutf8.Buffer) {
1218 ERR("out of memory\n");
1219 Status = STATUS_INSUFFICIENT_RESOURCES;
1220 goto end;
1221 }
1222
1223 RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1224
1225 me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
1226 }
1227
1228 me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
1229 me->dummyfileref->deleted = me->fileref->deleted;
1230
1231 me->dummyfileref->created = me->fileref->created;
1232 me->fileref->created = true;
1233
1234 me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
1235 increase_fileref_refcount(me->dummyfileref->parent);
1236
1237 ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, true);
1238 InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
1239 ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock);
1240
1241 if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY)
1242 me->dummyfileref->fcb->fileref = me->dummyfileref;
1243
1244 if (!me->parent) {
1245 RemoveEntryList(&me->fileref->list_entry);
1246
1247 increase_fileref_refcount(destdir);
1248
1249 if (me->fileref->dc) {
1250 // remove from old parent
1251 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1252 RemoveEntryList(&me->fileref->dc->list_entry_index);
1253 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1254 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1255
1256 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
1257 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1258 me->fileref->parent->fcb->inode_item.sequence++;
1259 me->fileref->parent->fcb->inode_item.st_ctime = now;
1260 me->fileref->parent->fcb->inode_item.st_mtime = now;
1261 me->fileref->parent->fcb->inode_item_changed = true;
1262 mark_fcb_dirty(me->fileref->parent->fcb);
1263
1264 if (name_changed) {
1265 ExFreePool(me->fileref->dc->utf8.Buffer);
1266 ExFreePool(me->fileref->dc->name.Buffer);
1267 ExFreePool(me->fileref->dc->name_uc.Buffer);
1268
1269 me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1270 if (!me->fileref->dc->utf8.Buffer) {
1271 ERR("out of memory\n");
1272 Status = STATUS_INSUFFICIENT_RESOURCES;
1273 goto end;
1274 }
1275
1276 me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
1277 RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1278
1279 me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
1280 if (!me->fileref->dc->name.Buffer) {
1281 ERR("out of memory\n");
1282 Status = STATUS_INSUFFICIENT_RESOURCES;
1283 goto end;
1284 }
1285
1286 me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
1287 RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
1288
1289 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
1290 if (!NT_SUCCESS(Status)) {
1291 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1292 goto end;
1293 }
1294
1295 me->fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1296 me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
1297 }
1298
1299 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
1300 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1301
1302 // add to new parent
1303
1304 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, true);
1305
1306 if (IsListEmpty(&destdir->fcb->dir_children_index))
1307 me->fileref->dc->index = 2;
1308 else {
1309 dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1310
1311 me->fileref->dc->index = max(2, dc2->index + 1);
1312 }
1313
1314 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1315 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
1316 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
1317 }
1318
1319 free_fileref(me->fileref->parent);
1320 me->fileref->parent = destdir;
1321
1322 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1323 InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
1324 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1325
1326 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1327 me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
1328 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
1329 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
1330 me->fileref->parent->fcb->inode_item.sequence++;
1331 me->fileref->parent->fcb->inode_item.st_ctime = now;
1332 me->fileref->parent->fcb->inode_item.st_mtime = now;
1333 me->fileref->parent->fcb->inode_item_changed = true;
1334 mark_fcb_dirty(me->fileref->parent->fcb);
1335 } else {
1336 if (me->fileref->dc) {
1337 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true);
1338 RemoveEntryList(&me->fileref->dc->list_entry_index);
1339
1340 if (!me->fileref->fcb->ads)
1341 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
1342
1343 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
1344
1345 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, true);
1346
1347 if (me->fileref->fcb->ads)
1348 InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1349 else {
1350 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE)
1351 me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
1352
1353 if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index))
1354 me->fileref->dc->index = 2;
1355 else {
1356 dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index);
1357
1358 me->fileref->dc->index = max(2, dc2->index + 1);
1359 }
1360
1361 InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
1362 insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
1363 }
1364
1365 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
1366 }
1367 }
1368
1369 if (!me->dummyfileref->fcb->ads) {
1370 Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback);
1371 if (!NT_SUCCESS(Status)) {
1372 ERR("delete_fileref returned %08lx\n", Status);
1373 goto end;
1374 }
1375 }
1376
1377 if (me->fileref->fcb->inode_item.st_nlink > 1) {
1378 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
1379 if (!hl) {
1380 ERR("out of memory\n");
1381 Status = STATUS_INSUFFICIENT_RESOURCES;
1382 goto end;
1383 }
1384
1385 hl->parent = me->fileref->parent->fcb->inode;
1386 hl->index = me->fileref->dc->index;
1387
1388 hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
1389 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
1390 if (!hl->utf8.Buffer) {
1391 ERR("out of memory\n");
1392 Status = STATUS_INSUFFICIENT_RESOURCES;
1393 ExFreePool(hl);
1394 goto end;
1395 }
1396
1397 RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
1398
1399 hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
1400 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
1401 if (!hl->name.Buffer) {
1402 ERR("out of memory\n");
1403 Status = STATUS_INSUFFICIENT_RESOURCES;
1404 ExFreePool(hl->utf8.Buffer);
1405 ExFreePool(hl);
1406 goto end;
1407 }
1408
1409 RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
1410
1411 InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
1412 }
1413
1414 mark_fileref_dirty(me->fileref);
1415
1416 le = le->Flink;
1417 }
1418
1419 // loop through, and only mark streams as deleted if their parent inodes are also deleted
1420
1421 le = move_list.Flink;
1422 while (le != &move_list) {
1423 me = CONTAINING_RECORD(le, move_entry, list_entry);
1424
1425 if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
1426 Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback);
1427 if (!NT_SUCCESS(Status)) {
1428 ERR("delete_fileref returned %08lx\n", Status);
1429 goto end;
1430 }
1431 }
1432
1433 le = le->Flink;
1434 }
1435
1436 destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation;
1437 destdir->fcb->subvol->root_item.ctime = now;
1438
1439 me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
1440 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
1441 send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1442 send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
1443
1444 Status = STATUS_SUCCESS;
1445
1446 end:
1447 while (!IsListEmpty(&move_list)) {
1448 le = RemoveHeadList(&move_list);
1449 me = CONTAINING_RECORD(le, move_entry, list_entry);
1450
1451 if (me->dummyfcb)
1452 free_fcb(me->dummyfcb);
1453
1454 if (me->dummyfileref)
1455 free_fileref(me->dummyfileref);
1456
1457 free_fileref(me->fileref);
1458
1459 ExFreePool(me);
1460 }
1461
1462 destdir->fcb->subvol->fcbs_version++;
1463 fileref->fcb->subvol->fcbs_version++;
1464
1465 release_fcb_lock(fileref->fcb->Vcb);
1466
1467 return Status;
1468 }
1469
insert_dir_child_into_hash_lists(fcb * fcb,dir_child * dc)1470 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
1471 bool inserted;
1472 LIST_ENTRY* le;
1473 uint8_t c, d;
1474
1475 c = dc->hash >> 24;
1476
1477 inserted = false;
1478
1479 d = c;
1480 do {
1481 le = fcb->hash_ptrs[d];
1482
1483 if (d == 0)
1484 break;
1485
1486 d--;
1487 } while (!le);
1488
1489 if (!le)
1490 le = fcb->dir_children_hash.Flink;
1491
1492 while (le != &fcb->dir_children_hash) {
1493 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
1494
1495 if (dc2->hash > dc->hash) {
1496 InsertHeadList(le->Blink, &dc->list_entry_hash);
1497 inserted = true;
1498 break;
1499 }
1500
1501 le = le->Flink;
1502 }
1503
1504 if (!inserted)
1505 InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash);
1506
1507 if (!fcb->hash_ptrs[c])
1508 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1509 else {
1510 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash);
1511
1512 if (dc2->hash > dc->hash)
1513 fcb->hash_ptrs[c] = &dc->list_entry_hash;
1514 }
1515
1516 c = dc->hash_uc >> 24;
1517
1518 inserted = false;
1519
1520 d = c;
1521 do {
1522 le = fcb->hash_ptrs_uc[d];
1523
1524 if (d == 0)
1525 break;
1526
1527 d--;
1528 } while (!le);
1529
1530 if (!le)
1531 le = fcb->dir_children_hash_uc.Flink;
1532
1533 while (le != &fcb->dir_children_hash_uc) {
1534 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
1535
1536 if (dc2->hash_uc > dc->hash_uc) {
1537 InsertHeadList(le->Blink, &dc->list_entry_hash_uc);
1538 inserted = true;
1539 break;
1540 }
1541
1542 le = le->Flink;
1543 }
1544
1545 if (!inserted)
1546 InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc);
1547
1548 if (!fcb->hash_ptrs_uc[c])
1549 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1550 else {
1551 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc);
1552
1553 if (dc2->hash_uc > dc->hash_uc)
1554 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc;
1555 }
1556 }
1557
rename_stream_to_file(device_extension * Vcb,file_ref * fileref,ccb * ccb,ULONG flags,PIRP Irp,LIST_ENTRY * rollback)1558 static NTSTATUS rename_stream_to_file(device_extension* Vcb, file_ref* fileref, ccb* ccb, ULONG flags,
1559 PIRP Irp, LIST_ENTRY* rollback) {
1560 NTSTATUS Status;
1561 file_ref* ofr;
1562 ANSI_STRING adsdata;
1563 dir_child* dc;
1564 fcb* dummyfcb;
1565
1566 if (fileref->fcb->type != BTRFS_TYPE_FILE)
1567 return STATUS_INVALID_PARAMETER;
1568
1569 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
1570 WARN("trying to rename stream on readonly file\n");
1571 return STATUS_ACCESS_DENIED;
1572 }
1573
1574 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
1575 WARN("insufficient permissions\n");
1576 return STATUS_ACCESS_DENIED;
1577 }
1578
1579 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) // file will always exist
1580 return STATUS_OBJECT_NAME_COLLISION;
1581
1582 // FIXME - POSIX overwrites of stream?
1583
1584 ofr = fileref->parent;
1585
1586 if (ofr->open_count > 0) {
1587 WARN("trying to overwrite open file\n");
1588 return STATUS_ACCESS_DENIED;
1589 }
1590
1591 if (ofr->fcb->inode_item.st_size > 0) {
1592 WARN("can only overwrite existing stream if it is zero-length\n");
1593 return STATUS_INVALID_PARAMETER;
1594 }
1595
1596 dummyfcb = create_fcb(Vcb, PagedPool);
1597 if (!dummyfcb) {
1598 ERR("out of memory\n");
1599 return STATUS_INSUFFICIENT_RESOURCES;
1600 }
1601
1602 // copy parent fcb onto this one
1603
1604 fileref->fcb->subvol = ofr->fcb->subvol;
1605 fileref->fcb->inode = ofr->fcb->inode;
1606 fileref->fcb->hash = ofr->fcb->hash;
1607 fileref->fcb->type = ofr->fcb->type;
1608 fileref->fcb->inode_item = ofr->fcb->inode_item;
1609
1610 fileref->fcb->sd = ofr->fcb->sd;
1611 ofr->fcb->sd = NULL;
1612
1613 fileref->fcb->deleted = ofr->fcb->deleted;
1614 fileref->fcb->atts = ofr->fcb->atts;
1615
1616 fileref->fcb->reparse_xattr = ofr->fcb->reparse_xattr;
1617 ofr->fcb->reparse_xattr.Buffer = NULL;
1618 ofr->fcb->reparse_xattr.Length = ofr->fcb->reparse_xattr.MaximumLength = 0;
1619
1620 fileref->fcb->ea_xattr = ofr->fcb->ea_xattr;
1621 ofr->fcb->ea_xattr.Buffer = NULL;
1622 ofr->fcb->ea_xattr.Length = ofr->fcb->ea_xattr.MaximumLength = 0;
1623
1624 fileref->fcb->ealen = ofr->fcb->ealen;
1625
1626 while (!IsListEmpty(&ofr->fcb->hardlinks)) {
1627 InsertTailList(&fileref->fcb->hardlinks, RemoveHeadList(&ofr->fcb->hardlinks));
1628 }
1629
1630 fileref->fcb->inode_item_changed = true;
1631 fileref->fcb->prop_compression = ofr->fcb->prop_compression;
1632
1633 while (!IsListEmpty(&ofr->fcb->xattrs)) {
1634 InsertTailList(&fileref->fcb->xattrs, RemoveHeadList(&ofr->fcb->xattrs));
1635 }
1636
1637 fileref->fcb->marked_as_orphan = ofr->fcb->marked_as_orphan;
1638 fileref->fcb->case_sensitive = ofr->fcb->case_sensitive;
1639 fileref->fcb->case_sensitive_set = ofr->fcb->case_sensitive_set;
1640
1641 while (!IsListEmpty(&ofr->fcb->dir_children_index)) {
1642 InsertTailList(&fileref->fcb->dir_children_index, RemoveHeadList(&ofr->fcb->dir_children_index));
1643 }
1644
1645 while (!IsListEmpty(&ofr->fcb->dir_children_hash)) {
1646 InsertTailList(&fileref->fcb->dir_children_hash, RemoveHeadList(&ofr->fcb->dir_children_hash));
1647 }
1648
1649 while (!IsListEmpty(&ofr->fcb->dir_children_hash_uc)) {
1650 InsertTailList(&fileref->fcb->dir_children_hash_uc, RemoveHeadList(&ofr->fcb->dir_children_hash_uc));
1651 }
1652
1653 fileref->fcb->hash_ptrs = ofr->fcb->hash_ptrs;
1654 fileref->fcb->hash_ptrs_uc = ofr->fcb->hash_ptrs_uc;
1655
1656 ofr->fcb->hash_ptrs = NULL;
1657 ofr->fcb->hash_ptrs_uc = NULL;
1658
1659 fileref->fcb->sd_dirty = ofr->fcb->sd_dirty;
1660 fileref->fcb->sd_deleted = ofr->fcb->sd_deleted;
1661 fileref->fcb->atts_changed = ofr->fcb->atts_changed;
1662 fileref->fcb->atts_deleted = ofr->fcb->atts_deleted;
1663 fileref->fcb->extents_changed = true;
1664 fileref->fcb->reparse_xattr_changed = ofr->fcb->reparse_xattr_changed;
1665 fileref->fcb->ea_changed = ofr->fcb->ea_changed;
1666 fileref->fcb->prop_compression_changed = ofr->fcb->prop_compression_changed;
1667 fileref->fcb->xattrs_changed = ofr->fcb->xattrs_changed;
1668 fileref->fcb->created = ofr->fcb->created;
1669 fileref->fcb->ads = false;
1670
1671 if (fileref->fcb->adsxattr.Buffer) {
1672 ExFreePool(fileref->fcb->adsxattr.Buffer);
1673 fileref->fcb->adsxattr.Length = fileref->fcb->adsxattr.MaximumLength = 0;
1674 fileref->fcb->adsxattr.Buffer = NULL;
1675 }
1676
1677 adsdata = fileref->fcb->adsdata;
1678
1679 fileref->fcb->adsdata.Buffer = NULL;
1680 fileref->fcb->adsdata.Length = fileref->fcb->adsdata.MaximumLength = 0;
1681
1682 acquire_fcb_lock_exclusive(Vcb);
1683
1684 RemoveEntryList(&fileref->fcb->list_entry);
1685 InsertHeadList(ofr->fcb->list_entry.Blink, &fileref->fcb->list_entry);
1686
1687 if (fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] == &ofr->fcb->list_entry)
1688 fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] = &fileref->fcb->list_entry;
1689
1690 RemoveEntryList(&ofr->fcb->list_entry);
1691
1692 release_fcb_lock(Vcb);
1693
1694 ofr->fcb->list_entry.Flink = ofr->fcb->list_entry.Blink = NULL;
1695
1696 mark_fcb_dirty(fileref->fcb);
1697
1698 // mark old parent fcb so it gets ignored by flush_fcb
1699 ofr->fcb->created = true;
1700 ofr->fcb->deleted = true;
1701
1702 mark_fcb_dirty(ofr->fcb);
1703
1704 // copy parent fileref onto this one
1705
1706 fileref->oldutf8 = ofr->oldutf8;
1707 ofr->oldutf8.Buffer = NULL;
1708 ofr->oldutf8.Length = ofr->oldutf8.MaximumLength = 0;
1709
1710 fileref->oldindex = ofr->oldindex;
1711 fileref->delete_on_close = ofr->delete_on_close;
1712 fileref->posix_delete = ofr->posix_delete;
1713 fileref->deleted = ofr->deleted;
1714 fileref->created = ofr->created;
1715
1716 fileref->parent = ofr->parent;
1717
1718 RemoveEntryList(&fileref->list_entry);
1719 InsertHeadList(ofr->list_entry.Blink, &fileref->list_entry);
1720 RemoveEntryList(&ofr->list_entry);
1721 ofr->list_entry.Flink = ofr->list_entry.Blink = NULL;
1722
1723 while (!IsListEmpty(&ofr->children)) {
1724 file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&ofr->children), file_ref, list_entry);
1725
1726 free_fileref(fr->parent);
1727
1728 fr->parent = fileref;
1729 InterlockedIncrement(&fileref->refcount);
1730
1731 InsertTailList(&fileref->children, &fr->list_entry);
1732 }
1733
1734 dc = fileref->dc;
1735
1736 fileref->dc = ofr->dc;
1737 fileref->dc->fileref = fileref;
1738
1739 mark_fileref_dirty(fileref);
1740
1741 // mark old parent fileref so it gets ignored by flush_fileref
1742 ofr->created = true;
1743 ofr->deleted = true;
1744
1745 // write file data
1746
1747 fileref->fcb->inode_item.st_size = adsdata.Length;
1748
1749 if (adsdata.Length > 0) {
1750 bool make_inline = adsdata.Length <= Vcb->options.max_inline;
1751
1752 if (make_inline) {
1753 EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), ALLOC_TAG);
1754 if (!ed) {
1755 ERR("out of memory\n");
1756 ExFreePool(adsdata.Buffer);
1757 reap_fcb(dummyfcb);
1758 return STATUS_INSUFFICIENT_RESOURCES;
1759 }
1760
1761 ed->generation = Vcb->superblock.generation;
1762 ed->decoded_size = adsdata.Length;
1763 ed->compression = BTRFS_COMPRESSION_NONE;
1764 ed->encryption = BTRFS_ENCRYPTION_NONE;
1765 ed->encoding = BTRFS_ENCODING_NONE;
1766 ed->type = EXTENT_TYPE_INLINE;
1767
1768 RtlCopyMemory(ed->data, adsdata.Buffer, adsdata.Length);
1769
1770 ExFreePool(adsdata.Buffer);
1771
1772 Status = add_extent_to_fcb(fileref->fcb, 0, ed, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), false, NULL, rollback);
1773 if (!NT_SUCCESS(Status)) {
1774 ERR("add_extent_to_fcb returned %08lx\n", Status);
1775 ExFreePool(ed);
1776 reap_fcb(dummyfcb);
1777 return Status;
1778 }
1779
1780 ExFreePool(ed);
1781 } else if (adsdata.Length & (Vcb->superblock.sector_size - 1)) {
1782 char* newbuf = ExAllocatePoolWithTag(PagedPool, (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size), ALLOC_TAG);
1783 if (!newbuf) {
1784 ERR("out of memory\n");
1785 ExFreePool(adsdata.Buffer);
1786 reap_fcb(dummyfcb);
1787 return STATUS_INSUFFICIENT_RESOURCES;
1788 }
1789
1790 RtlCopyMemory(newbuf, adsdata.Buffer, adsdata.Length);
1791 RtlZeroMemory(newbuf + adsdata.Length, (uint16_t)(sector_align(adsdata.Length, Vcb->superblock.sector_size) - adsdata.Length));
1792
1793 ExFreePool(adsdata.Buffer);
1794
1795 adsdata.Buffer = newbuf;
1796 adsdata.Length = adsdata.MaximumLength = (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size);
1797 }
1798
1799 if (!make_inline) {
1800 Status = do_write_file(fileref->fcb, 0, adsdata.Length, adsdata.Buffer, Irp, false, 0, rollback);
1801 if (!NT_SUCCESS(Status)) {
1802 ERR("do_write_file returned %08lx\n", Status);
1803 ExFreePool(adsdata.Buffer);
1804 reap_fcb(dummyfcb);
1805 return Status;
1806 }
1807
1808 ExFreePool(adsdata.Buffer);
1809 }
1810
1811 fileref->fcb->inode_item.st_blocks = adsdata.Length;
1812 fileref->fcb->inode_item_changed = true;
1813 }
1814
1815 RemoveEntryList(&dc->list_entry_index);
1816
1817 if (dc->utf8.Buffer)
1818 ExFreePool(dc->utf8.Buffer);
1819
1820 if (dc->name.Buffer)
1821 ExFreePool(dc->name.Buffer);
1822
1823 if (dc->name_uc.Buffer)
1824 ExFreePool(dc->name_uc.Buffer);
1825
1826 ExFreePool(dc);
1827
1828 // FIXME - csums?
1829
1830 // add dummy deleted xattr with old name
1831
1832 dummyfcb->Vcb = Vcb;
1833 dummyfcb->subvol = fileref->fcb->subvol;
1834 dummyfcb->inode = fileref->fcb->inode;
1835 dummyfcb->hash = fileref->fcb->hash;
1836 dummyfcb->adsxattr = fileref->fcb->adsxattr;
1837 dummyfcb->adshash = fileref->fcb->adshash;
1838 dummyfcb->ads = true;
1839 dummyfcb->deleted = true;
1840
1841 acquire_fcb_lock_exclusive(Vcb);
1842 add_fcb_to_subvol(dummyfcb);
1843 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all);
1844 dummyfcb->subvol->fcbs_version++;
1845 release_fcb_lock(Vcb);
1846
1847 // FIXME - dummyfileref as well?
1848
1849 mark_fcb_dirty(dummyfcb);
1850
1851 free_fcb(dummyfcb);
1852
1853 return STATUS_SUCCESS;
1854 }
1855
rename_stream(device_extension * Vcb,file_ref * fileref,ccb * ccb,FILE_RENAME_INFORMATION_EX * fri,ULONG flags,PIRP Irp,LIST_ENTRY * rollback)1856 static NTSTATUS rename_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
1857 ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
1858 NTSTATUS Status;
1859 UNICODE_STRING fn;
1860 file_ref* sf = NULL;
1861 uint16_t newmaxlen;
1862 ULONG utf8len;
1863 ANSI_STRING utf8;
1864 UNICODE_STRING utf16, utf16uc;
1865 ANSI_STRING adsxattr;
1866 uint32_t crc32;
1867 fcb* dummyfcb;
1868
1869 static const WCHAR datasuf[] = L":$DATA";
1870 static const char xapref[] = "user.";
1871
1872 if (!fileref) {
1873 ERR("fileref not set\n");
1874 return STATUS_INVALID_PARAMETER;
1875 }
1876
1877 if (!fileref->parent) {
1878 ERR("fileref->parent not set\n");
1879 return STATUS_INVALID_PARAMETER;
1880 }
1881
1882 if (fri->FileNameLength < sizeof(WCHAR)) {
1883 WARN("filename too short\n");
1884 return STATUS_OBJECT_NAME_INVALID;
1885 }
1886
1887 if (fri->FileName[0] != ':') {
1888 WARN("destination filename must begin with a colon\n");
1889 return STATUS_INVALID_PARAMETER;
1890 }
1891
1892 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
1893 WARN("insufficient permissions\n");
1894 return STATUS_ACCESS_DENIED;
1895 }
1896
1897 fn.Buffer = &fri->FileName[1];
1898 fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
1899
1900 // remove :$DATA suffix
1901 if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
1902 RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
1903 fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
1904
1905 if (fn.Length == 0)
1906 return rename_stream_to_file(Vcb, fileref, ccb, flags, Irp, rollback);
1907
1908 Status = check_file_name_valid(&fn, false, true);
1909 if (!NT_SUCCESS(Status)) {
1910 WARN("invalid stream name %.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
1911 return Status;
1912 }
1913
1914 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) {
1915 WARN("trying to rename stream on readonly file\n");
1916 return STATUS_ACCESS_DENIED;
1917 }
1918
1919 Status = open_fileref_child(Vcb, fileref->parent, &fn, fileref->parent->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
1920 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
1921 if (Status == STATUS_SUCCESS) {
1922 if (fileref == sf || sf->deleted) {
1923 free_fileref(sf);
1924 sf = NULL;
1925 } else {
1926 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
1927 Status = STATUS_OBJECT_NAME_COLLISION;
1928 goto end;
1929 }
1930
1931 // FIXME - POSIX overwrites of stream?
1932
1933 if (sf->open_count > 0) {
1934 WARN("trying to overwrite open file\n");
1935 Status = STATUS_ACCESS_DENIED;
1936 goto end;
1937 }
1938
1939 if (sf->fcb->adsdata.Length > 0) {
1940 WARN("can only overwrite existing stream if it is zero-length\n");
1941 Status = STATUS_INVALID_PARAMETER;
1942 goto end;
1943 }
1944
1945 Status = delete_fileref(sf, NULL, false, Irp, rollback);
1946 if (!NT_SUCCESS(Status)) {
1947 ERR("delete_fileref returned %08lx\n", Status);
1948 goto end;
1949 }
1950 }
1951 } else {
1952 ERR("open_fileref_child returned %08lx\n", Status);
1953 goto end;
1954 }
1955 }
1956
1957 Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
1958 if (!NT_SUCCESS(Status))
1959 goto end;
1960
1961 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
1962 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
1963 if (!utf8.Buffer) {
1964 ERR("out of memory\n");
1965 Status = STATUS_INSUFFICIENT_RESOURCES;
1966 goto end;
1967 }
1968
1969 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
1970 if (!NT_SUCCESS(Status)) {
1971 ExFreePool(utf8.Buffer);
1972 goto end;
1973 }
1974
1975 adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
1976 adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
1977 if (!adsxattr.Buffer) {
1978 ERR("out of memory\n");
1979 Status = STATUS_INSUFFICIENT_RESOURCES;
1980 ExFreePool(utf8.Buffer);
1981 goto end;
1982 }
1983
1984 RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
1985 RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
1986
1987 // don't allow if it's one of our reserved names
1988
1989 if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
1990 (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
1991 (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
1992 (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
1993 Status = STATUS_OBJECT_NAME_INVALID;
1994 ExFreePool(utf8.Buffer);
1995 ExFreePool(adsxattr.Buffer);
1996 goto end;
1997 }
1998
1999 utf16.Length = utf16.MaximumLength = fn.Length;
2000 utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
2001 if (!utf16.Buffer) {
2002 ERR("out of memory\n");
2003 Status = STATUS_INSUFFICIENT_RESOURCES;
2004 ExFreePool(utf8.Buffer);
2005 ExFreePool(adsxattr.Buffer);
2006 goto end;
2007 }
2008
2009 RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
2010
2011 newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
2012 offsetof(DIR_ITEM, name[0]);
2013
2014 if (newmaxlen < adsxattr.Length) {
2015 WARN("cannot rename as data too long\n");
2016 Status = STATUS_INVALID_PARAMETER;
2017 ExFreePool(utf8.Buffer);
2018 ExFreePool(utf16.Buffer);
2019 ExFreePool(adsxattr.Buffer);
2020 goto end;
2021 }
2022
2023 newmaxlen -= adsxattr.Length;
2024
2025 if (newmaxlen < fileref->fcb->adsdata.Length) {
2026 WARN("cannot rename as data too long\n");
2027 Status = STATUS_INVALID_PARAMETER;
2028 ExFreePool(utf8.Buffer);
2029 ExFreePool(utf16.Buffer);
2030 ExFreePool(adsxattr.Buffer);
2031 goto end;
2032 }
2033
2034 Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
2035 if (!NT_SUCCESS(Status)) {
2036 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2037 ExFreePool(utf8.Buffer);
2038 ExFreePool(utf16.Buffer);
2039 ExFreePool(adsxattr.Buffer);
2040 goto end;
2041 }
2042
2043 // add dummy deleted xattr with old name
2044
2045 dummyfcb = create_fcb(Vcb, PagedPool);
2046 if (!dummyfcb) {
2047 ERR("out of memory\n");
2048 Status = STATUS_INSUFFICIENT_RESOURCES;
2049 ExFreePool(utf8.Buffer);
2050 ExFreePool(utf16.Buffer);
2051 ExFreePool(utf16uc.Buffer);
2052 ExFreePool(adsxattr.Buffer);
2053 goto end;
2054 }
2055
2056 dummyfcb->Vcb = Vcb;
2057 dummyfcb->subvol = fileref->fcb->subvol;
2058 dummyfcb->inode = fileref->fcb->inode;
2059 dummyfcb->hash = fileref->fcb->hash;
2060 dummyfcb->adsxattr = fileref->fcb->adsxattr;
2061 dummyfcb->adshash = fileref->fcb->adshash;
2062 dummyfcb->ads = true;
2063 dummyfcb->deleted = true;
2064
2065 acquire_fcb_lock_exclusive(Vcb);
2066 add_fcb_to_subvol(dummyfcb);
2067 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all);
2068 dummyfcb->subvol->fcbs_version++;
2069 release_fcb_lock(Vcb);
2070
2071 mark_fcb_dirty(dummyfcb);
2072
2073 free_fcb(dummyfcb);
2074
2075 // change fcb values
2076
2077 fileref->dc->utf8 = utf8;
2078 fileref->dc->name = utf16;
2079 fileref->dc->name_uc = utf16uc;
2080
2081 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
2082
2083 fileref->fcb->adsxattr = adsxattr;
2084 fileref->fcb->adshash = crc32;
2085 fileref->fcb->adsmaxlen = newmaxlen;
2086
2087 fileref->fcb->created = true;
2088
2089 mark_fcb_dirty(fileref->fcb);
2090
2091 Status = STATUS_SUCCESS;
2092
2093 end:
2094 if (sf)
2095 free_fileref(sf);
2096
2097 return Status;
2098 }
2099
rename_file_to_stream(device_extension * Vcb,file_ref * fileref,ccb * ccb,FILE_RENAME_INFORMATION_EX * fri,ULONG flags,PIRP Irp,LIST_ENTRY * rollback)2100 static NTSTATUS rename_file_to_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri,
2101 ULONG flags, PIRP Irp, LIST_ENTRY* rollback) {
2102 NTSTATUS Status;
2103 UNICODE_STRING fn;
2104 file_ref* sf = NULL;
2105 uint16_t newmaxlen;
2106 ULONG utf8len;
2107 ANSI_STRING utf8;
2108 UNICODE_STRING utf16, utf16uc;
2109 ANSI_STRING adsxattr, adsdata;
2110 uint32_t crc32;
2111 fcb* dummyfcb;
2112 file_ref* dummyfileref;
2113 dir_child* dc;
2114 LIST_ENTRY* le;
2115
2116 static const WCHAR datasuf[] = L":$DATA";
2117 static const char xapref[] = "user.";
2118
2119 if (!fileref) {
2120 ERR("fileref not set\n");
2121 return STATUS_INVALID_PARAMETER;
2122 }
2123
2124 if (fri->FileNameLength < sizeof(WCHAR)) {
2125 WARN("filename too short\n");
2126 return STATUS_OBJECT_NAME_INVALID;
2127 }
2128
2129 if (fri->FileName[0] != ':') {
2130 WARN("destination filename must begin with a colon\n");
2131 return STATUS_INVALID_PARAMETER;
2132 }
2133
2134 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) {
2135 WARN("insufficient permissions\n");
2136 return STATUS_ACCESS_DENIED;
2137 }
2138
2139 if (fileref->fcb->type != BTRFS_TYPE_FILE)
2140 return STATUS_INVALID_PARAMETER;
2141
2142 fn.Buffer = &fri->FileName[1];
2143 fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR));
2144
2145 // remove :$DATA suffix
2146 if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) &&
2147 RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR))
2148 fn.Length -= sizeof(datasuf) - sizeof(WCHAR);
2149
2150 if (fn.Length == 0) {
2151 WARN("not allowing overwriting file with itself\n");
2152 return STATUS_INVALID_PARAMETER;
2153 }
2154
2155 Status = check_file_name_valid(&fn, false, true);
2156 if (!NT_SUCCESS(Status)) {
2157 WARN("invalid stream name %.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
2158 return Status;
2159 }
2160
2161 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
2162 WARN("trying to rename stream on readonly file\n");
2163 return STATUS_ACCESS_DENIED;
2164 }
2165
2166 Status = open_fileref_child(Vcb, fileref, &fn, fileref->fcb->case_sensitive, true, true, PagedPool, &sf, Irp);
2167 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
2168 if (Status == STATUS_SUCCESS) {
2169 if (fileref == sf || sf->deleted) {
2170 free_fileref(sf);
2171 sf = NULL;
2172 } else {
2173 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
2174 Status = STATUS_OBJECT_NAME_COLLISION;
2175 goto end;
2176 }
2177
2178 // FIXME - POSIX overwrites of stream?
2179
2180 if (sf->open_count > 0) {
2181 WARN("trying to overwrite open file\n");
2182 Status = STATUS_ACCESS_DENIED;
2183 goto end;
2184 }
2185
2186 if (sf->fcb->adsdata.Length > 0) {
2187 WARN("can only overwrite existing stream if it is zero-length\n");
2188 Status = STATUS_INVALID_PARAMETER;
2189 goto end;
2190 }
2191
2192 Status = delete_fileref(sf, NULL, false, Irp, rollback);
2193 if (!NT_SUCCESS(Status)) {
2194 ERR("delete_fileref returned %08lx\n", Status);
2195 goto end;
2196 }
2197 }
2198 } else {
2199 ERR("open_fileref_child returned %08lx\n", Status);
2200 goto end;
2201 }
2202 }
2203
2204 Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length);
2205 if (!NT_SUCCESS(Status))
2206 goto end;
2207
2208 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
2209 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2210 if (!utf8.Buffer) {
2211 ERR("out of memory\n");
2212 Status = STATUS_INSUFFICIENT_RESOURCES;
2213 goto end;
2214 }
2215
2216 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length);
2217 if (!NT_SUCCESS(Status)) {
2218 ExFreePool(utf8.Buffer);
2219 goto end;
2220 }
2221
2222 adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length;
2223 adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG);
2224 if (!adsxattr.Buffer) {
2225 ERR("out of memory\n");
2226 Status = STATUS_INSUFFICIENT_RESOURCES;
2227 ExFreePool(utf8.Buffer);
2228 goto end;
2229 }
2230
2231 RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1);
2232 RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length);
2233
2234 // don't allow if it's one of our reserved names
2235
2236 if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) ||
2237 (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) ||
2238 (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) ||
2239 (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) {
2240 Status = STATUS_OBJECT_NAME_INVALID;
2241 ExFreePool(utf8.Buffer);
2242 ExFreePool(adsxattr.Buffer);
2243 goto end;
2244 }
2245
2246 utf16.Length = utf16.MaximumLength = fn.Length;
2247 utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG);
2248 if (!utf16.Buffer) {
2249 ERR("out of memory\n");
2250 Status = STATUS_INSUFFICIENT_RESOURCES;
2251 ExFreePool(utf8.Buffer);
2252 ExFreePool(adsxattr.Buffer);
2253 goto end;
2254 }
2255
2256 RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length);
2257
2258 newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) -
2259 offsetof(DIR_ITEM, name[0]);
2260
2261 if (newmaxlen < adsxattr.Length) {
2262 WARN("cannot rename as data too long\n");
2263 Status = STATUS_INVALID_PARAMETER;
2264 ExFreePool(utf8.Buffer);
2265 ExFreePool(utf16.Buffer);
2266 ExFreePool(adsxattr.Buffer);
2267 goto end;
2268 }
2269
2270 newmaxlen -= adsxattr.Length;
2271
2272 if (newmaxlen < fileref->fcb->inode_item.st_size) {
2273 WARN("cannot rename as data too long\n");
2274 Status = STATUS_INVALID_PARAMETER;
2275 ExFreePool(utf8.Buffer);
2276 ExFreePool(utf16.Buffer);
2277 ExFreePool(adsxattr.Buffer);
2278 goto end;
2279 }
2280
2281 Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true);
2282 if (!NT_SUCCESS(Status)) {
2283 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2284 ExFreePool(utf8.Buffer);
2285 ExFreePool(utf16.Buffer);
2286 ExFreePool(adsxattr.Buffer);
2287 goto end;
2288 }
2289
2290 // read existing file data
2291
2292 if (fileref->fcb->inode_item.st_size > 0) {
2293 ULONG bytes_read;
2294
2295 adsdata.Length = adsdata.MaximumLength = (uint16_t)fileref->fcb->inode_item.st_size;
2296
2297 adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, adsdata.MaximumLength, ALLOC_TAG);
2298 if (!adsdata.Buffer) {
2299 ERR("out of memory\n");
2300 Status = STATUS_INSUFFICIENT_RESOURCES;
2301 ExFreePool(utf8.Buffer);
2302 ExFreePool(utf16.Buffer);
2303 ExFreePool(utf16uc.Buffer);
2304 ExFreePool(adsxattr.Buffer);
2305 goto end;
2306 }
2307
2308 Status = read_file(fileref->fcb, (uint8_t*)adsdata.Buffer, 0, adsdata.Length, &bytes_read, Irp);
2309 if (!NT_SUCCESS(Status)) {
2310 ERR("out of memory\n");
2311 Status = STATUS_INSUFFICIENT_RESOURCES;
2312 ExFreePool(utf8.Buffer);
2313 ExFreePool(utf16.Buffer);
2314 ExFreePool(utf16uc.Buffer);
2315 ExFreePool(adsxattr.Buffer);
2316 ExFreePool(adsdata.Buffer);
2317 goto end;
2318 }
2319
2320 if (bytes_read < fileref->fcb->inode_item.st_size) {
2321 ERR("short read\n");
2322 Status = STATUS_INTERNAL_ERROR;
2323 ExFreePool(utf8.Buffer);
2324 ExFreePool(utf16.Buffer);
2325 ExFreePool(utf16uc.Buffer);
2326 ExFreePool(adsxattr.Buffer);
2327 ExFreePool(adsdata.Buffer);
2328 goto end;
2329 }
2330 } else
2331 adsdata.Buffer = NULL;
2332
2333 dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
2334 if (!dc) {
2335 ERR("out of memory\n");
2336 Status = STATUS_INSUFFICIENT_RESOURCES;;
2337 ExFreePool(utf8.Buffer);
2338 ExFreePool(utf16.Buffer);
2339 ExFreePool(utf16uc.Buffer);
2340 ExFreePool(adsxattr.Buffer);
2341
2342 if (adsdata.Buffer)
2343 ExFreePool(adsdata.Buffer);
2344
2345 goto end;
2346 }
2347
2348 // add dummy deleted fcb with old name
2349
2350 Status = duplicate_fcb(fileref->fcb, &dummyfcb);
2351 if (!NT_SUCCESS(Status)) {
2352 ERR("duplicate_fcb returned %08lx\n", Status);
2353 ExFreePool(utf8.Buffer);
2354 ExFreePool(utf16.Buffer);
2355 ExFreePool(utf16uc.Buffer);
2356 ExFreePool(adsxattr.Buffer);
2357
2358 if (adsdata.Buffer)
2359 ExFreePool(adsdata.Buffer);
2360
2361 ExFreePool(dc);
2362
2363 goto end;
2364 }
2365
2366 dummyfileref = create_fileref(Vcb);
2367 if (!dummyfileref) {
2368 ERR("out of memory\n");
2369 Status = STATUS_INSUFFICIENT_RESOURCES;
2370 ExFreePool(utf8.Buffer);
2371 ExFreePool(utf16.Buffer);
2372 ExFreePool(utf16uc.Buffer);
2373 ExFreePool(adsxattr.Buffer);
2374
2375 if (adsdata.Buffer)
2376 ExFreePool(adsdata.Buffer);
2377
2378 ExFreePool(dc);
2379
2380 reap_fcb(dummyfcb);
2381
2382 goto end;
2383 }
2384
2385 dummyfileref->fcb = dummyfcb;
2386
2387 dummyfcb->Vcb = Vcb;
2388 dummyfcb->subvol = fileref->fcb->subvol;
2389 dummyfcb->inode = fileref->fcb->inode;
2390 dummyfcb->hash = fileref->fcb->hash;
2391
2392 if (fileref->fcb->inode_item.st_size > 0) {
2393 Status = excise_extents(Vcb, dummyfcb, 0, sector_align(fileref->fcb->inode_item.st_size, Vcb->superblock.sector_size),
2394 Irp, rollback);
2395 if (!NT_SUCCESS(Status)) {
2396 ERR("excise_extents returned %08lx\n", Status);
2397 ExFreePool(utf8.Buffer);
2398 ExFreePool(utf16.Buffer);
2399 ExFreePool(utf16uc.Buffer);
2400 ExFreePool(adsxattr.Buffer);
2401 ExFreePool(adsdata.Buffer);
2402 ExFreePool(dc);
2403
2404 reap_fileref(Vcb, dummyfileref);
2405 reap_fcb(dummyfcb);
2406
2407 goto end;
2408 }
2409
2410 dummyfcb->inode_item.st_size = 0;
2411 dummyfcb->Header.AllocationSize.QuadPart = 0;
2412 dummyfcb->Header.FileSize.QuadPart = 0;
2413 dummyfcb->Header.ValidDataLength.QuadPart = 0;
2414 }
2415
2416 dummyfcb->hash_ptrs = fileref->fcb->hash_ptrs;
2417 dummyfcb->hash_ptrs_uc = fileref->fcb->hash_ptrs_uc;
2418 dummyfcb->created = fileref->fcb->created;
2419
2420 le = fileref->fcb->extents.Flink;
2421 while (le != &fileref->fcb->extents) {
2422 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2423
2424 ext->ignore = true;
2425
2426 le = le->Flink;
2427 }
2428
2429 while (!IsListEmpty(&fileref->fcb->dir_children_index)) {
2430 InsertTailList(&dummyfcb->dir_children_index, RemoveHeadList(&fileref->fcb->dir_children_index));
2431 }
2432
2433 while (!IsListEmpty(&fileref->fcb->dir_children_hash)) {
2434 InsertTailList(&dummyfcb->dir_children_hash, RemoveHeadList(&fileref->fcb->dir_children_hash));
2435 }
2436
2437 while (!IsListEmpty(&fileref->fcb->dir_children_hash_uc)) {
2438 InsertTailList(&dummyfcb->dir_children_hash_uc, RemoveHeadList(&fileref->fcb->dir_children_hash_uc));
2439 }
2440
2441 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all);
2442
2443 InsertHeadList(fileref->fcb->list_entry.Blink, &dummyfcb->list_entry);
2444
2445 if (fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] == &fileref->fcb->list_entry)
2446 fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] = &dummyfcb->list_entry;
2447
2448 RemoveEntryList(&fileref->fcb->list_entry);
2449 fileref->fcb->list_entry.Flink = fileref->fcb->list_entry.Blink = NULL;
2450
2451 mark_fcb_dirty(dummyfcb);
2452
2453 // create dummy fileref
2454
2455 dummyfileref->oldutf8 = fileref->oldutf8;
2456 dummyfileref->oldindex = fileref->oldindex;
2457 dummyfileref->delete_on_close = fileref->delete_on_close;
2458 dummyfileref->posix_delete = fileref->posix_delete;
2459 dummyfileref->deleted = fileref->deleted;
2460 dummyfileref->created = fileref->created;
2461 dummyfileref->parent = fileref->parent;
2462
2463 while (!IsListEmpty(&fileref->children)) {
2464 file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&fileref->children), file_ref, list_entry);
2465
2466 free_fileref(fr->parent);
2467
2468 fr->parent = dummyfileref;
2469 InterlockedIncrement(&dummyfileref->refcount);
2470
2471 InsertTailList(&dummyfileref->children, &fr->list_entry);
2472 }
2473
2474 InsertTailList(fileref->list_entry.Blink, &dummyfileref->list_entry);
2475
2476 RemoveEntryList(&fileref->list_entry);
2477 InsertTailList(&dummyfileref->children, &fileref->list_entry);
2478
2479 dummyfileref->dc = fileref->dc;
2480 dummyfileref->dc->fileref = dummyfileref;
2481
2482 mark_fileref_dirty(dummyfileref);
2483
2484 // change fcb values
2485
2486 fileref->fcb->hash_ptrs = NULL;
2487 fileref->fcb->hash_ptrs_uc = NULL;
2488
2489 fileref->fcb->ads = true;
2490
2491 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
2492 fileref->oldutf8.Buffer = NULL;
2493
2494 RtlZeroMemory(dc, sizeof(dir_child));
2495
2496 dc->utf8 = utf8;
2497 dc->name = utf16;
2498 dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
2499 dc->name_uc = utf16uc;
2500 dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
2501 dc->fileref = fileref;
2502 InsertTailList(&dummyfcb->dir_children_index, &dc->list_entry_index);
2503
2504 fileref->dc = dc;
2505 fileref->parent = dummyfileref;
2506
2507 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length);
2508
2509 fileref->fcb->adsxattr = adsxattr;
2510 fileref->fcb->adshash = crc32;
2511 fileref->fcb->adsmaxlen = newmaxlen;
2512 fileref->fcb->adsdata = adsdata;
2513
2514 fileref->fcb->created = true;
2515
2516 mark_fcb_dirty(fileref->fcb);
2517
2518 Status = STATUS_SUCCESS;
2519
2520 end:
2521 if (sf)
2522 free_fileref(sf);
2523
2524 return Status;
2525 }
2526
set_rename_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject,PFILE_OBJECT tfo,bool ex)2527 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
2528 FILE_RENAME_INFORMATION_EX* fri = Irp->AssociatedIrp.SystemBuffer;
2529 fcb* fcb = FileObject->FsContext;
2530 ccb* ccb = FileObject->FsContext2;
2531 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
2532 WCHAR* fn;
2533 ULONG fnlen, utf8len, origutf8len;
2534 UNICODE_STRING fnus;
2535 ANSI_STRING utf8;
2536 NTSTATUS Status;
2537 LARGE_INTEGER time;
2538 BTRFS_TIME now;
2539 LIST_ENTRY rollback, *le;
2540 hardlink* hl;
2541 SECURITY_SUBJECT_CONTEXT subjcont;
2542 ACCESS_MASK access;
2543 ULONG flags;
2544
2545 InitializeListHead(&rollback);
2546
2547 if (ex)
2548 flags = fri->Flags;
2549 else
2550 flags = fri->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0;
2551
2552 TRACE("tfo = %p\n", tfo);
2553 TRACE("Flags = %lx\n", flags);
2554 TRACE("RootDirectory = %p\n", fri->RootDirectory);
2555 TRACE("FileName = %.*S\n", (int)(fri->FileNameLength / sizeof(WCHAR)), fri->FileName);
2556
2557 fn = fri->FileName;
2558 fnlen = fri->FileNameLength / sizeof(WCHAR);
2559
2560 if (!tfo) {
2561 if (!fileref || !fileref->parent) {
2562 ERR("no fileref set and no directory given\n");
2563 return STATUS_INVALID_PARAMETER;
2564 }
2565 } else {
2566 LONG i;
2567
2568 while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\')) {
2569 fnlen--;
2570 }
2571
2572 if (fnlen == 0)
2573 return STATUS_INVALID_PARAMETER;
2574
2575 for (i = fnlen - 1; i >= 0; i--) {
2576 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
2577 fn = &fri->FileName[i+1];
2578 fnlen -= i + 1;
2579 break;
2580 }
2581 }
2582 }
2583
2584 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
2585 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
2586 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2587
2588 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) {
2589 WARN("not allowing \\$Root to be renamed\n");
2590 Status = STATUS_ACCESS_DENIED;
2591 goto end;
2592 }
2593
2594 if (fcb->ads) {
2595 if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
2596 IO_STATUS_BLOCK iosb;
2597
2598 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2599 if (!NT_SUCCESS(iosb.Status)) {
2600 ERR("CcFlushCache returned %08lx\n", iosb.Status);
2601 Status = iosb.Status;
2602 goto end;
2603 }
2604 }
2605
2606 Status = rename_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
2607 goto end;
2608 } else if (fnlen >= 1 && fn[0] == ':') {
2609 if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) {
2610 IO_STATUS_BLOCK iosb;
2611
2612 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2613 if (!NT_SUCCESS(iosb.Status)) {
2614 ERR("CcFlushCache returned %08lx\n", iosb.Status);
2615 Status = iosb.Status;
2616 goto end;
2617 }
2618 }
2619
2620 Status = rename_file_to_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback);
2621 goto end;
2622 }
2623
2624 fnus.Buffer = fn;
2625 fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR));
2626
2627 TRACE("fnus = %.*S\n", (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer);
2628
2629 Status = check_file_name_valid(&fnus, false, false);
2630 if (!NT_SUCCESS(Status))
2631 goto end;
2632
2633 origutf8len = fileref->dc->utf8.Length;
2634
2635 Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2636 if (!NT_SUCCESS(Status))
2637 goto end;
2638
2639 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
2640 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
2641 if (!utf8.Buffer) {
2642 ERR("out of memory\n");
2643 Status = STATUS_INSUFFICIENT_RESOURCES;
2644 goto end;
2645 }
2646
2647 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
2648 if (!NT_SUCCESS(Status))
2649 goto end;
2650
2651 if (tfo && tfo->FsContext2) {
2652 struct _ccb* relatedccb = tfo->FsContext2;
2653
2654 related = relatedccb->fileref;
2655 increase_fileref_refcount(related);
2656 } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') {
2657 related = fileref->parent;
2658 increase_fileref_refcount(related);
2659 }
2660
2661 Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2662
2663 if (NT_SUCCESS(Status)) {
2664 TRACE("destination file already exists\n");
2665
2666 if (fileref != oldfileref && !oldfileref->deleted) {
2667 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
2668 Status = STATUS_OBJECT_NAME_COLLISION;
2669 goto end;
2670 } else if (fileref == oldfileref) {
2671 Status = STATUS_ACCESS_DENIED;
2672 goto end;
2673 } else if (!(flags & FILE_RENAME_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
2674 WARN("trying to overwrite open file\n");
2675 Status = STATUS_ACCESS_DENIED;
2676 goto end;
2677 } else if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
2678 WARN("trying to overwrite readonly file\n");
2679 Status = STATUS_ACCESS_DENIED;
2680 goto end;
2681 } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
2682 WARN("trying to overwrite directory\n");
2683 Status = STATUS_ACCESS_DENIED;
2684 goto end;
2685 }
2686 }
2687
2688 if (fileref == oldfileref || oldfileref->deleted) {
2689 free_fileref(oldfileref);
2690 oldfileref = NULL;
2691 }
2692 }
2693
2694 if (!related) {
2695 Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
2696
2697 if (!NT_SUCCESS(Status)) {
2698 ERR("open_fileref returned %08lx\n", Status);
2699 goto end;
2700 }
2701 }
2702
2703 if (related->fcb == Vcb->dummy_fcb) {
2704 Status = STATUS_ACCESS_DENIED;
2705 goto end;
2706 }
2707
2708 SeCaptureSubjectContext(&subjcont);
2709
2710 if (!SeAccessCheck(related->fcb->sd, &subjcont, false, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
2711 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2712 SeReleaseSubjectContext(&subjcont);
2713 TRACE("SeAccessCheck failed, returning %08lx\n", Status);
2714 goto end;
2715 }
2716
2717 SeReleaseSubjectContext(&subjcont);
2718
2719 if (has_open_children(fileref)) {
2720 WARN("trying to rename file with open children\n");
2721 Status = STATUS_ACCESS_DENIED;
2722 goto end;
2723 }
2724
2725 if (oldfileref) {
2726 SeCaptureSubjectContext(&subjcont);
2727
2728 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL,
2729 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
2730 SeReleaseSubjectContext(&subjcont);
2731 TRACE("SeAccessCheck failed, returning %08lx\n", Status);
2732 goto end;
2733 }
2734
2735 SeReleaseSubjectContext(&subjcont);
2736
2737 if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
2738 oldfileref->delete_on_close = true;
2739 oldfileref->posix_delete = true;
2740 }
2741
2742 Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
2743 if (!NT_SUCCESS(Status)) {
2744 ERR("delete_fileref returned %08lx\n", Status);
2745 goto end;
2746 }
2747 }
2748
2749 if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) {
2750 Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
2751 if (!NT_SUCCESS(Status)) {
2752 ERR("move_across_subvols returned %08lx\n", Status);
2753 }
2754 goto end;
2755 }
2756
2757 if (related == fileref->parent) { // keeping file in same directory
2758 UNICODE_STRING oldfn, newfn;
2759 USHORT name_offset;
2760 ULONG reqlen, oldutf8len;
2761
2762 oldfn.Length = oldfn.MaximumLength = 0;
2763
2764 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
2765 if (Status != STATUS_BUFFER_OVERFLOW) {
2766 ERR("fileref_get_filename returned %08lx\n", Status);
2767 goto end;
2768 }
2769
2770 oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
2771 if (!oldfn.Buffer) {
2772 ERR("out of memory\n");
2773 Status = STATUS_INSUFFICIENT_RESOURCES;
2774 goto end;
2775 }
2776
2777 oldfn.MaximumLength = (uint16_t)reqlen;
2778
2779 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
2780 if (!NT_SUCCESS(Status)) {
2781 ERR("fileref_get_filename returned %08lx\n", Status);
2782 ExFreePool(oldfn.Buffer);
2783 goto end;
2784 }
2785
2786 oldutf8len = fileref->dc->utf8.Length;
2787
2788 if (!fileref->created && !fileref->oldutf8.Buffer) {
2789 fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
2790 if (!fileref->oldutf8.Buffer) {
2791 ERR("out of memory\n");
2792 Status = STATUS_INSUFFICIENT_RESOURCES;
2793 goto end;
2794 }
2795
2796 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
2797 RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2798 }
2799
2800 TRACE("renaming %.*S to %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer, (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer);
2801
2802 mark_fileref_dirty(fileref);
2803
2804 if (fileref->dc) {
2805 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2806
2807 ExFreePool(fileref->dc->utf8.Buffer);
2808 ExFreePool(fileref->dc->name.Buffer);
2809 ExFreePool(fileref->dc->name_uc.Buffer);
2810
2811 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
2812 if (!fileref->dc->utf8.Buffer) {
2813 ERR("out of memory\n");
2814 Status = STATUS_INSUFFICIENT_RESOURCES;
2815 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2816 ExFreePool(oldfn.Buffer);
2817 goto end;
2818 }
2819
2820 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
2821 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
2822
2823 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2824 if (!fileref->dc->name.Buffer) {
2825 ERR("out of memory\n");
2826 Status = STATUS_INSUFFICIENT_RESOURCES;
2827 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2828 ExFreePool(oldfn.Buffer);
2829 goto end;
2830 }
2831
2832 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
2833 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
2834
2835 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
2836 if (!NT_SUCCESS(Status)) {
2837 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2838 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2839 ExFreePool(oldfn.Buffer);
2840 goto end;
2841 }
2842
2843 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2844
2845 fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length);
2846 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
2847
2848 insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc);
2849
2850 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2851 }
2852
2853 newfn.Length = newfn.MaximumLength = 0;
2854
2855 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
2856 if (Status != STATUS_BUFFER_OVERFLOW) {
2857 ERR("fileref_get_filename returned %08lx\n", Status);
2858 ExFreePool(oldfn.Buffer);
2859 goto end;
2860 }
2861
2862 newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
2863 if (!newfn.Buffer) {
2864 ERR("out of memory\n");
2865 Status = STATUS_INSUFFICIENT_RESOURCES;
2866 ExFreePool(oldfn.Buffer);
2867 goto end;
2868 }
2869
2870 newfn.MaximumLength = (uint16_t)reqlen;
2871
2872 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
2873 if (!NT_SUCCESS(Status)) {
2874 ERR("fileref_get_filename returned %08lx\n", Status);
2875 ExFreePool(oldfn.Buffer);
2876 ExFreePool(newfn.Buffer);
2877 goto end;
2878 }
2879
2880 KeQuerySystemTime(&time);
2881 win_time_to_unix(time, &now);
2882
2883 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
2884 fcb->inode_item.transid = Vcb->superblock.generation;
2885 fcb->inode_item.sequence++;
2886
2887 if (!ccb->user_set_change_time)
2888 fcb->inode_item.st_ctime = now;
2889
2890 fcb->inode_item_changed = true;
2891 mark_fcb_dirty(fcb);
2892 }
2893
2894 // update parent's INODE_ITEM
2895
2896 related->fcb->inode_item.transid = Vcb->superblock.generation;
2897 TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
2898 related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len);
2899 TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
2900 related->fcb->inode_item.sequence++;
2901 related->fcb->inode_item.st_ctime = now;
2902 related->fcb->inode_item.st_mtime = now;
2903
2904 related->fcb->inode_item_changed = true;
2905 mark_fcb_dirty(related->fcb);
2906 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2907
2908 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL,
2909 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL);
2910 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL,
2911 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL);
2912
2913 ExFreePool(oldfn.Buffer);
2914 ExFreePool(newfn.Buffer);
2915
2916 Status = STATUS_SUCCESS;
2917 goto end;
2918 }
2919
2920 // We move files by moving the existing fileref to the new directory, and
2921 // replacing it with a dummy fileref with the same original values, but marked as deleted.
2922
2923 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
2924
2925 fr2 = create_fileref(Vcb);
2926
2927 fr2->fcb = fileref->fcb;
2928 fr2->fcb->refcount++;
2929
2930 fr2->oldutf8 = fileref->oldutf8;
2931 fr2->oldindex = fileref->dc->index;
2932 fr2->delete_on_close = fileref->delete_on_close;
2933 fr2->deleted = true;
2934 fr2->created = fileref->created;
2935 fr2->parent = fileref->parent;
2936 fr2->dc = NULL;
2937
2938 if (!fr2->oldutf8.Buffer) {
2939 fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
2940 if (!fr2->oldutf8.Buffer) {
2941 ERR("out of memory\n");
2942 Status = STATUS_INSUFFICIENT_RESOURCES;
2943 goto end;
2944 }
2945
2946 RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
2947
2948 fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
2949 }
2950
2951 if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
2952 fr2->fcb->fileref = fr2;
2953
2954 if (fileref->fcb->inode == SUBVOL_ROOT_INODE)
2955 fileref->fcb->subvol->parent = related->fcb->subvol->id;
2956
2957 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
2958 fileref->oldutf8.Buffer = NULL;
2959 fileref->deleted = false;
2960 fileref->created = true;
2961 fileref->parent = related;
2962
2963 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2964 InsertHeadList(&fileref->list_entry, &fr2->list_entry);
2965 RemoveEntryList(&fileref->list_entry);
2966 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2967
2968 mark_fileref_dirty(fr2);
2969 mark_fileref_dirty(fileref);
2970
2971 if (fileref->dc) {
2972 // remove from old parent
2973 ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, true);
2974 RemoveEntryList(&fileref->dc->list_entry_index);
2975 remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
2976 ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
2977
2978 if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
2979 // handle changed name
2980
2981 ExFreePool(fileref->dc->utf8.Buffer);
2982 ExFreePool(fileref->dc->name.Buffer);
2983 ExFreePool(fileref->dc->name_uc.Buffer);
2984
2985 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG);
2986 if (!fileref->dc->utf8.Buffer) {
2987 ERR("out of memory\n");
2988 Status = STATUS_INSUFFICIENT_RESOURCES;
2989 goto end;
2990 }
2991
2992 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
2993 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
2994
2995 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
2996 if (!fileref->dc->name.Buffer) {
2997 ERR("out of memory\n");
2998 Status = STATUS_INSUFFICIENT_RESOURCES;
2999 goto end;
3000 }
3001
3002 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
3003 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
3004
3005 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true);
3006 if (!NT_SUCCESS(Status)) {
3007 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
3008 goto end;
3009 }
3010
3011 fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length);
3012 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
3013 }
3014
3015 // add to new parent
3016 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
3017
3018 if (IsListEmpty(&related->fcb->dir_children_index))
3019 fileref->dc->index = 2;
3020 else {
3021 dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
3022
3023 fileref->dc->index = max(2, dc2->index + 1);
3024 }
3025
3026 InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
3027 insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
3028 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
3029 }
3030
3031 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
3032 InsertTailList(&related->children, &fileref->list_entry);
3033 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
3034
3035 if (fcb->inode_item.st_nlink > 1) {
3036 // add new hardlink entry to fcb
3037
3038 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3039 if (!hl) {
3040 ERR("out of memory\n");
3041 Status = STATUS_INSUFFICIENT_RESOURCES;
3042 goto end;
3043 }
3044
3045 hl->parent = related->fcb->inode;
3046 hl->index = fileref->dc->index;
3047
3048 hl->name.Length = hl->name.MaximumLength = fnus.Length;
3049 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
3050
3051 if (!hl->name.Buffer) {
3052 ERR("out of memory\n");
3053 ExFreePool(hl);
3054 Status = STATUS_INSUFFICIENT_RESOURCES;
3055 goto end;
3056 }
3057
3058 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3059
3060 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
3061 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3062
3063 if (!hl->utf8.Buffer) {
3064 ERR("out of memory\n");
3065 ExFreePool(hl->name.Buffer);
3066 ExFreePool(hl);
3067 Status = STATUS_INSUFFICIENT_RESOURCES;
3068 goto end;
3069 }
3070
3071 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
3072
3073 InsertTailList(&fcb->hardlinks, &hl->list_entry);
3074 }
3075
3076 // delete old hardlink entry from fcb
3077
3078 le = fcb->hardlinks.Flink;
3079 while (le != &fcb->hardlinks) {
3080 hl = CONTAINING_RECORD(le, hardlink, list_entry);
3081
3082 if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
3083 RemoveEntryList(&hl->list_entry);
3084
3085 if (hl->utf8.Buffer)
3086 ExFreePool(hl->utf8.Buffer);
3087
3088 if (hl->name.Buffer)
3089 ExFreePool(hl->name.Buffer);
3090
3091 ExFreePool(hl);
3092 break;
3093 }
3094
3095 le = le->Flink;
3096 }
3097
3098 // update inode's INODE_ITEM
3099
3100 KeQuerySystemTime(&time);
3101 win_time_to_unix(time, &now);
3102
3103 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) {
3104 fcb->inode_item.transid = Vcb->superblock.generation;
3105 fcb->inode_item.sequence++;
3106
3107 if (!ccb->user_set_change_time)
3108 fcb->inode_item.st_ctime = now;
3109
3110 fcb->inode_item_changed = true;
3111 mark_fcb_dirty(fcb);
3112 }
3113
3114 // update new parent's INODE_ITEM
3115
3116 related->fcb->inode_item.transid = Vcb->superblock.generation;
3117 TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
3118 related->fcb->inode_item.st_size += 2 * utf8len;
3119 TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size);
3120 related->fcb->inode_item.sequence++;
3121 related->fcb->inode_item.st_ctime = now;
3122 related->fcb->inode_item.st_mtime = now;
3123
3124 related->fcb->inode_item_changed = true;
3125 mark_fcb_dirty(related->fcb);
3126
3127 // update old parent's INODE_ITEM
3128
3129 fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
3130 TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
3131 fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
3132 TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
3133 fr2->parent->fcb->inode_item.sequence++;
3134 fr2->parent->fcb->inode_item.st_ctime = now;
3135 fr2->parent->fcb->inode_item.st_mtime = now;
3136
3137 free_fileref(fr2);
3138
3139 fr2->parent->fcb->inode_item_changed = true;
3140 mark_fcb_dirty(fr2->parent->fcb);
3141
3142 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3143 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3144 send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3145
3146 Status = STATUS_SUCCESS;
3147
3148 end:
3149 if (oldfileref)
3150 free_fileref(oldfileref);
3151
3152 if (!NT_SUCCESS(Status) && related)
3153 free_fileref(related);
3154
3155 if (!NT_SUCCESS(Status) && fr2)
3156 free_fileref(fr2);
3157
3158 if (NT_SUCCESS(Status))
3159 clear_rollback(&rollback);
3160 else
3161 do_rollback(Vcb, &rollback);
3162
3163 ExReleaseResourceLite(fcb->Header.Resource);
3164 ExReleaseResourceLite(&Vcb->fileref_lock);
3165 ExReleaseResourceLite(&Vcb->tree_lock);
3166
3167 return Status;
3168 }
3169
stream_set_end_of_file_information(device_extension * Vcb,uint16_t end,fcb * fcb,file_ref * fileref,bool advance_only)3170 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, uint16_t end, fcb* fcb, file_ref* fileref, bool advance_only) {
3171 LARGE_INTEGER time;
3172 BTRFS_TIME now;
3173
3174 TRACE("setting new end to %x bytes (currently %x)\n", end, fcb->adsdata.Length);
3175
3176 if (!fileref || !fileref->parent) {
3177 ERR("no fileref for stream\n");
3178 return STATUS_INTERNAL_ERROR;
3179 }
3180
3181 if (end < fcb->adsdata.Length) {
3182 if (advance_only)
3183 return STATUS_SUCCESS;
3184
3185 TRACE("truncating stream to %x bytes\n", end);
3186
3187 fcb->adsdata.Length = end;
3188 } else if (end > fcb->adsdata.Length) {
3189 TRACE("extending stream to %x bytes\n", end);
3190
3191 if (end > fcb->adsmaxlen) {
3192 ERR("error - xattr too long (%u > %lu)\n", end, fcb->adsmaxlen);
3193 return STATUS_DISK_FULL;
3194 }
3195
3196 if (end > fcb->adsdata.MaximumLength) {
3197 char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
3198 if (!data) {
3199 ERR("out of memory\n");
3200 ExFreePool(data);
3201 return STATUS_INSUFFICIENT_RESOURCES;
3202 }
3203
3204 if (fcb->adsdata.Buffer) {
3205 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length);
3206 ExFreePool(fcb->adsdata.Buffer);
3207 }
3208
3209 fcb->adsdata.Buffer = data;
3210 fcb->adsdata.MaximumLength = end;
3211 }
3212
3213 RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length);
3214
3215 fcb->adsdata.Length = end;
3216 }
3217
3218 mark_fcb_dirty(fcb);
3219
3220 fcb->Header.AllocationSize.QuadPart = end;
3221 fcb->Header.FileSize.QuadPart = end;
3222 fcb->Header.ValidDataLength.QuadPart = end;
3223
3224 KeQuerySystemTime(&time);
3225 win_time_to_unix(time, &now);
3226
3227 fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation;
3228 fileref->parent->fcb->inode_item.sequence++;
3229 fileref->parent->fcb->inode_item.st_ctime = now;
3230
3231 fileref->parent->fcb->inode_item_changed = true;
3232 mark_fcb_dirty(fileref->parent->fcb);
3233
3234 fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
3235 fileref->parent->fcb->subvol->root_item.ctime = now;
3236
3237 return STATUS_SUCCESS;
3238 }
3239
set_end_of_file_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject,bool advance_only,bool prealloc)3240 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool advance_only, bool prealloc) {
3241 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer;
3242 fcb* fcb = FileObject->FsContext;
3243 ccb* ccb = FileObject->FsContext2;
3244 file_ref* fileref = ccb ? ccb->fileref : NULL;
3245 NTSTATUS Status;
3246 LARGE_INTEGER time;
3247 CC_FILE_SIZES ccfs;
3248 LIST_ENTRY rollback;
3249 bool set_size = false;
3250 ULONG filter;
3251 uint64_t new_end_of_file;
3252
3253 if (!fileref) {
3254 ERR("fileref is NULL\n");
3255 return STATUS_INVALID_PARAMETER;
3256 }
3257
3258 InitializeListHead(&rollback);
3259
3260 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3261
3262 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3263
3264 if (fileref ? fileref->deleted : fcb->deleted) {
3265 Status = STATUS_FILE_CLOSED;
3266 goto end;
3267 }
3268
3269 if (fcb->ads) {
3270 if (feofi->EndOfFile.QuadPart > 0xffff) {
3271 Status = STATUS_DISK_FULL;
3272 goto end;
3273 }
3274
3275 if (feofi->EndOfFile.QuadPart < 0) {
3276 Status = STATUS_INVALID_PARAMETER;
3277 goto end;
3278 }
3279
3280 Status = stream_set_end_of_file_information(Vcb, (uint16_t)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only);
3281
3282 if (NT_SUCCESS(Status)) {
3283 ccfs.AllocationSize = fcb->Header.AllocationSize;
3284 ccfs.FileSize = fcb->Header.FileSize;
3285 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
3286 set_size = true;
3287 }
3288
3289 filter = FILE_NOTIFY_CHANGE_STREAM_SIZE;
3290
3291 if (!ccb->user_set_write_time) {
3292 KeQuerySystemTime(&time);
3293 win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime);
3294 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3295
3296 fileref->parent->fcb->inode_item_changed = true;
3297 mark_fcb_dirty(fileref->parent->fcb);
3298 }
3299
3300 queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name);
3301
3302 goto end;
3303 }
3304
3305 TRACE("file: %p\n", FileObject);
3306 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "true" : "false");
3307 TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n",
3308 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
3309
3310 new_end_of_file = feofi->EndOfFile.QuadPart;
3311
3312 /* The lazy writer sometimes tries to round files to the next page size through CcSetValidData -
3313 * ignore these. See fastfat!FatSetEndOfFileInfo, where Microsoft does the same as we're
3314 * doing below. */
3315 if (advance_only && new_end_of_file >= (uint64_t)fcb->Header.FileSize.QuadPart)
3316 new_end_of_file = fcb->Header.FileSize.QuadPart;
3317
3318 TRACE("setting new end to %I64x bytes (currently %I64x)\n", new_end_of_file, fcb->inode_item.st_size);
3319
3320 if (new_end_of_file < fcb->inode_item.st_size) {
3321 if (advance_only) {
3322 Status = STATUS_SUCCESS;
3323 goto end;
3324 }
3325
3326 TRACE("truncating file to %I64x bytes\n", new_end_of_file);
3327
3328 if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
3329 Status = STATUS_USER_MAPPED_FILE;
3330 goto end;
3331 }
3332
3333 Status = truncate_file(fcb, new_end_of_file, Irp, &rollback);
3334 if (!NT_SUCCESS(Status)) {
3335 ERR("error - truncate_file failed\n");
3336 goto end;
3337 }
3338 } else if (new_end_of_file > fcb->inode_item.st_size) {
3339 TRACE("extending file to %I64x bytes\n", new_end_of_file);
3340
3341 Status = extend_file(fcb, fileref, new_end_of_file, prealloc, NULL, &rollback);
3342 if (!NT_SUCCESS(Status)) {
3343 ERR("error - extend_file failed\n");
3344 goto end;
3345 }
3346 } else if (new_end_of_file == fcb->inode_item.st_size && advance_only) {
3347 Status = STATUS_SUCCESS;
3348 goto end;
3349 }
3350
3351 ccfs.AllocationSize = fcb->Header.AllocationSize;
3352 ccfs.FileSize = fcb->Header.FileSize;
3353 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
3354 set_size = true;
3355
3356 filter = FILE_NOTIFY_CHANGE_SIZE;
3357
3358 if (!ccb->user_set_write_time) {
3359 KeQuerySystemTime(&time);
3360 win_time_to_unix(time, &fcb->inode_item.st_mtime);
3361 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3362 }
3363
3364 fcb->inode_item_changed = true;
3365 mark_fcb_dirty(fcb);
3366 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3367
3368 Status = STATUS_SUCCESS;
3369
3370 end:
3371 if (NT_SUCCESS(Status))
3372 clear_rollback(&rollback);
3373 else
3374 do_rollback(Vcb, &rollback);
3375
3376 ExReleaseResourceLite(fcb->Header.Resource);
3377
3378 if (set_size) {
3379 _SEH2_TRY {
3380 CcSetFileSizes(FileObject, &ccfs);
3381 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3382 Status = _SEH2_GetExceptionCode();
3383 } _SEH2_END;
3384
3385 if (!NT_SUCCESS(Status))
3386 ERR("CcSetFileSizes threw exception %08lx\n", Status);
3387 }
3388
3389 ExReleaseResourceLite(&Vcb->tree_lock);
3390
3391 return Status;
3392 }
3393
set_position_information(PFILE_OBJECT FileObject,PIRP Irp)3394 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
3395 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3396
3397 TRACE("setting the position on %p to %I64x\n", FileObject, fpi->CurrentByteOffset.QuadPart);
3398
3399 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
3400
3401 FileObject->CurrentByteOffset = fpi->CurrentByteOffset;
3402
3403 return STATUS_SUCCESS;
3404 }
3405
set_link_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject,PFILE_OBJECT tfo,bool ex)3406 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) {
3407 FILE_LINK_INFORMATION_EX* fli = Irp->AssociatedIrp.SystemBuffer;
3408 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
3409 ccb* ccb = FileObject->FsContext2;
3410 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
3411 WCHAR* fn;
3412 ULONG fnlen, utf8len;
3413 UNICODE_STRING fnus;
3414 ANSI_STRING utf8;
3415 NTSTATUS Status;
3416 LARGE_INTEGER time;
3417 BTRFS_TIME now;
3418 LIST_ENTRY rollback;
3419 hardlink* hl;
3420 ACCESS_MASK access;
3421 SECURITY_SUBJECT_CONTEXT subjcont;
3422 dir_child* dc = NULL;
3423 ULONG flags;
3424
3425 InitializeListHead(&rollback);
3426
3427 // FIXME - check fli length
3428 // FIXME - don't ignore fli->RootDirectory
3429
3430 if (ex)
3431 flags = fli->Flags;
3432 else
3433 flags = fli->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0;
3434
3435 TRACE("flags = %lx\n", flags);
3436 TRACE("RootDirectory = %p\n", fli->RootDirectory);
3437 TRACE("FileNameLength = %lx\n", fli->FileNameLength);
3438 TRACE("FileName = %.*S\n", (int)(fli->FileNameLength / sizeof(WCHAR)), fli->FileName);
3439
3440 fn = fli->FileName;
3441 fnlen = fli->FileNameLength / sizeof(WCHAR);
3442
3443 if (!tfo) {
3444 if (!fileref || !fileref->parent) {
3445 ERR("no fileref set and no directory given\n");
3446 return STATUS_INVALID_PARAMETER;
3447 }
3448
3449 parfcb = fileref->parent->fcb;
3450 tfofcb = NULL;
3451 } else {
3452 LONG i;
3453
3454 tfofcb = tfo->FsContext;
3455 parfcb = tfofcb;
3456
3457 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\')) {
3458 fnlen--;
3459 }
3460
3461 if (fnlen == 0)
3462 return STATUS_INVALID_PARAMETER;
3463
3464 for (i = fnlen - 1; i >= 0; i--) {
3465 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
3466 fn = &fli->FileName[i+1];
3467 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1;
3468 break;
3469 }
3470 }
3471 }
3472
3473 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3474 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true);
3475 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3476
3477 if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3478 WARN("tried to create hard link on directory\n");
3479 Status = STATUS_FILE_IS_A_DIRECTORY;
3480 goto end;
3481 }
3482
3483 if (fcb->ads) {
3484 WARN("tried to create hard link on stream\n");
3485 Status = STATUS_INVALID_PARAMETER;
3486 goto end;
3487 }
3488
3489 if (fcb->inode_item.st_nlink >= 65535) {
3490 Status = STATUS_TOO_MANY_LINKS;
3491 goto end;
3492 }
3493
3494 fnus.Buffer = fn;
3495 fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR));
3496
3497 TRACE("fnus = %.*S\n", (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer);
3498
3499 Status = check_file_name_valid(&fnus, false, false);
3500 if (!NT_SUCCESS(Status))
3501 goto end;
3502
3503 Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3504 if (!NT_SUCCESS(Status))
3505 goto end;
3506
3507 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len;
3508 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
3509 if (!utf8.Buffer) {
3510 ERR("out of memory\n");
3511 Status = STATUS_INSUFFICIENT_RESOURCES;
3512 goto end;
3513 }
3514
3515 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
3516 if (!NT_SUCCESS(Status))
3517 goto end;
3518
3519 if (tfo && tfo->FsContext2) {
3520 struct _ccb* relatedccb = tfo->FsContext2;
3521
3522 related = relatedccb->fileref;
3523 increase_fileref_refcount(related);
3524 }
3525
3526 Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3527
3528 if (NT_SUCCESS(Status)) {
3529 if (!oldfileref->deleted) {
3530 WARN("destination file already exists\n");
3531
3532 if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) {
3533 Status = STATUS_OBJECT_NAME_COLLISION;
3534 goto end;
3535 } else if (fileref == oldfileref) {
3536 Status = STATUS_ACCESS_DENIED;
3537 goto end;
3538 } else if (!(flags & FILE_LINK_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
3539 WARN("trying to overwrite open file\n");
3540 Status = STATUS_ACCESS_DENIED;
3541 goto end;
3542 } else if (!(flags & FILE_LINK_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
3543 WARN("trying to overwrite readonly file\n");
3544 Status = STATUS_ACCESS_DENIED;
3545 goto end;
3546 } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
3547 WARN("trying to overwrite directory\n");
3548 Status = STATUS_ACCESS_DENIED;
3549 goto end;
3550 }
3551 } else {
3552 free_fileref(oldfileref);
3553 oldfileref = NULL;
3554 }
3555 }
3556
3557 if (!related) {
3558 Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp);
3559
3560 if (!NT_SUCCESS(Status)) {
3561 ERR("open_fileref returned %08lx\n", Status);
3562 goto end;
3563 }
3564 }
3565
3566 SeCaptureSubjectContext(&subjcont);
3567
3568 if (!SeAccessCheck(related->fcb->sd, &subjcont, false, FILE_ADD_FILE, 0, NULL,
3569 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3570 SeReleaseSubjectContext(&subjcont);
3571 TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3572 goto end;
3573 }
3574
3575 SeReleaseSubjectContext(&subjcont);
3576
3577 if (fcb->subvol != parfcb->subvol) {
3578 WARN("can't create hard link over subvolume boundary\n");
3579 Status = STATUS_INVALID_PARAMETER;
3580 goto end;
3581 }
3582
3583 if (oldfileref) {
3584 SeCaptureSubjectContext(&subjcont);
3585
3586 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL,
3587 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
3588 SeReleaseSubjectContext(&subjcont);
3589 TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3590 goto end;
3591 }
3592
3593 SeReleaseSubjectContext(&subjcont);
3594
3595 if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
3596 oldfileref->delete_on_close = true;
3597 oldfileref->posix_delete = true;
3598 }
3599
3600 Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
3601 if (!NT_SUCCESS(Status)) {
3602 ERR("delete_fileref returned %08lx\n", Status);
3603 goto end;
3604 }
3605 }
3606
3607 fr2 = create_fileref(Vcb);
3608
3609 fr2->fcb = fcb;
3610 fcb->refcount++;
3611
3612 fr2->created = true;
3613 fr2->parent = related;
3614
3615 Status = add_dir_child(related->fcb, fcb->inode, false, &utf8, &fnus, fcb->type, &dc);
3616 if (!NT_SUCCESS(Status))
3617 WARN("add_dir_child returned %08lx\n", Status);
3618
3619 fr2->dc = dc;
3620 dc->fileref = fr2;
3621
3622 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true);
3623 InsertTailList(&related->children, &fr2->list_entry);
3624 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
3625
3626 // add hardlink for existing fileref, if it's not there already
3627 if (IsListEmpty(&fcb->hardlinks)) {
3628 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3629 if (!hl) {
3630 ERR("out of memory\n");
3631 Status = STATUS_INSUFFICIENT_RESOURCES;
3632 goto end;
3633 }
3634
3635 hl->parent = fileref->parent->fcb->inode;
3636 hl->index = fileref->dc->index;
3637
3638 hl->name.Length = hl->name.MaximumLength = fnus.Length;
3639 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
3640
3641 if (!hl->name.Buffer) {
3642 ERR("out of memory\n");
3643 ExFreePool(hl);
3644 Status = STATUS_INSUFFICIENT_RESOURCES;
3645 goto end;
3646 }
3647
3648 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3649
3650 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
3651 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3652
3653 if (!hl->utf8.Buffer) {
3654 ERR("out of memory\n");
3655 ExFreePool(hl->name.Buffer);
3656 ExFreePool(hl);
3657 Status = STATUS_INSUFFICIENT_RESOURCES;
3658 goto end;
3659 }
3660
3661 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
3662
3663 InsertTailList(&fcb->hardlinks, &hl->list_entry);
3664 }
3665
3666 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
3667 if (!hl) {
3668 ERR("out of memory\n");
3669 Status = STATUS_INSUFFICIENT_RESOURCES;
3670 goto end;
3671 }
3672
3673 hl->parent = related->fcb->inode;
3674 hl->index = dc->index;
3675
3676 hl->name.Length = hl->name.MaximumLength = fnus.Length;
3677 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
3678
3679 if (!hl->name.Buffer) {
3680 ERR("out of memory\n");
3681 ExFreePool(hl);
3682 Status = STATUS_INSUFFICIENT_RESOURCES;
3683 goto end;
3684 }
3685
3686 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
3687
3688 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length;
3689 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
3690
3691 if (!hl->utf8.Buffer) {
3692 ERR("out of memory\n");
3693 ExFreePool(hl->name.Buffer);
3694 ExFreePool(hl);
3695 Status = STATUS_INSUFFICIENT_RESOURCES;
3696 goto end;
3697 }
3698
3699 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
3700 ExFreePool(utf8.Buffer);
3701
3702 InsertTailList(&fcb->hardlinks, &hl->list_entry);
3703
3704 mark_fileref_dirty(fr2);
3705 free_fileref(fr2);
3706
3707 // update inode's INODE_ITEM
3708
3709 KeQuerySystemTime(&time);
3710 win_time_to_unix(time, &now);
3711
3712 fcb->inode_item.transid = Vcb->superblock.generation;
3713 fcb->inode_item.sequence++;
3714 fcb->inode_item.st_nlink++;
3715
3716 if (!ccb->user_set_change_time)
3717 fcb->inode_item.st_ctime = now;
3718
3719 fcb->inode_item_changed = true;
3720 mark_fcb_dirty(fcb);
3721
3722 // update parent's INODE_ITEM
3723
3724 parfcb->inode_item.transid = Vcb->superblock.generation;
3725 TRACE("parfcb->inode_item.st_size (inode %I64x) was %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3726 parfcb->inode_item.st_size += 2 * utf8len;
3727 TRACE("parfcb->inode_item.st_size (inode %I64x) now %I64x\n", parfcb->inode, parfcb->inode_item.st_size);
3728 parfcb->inode_item.sequence++;
3729 parfcb->inode_item.st_ctime = now;
3730
3731 parfcb->inode_item_changed = true;
3732 mark_fcb_dirty(parfcb);
3733
3734 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3735
3736 Status = STATUS_SUCCESS;
3737
3738 end:
3739 if (oldfileref)
3740 free_fileref(oldfileref);
3741
3742 if (!NT_SUCCESS(Status) && related)
3743 free_fileref(related);
3744
3745 if (!NT_SUCCESS(Status) && fr2)
3746 free_fileref(fr2);
3747
3748 if (NT_SUCCESS(Status))
3749 clear_rollback(&rollback);
3750 else
3751 do_rollback(Vcb, &rollback);
3752
3753 ExReleaseResourceLite(fcb->Header.Resource);
3754 ExReleaseResourceLite(&Vcb->fileref_lock);
3755 ExReleaseResourceLite(&Vcb->tree_lock);
3756
3757 return Status;
3758 }
3759
set_valid_data_length_information(device_extension * Vcb,PIRP Irp,PFILE_OBJECT FileObject)3760 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
3761 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer;
3762 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3763 fcb* fcb = FileObject->FsContext;
3764 ccb* ccb = FileObject->FsContext2;
3765 file_ref* fileref = ccb ? ccb->fileref : NULL;
3766 NTSTATUS Status;
3767 LARGE_INTEGER time;
3768 CC_FILE_SIZES ccfs;
3769 LIST_ENTRY rollback;
3770 bool set_size = false;
3771 ULONG filter;
3772
3773 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) {
3774 ERR("input buffer length was %lu, expected %Iu\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION));
3775 return STATUS_INVALID_PARAMETER;
3776 }
3777
3778 if (!fileref) {
3779 ERR("fileref is NULL\n");
3780 return STATUS_INVALID_PARAMETER;
3781 }
3782
3783 InitializeListHead(&rollback);
3784
3785 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
3786
3787 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
3788
3789 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) {
3790 Status = STATUS_INVALID_PARAMETER;
3791 goto end;
3792 }
3793
3794 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) {
3795 TRACE("invalid VDL of %I64u (current VDL = %I64u, file size = %I64u)\n", fvdli->ValidDataLength.QuadPart,
3796 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart);
3797 Status = STATUS_INVALID_PARAMETER;
3798 goto end;
3799 }
3800
3801 if (fileref ? fileref->deleted : fcb->deleted) {
3802 Status = STATUS_FILE_CLOSED;
3803 goto end;
3804 }
3805
3806 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength,
3807 // and we set it to the max anyway.
3808
3809 ccfs.AllocationSize = fcb->Header.AllocationSize;
3810 ccfs.FileSize = fcb->Header.FileSize;
3811 ccfs.ValidDataLength = fvdli->ValidDataLength;
3812 set_size = true;
3813
3814 filter = FILE_NOTIFY_CHANGE_SIZE;
3815
3816 if (!ccb->user_set_write_time) {
3817 KeQuerySystemTime(&time);
3818 win_time_to_unix(time, &fcb->inode_item.st_mtime);
3819 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
3820 }
3821
3822 fcb->inode_item_changed = true;
3823 mark_fcb_dirty(fcb);
3824
3825 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3826
3827 Status = STATUS_SUCCESS;
3828
3829 end:
3830 if (NT_SUCCESS(Status))
3831 clear_rollback(&rollback);
3832 else
3833 do_rollback(Vcb, &rollback);
3834
3835 ExReleaseResourceLite(fcb->Header.Resource);
3836
3837 if (set_size) {
3838 _SEH2_TRY {
3839 CcSetFileSizes(FileObject, &ccfs);
3840 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3841 Status = _SEH2_GetExceptionCode();
3842 } _SEH2_END;
3843
3844 if (!NT_SUCCESS(Status))
3845 ERR("CcSetFileSizes threw exception %08lx\n", Status);
3846 else
3847 fcb->Header.AllocationSize = ccfs.AllocationSize;
3848 }
3849
3850 ExReleaseResourceLite(&Vcb->tree_lock);
3851
3852 return Status;
3853 }
3854
3855 #ifndef __REACTOS__
set_case_sensitive_information(PIRP Irp)3856 static NTSTATUS set_case_sensitive_information(PIRP Irp) {
3857 FILE_CASE_SENSITIVE_INFORMATION* fcsi = (FILE_CASE_SENSITIVE_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
3858 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3859
3860 if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_CASE_SENSITIVE_INFORMATION))
3861 return STATUS_INFO_LENGTH_MISMATCH;
3862
3863 PFILE_OBJECT FileObject = IrpSp->FileObject;
3864
3865 if (!FileObject)
3866 return STATUS_INVALID_PARAMETER;
3867
3868 fcb* fcb = FileObject->FsContext;
3869
3870 if (!fcb)
3871 return STATUS_INVALID_PARAMETER;
3872
3873 if (!(fcb->atts & FILE_ATTRIBUTE_DIRECTORY)) {
3874 WARN("cannot set case-sensitive flag on anything other than directory\n");
3875 return STATUS_INVALID_PARAMETER;
3876 }
3877
3878 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
3879
3880 fcb->case_sensitive = fcsi->Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR;
3881 mark_fcb_dirty(fcb);
3882
3883 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
3884
3885 return STATUS_SUCCESS;
3886 }
3887 #endif
3888
3889 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
_Function_class_(DRIVER_DISPATCH)3890 _Function_class_(DRIVER_DISPATCH)
3891 NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3892 NTSTATUS Status;
3893 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3894 device_extension* Vcb = DeviceObject->DeviceExtension;
3895 fcb* fcb = IrpSp->FileObject->FsContext;
3896 ccb* ccb = IrpSp->FileObject->FsContext2;
3897 bool top_level;
3898
3899 FsRtlEnterFileSystem();
3900
3901 top_level = is_top_level(Irp);
3902
3903 Irp->IoStatus.Information = 0;
3904
3905 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
3906 Status = STATUS_INVALID_DEVICE_REQUEST;
3907 goto end;
3908 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
3909 Status = STATUS_INVALID_PARAMETER;
3910 goto end;
3911 }
3912
3913 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
3914 Status = STATUS_ACCESS_DENIED;
3915 goto end;
3916 }
3917
3918 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
3919 Status = STATUS_MEDIA_WRITE_PROTECTED;
3920 goto end;
3921 }
3922
3923 if (!fcb) {
3924 ERR("no fcb\n");
3925 Status = STATUS_INVALID_PARAMETER;
3926 goto end;
3927 }
3928
3929 if (!ccb) {
3930 ERR("no ccb\n");
3931 Status = STATUS_INVALID_PARAMETER;
3932 goto end;
3933 }
3934
3935 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
3936 #ifndef __REACTOS__
3937 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformationEx))) {
3938 #else
3939 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
3940 #endif
3941 Status = STATUS_ACCESS_DENIED;
3942 goto end;
3943 }
3944
3945 Status = STATUS_NOT_IMPLEMENTED;
3946
3947 TRACE("set information\n");
3948
3949 FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
3950
3951 switch (IrpSp->Parameters.SetFile.FileInformationClass) {
3952 case FileAllocationInformation:
3953 {
3954 TRACE("FileAllocationInformation\n");
3955
3956 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) {
3957 WARN("insufficient privileges\n");
3958 Status = STATUS_ACCESS_DENIED;
3959 break;
3960 }
3961
3962 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, false, true);
3963 break;
3964 }
3965
3966 case FileBasicInformation:
3967 {
3968 TRACE("FileBasicInformation\n");
3969
3970 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
3971 WARN("insufficient privileges\n");
3972 Status = STATUS_ACCESS_DENIED;
3973 break;
3974 }
3975
3976 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject);
3977
3978 break;
3979 }
3980
3981 case FileDispositionInformation:
3982 {
3983 TRACE("FileDispositionInformation\n");
3984
3985 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
3986 WARN("insufficient privileges\n");
3987 Status = STATUS_ACCESS_DENIED;
3988 break;
3989 }
3990
3991 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, false);
3992
3993 break;
3994 }
3995
3996 case FileEndOfFileInformation:
3997 {
3998 TRACE("FileEndOfFileInformation\n");
3999
4000 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
4001 WARN("insufficient privileges\n");
4002 Status = STATUS_ACCESS_DENIED;
4003 break;
4004 }
4005
4006 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, false);
4007
4008 break;
4009 }
4010
4011 case FileLinkInformation:
4012 TRACE("FileLinkInformation\n");
4013 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
4014 break;
4015
4016 case FilePositionInformation:
4017 TRACE("FilePositionInformation\n");
4018 Status = set_position_information(IrpSp->FileObject, Irp);
4019 break;
4020
4021 case FileRenameInformation:
4022 TRACE("FileRenameInformation\n");
4023 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false);
4024 break;
4025
4026 case FileValidDataLengthInformation:
4027 {
4028 TRACE("FileValidDataLengthInformation\n");
4029
4030 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
4031 WARN("insufficient privileges\n");
4032 Status = STATUS_ACCESS_DENIED;
4033 break;
4034 }
4035
4036 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject);
4037
4038 break;
4039 }
4040
4041 #ifndef __REACTOS__
4042 #ifndef _MSC_VER
4043 #pragma GCC diagnostic push
4044 #pragma GCC diagnostic ignored "-Wswitch"
4045 #endif
4046 case FileDispositionInformationEx:
4047 {
4048 TRACE("FileDispositionInformationEx\n");
4049
4050 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
4051 WARN("insufficient privileges\n");
4052 Status = STATUS_ACCESS_DENIED;
4053 break;
4054 }
4055
4056 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, true);
4057
4058 break;
4059 }
4060
4061 case FileRenameInformationEx:
4062 TRACE("FileRenameInformationEx\n");
4063 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
4064 break;
4065
4066 case FileLinkInformationEx:
4067 TRACE("FileLinkInformationEx\n");
4068 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true);
4069 break;
4070
4071 case FileCaseSensitiveInformation:
4072 TRACE("FileCaseSensitiveInformation\n");
4073
4074 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
4075 WARN("insufficient privileges\n");
4076 Status = STATUS_ACCESS_DENIED;
4077 break;
4078 }
4079
4080 Status = set_case_sensitive_information(Irp);
4081 break;
4082
4083 case FileStorageReserveIdInformation:
4084 WARN("unimplemented FileInformationClass FileStorageReserveIdInformation\n");
4085 break;
4086
4087 #ifndef _MSC_VER
4088 #pragma GCC diagnostic pop
4089 #endif
4090 #endif
4091
4092 default:
4093 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
4094 }
4095
4096 end:
4097 Irp->IoStatus.Status = Status;
4098
4099 TRACE("returning %08lx\n", Status);
4100
4101 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4102
4103 if (top_level)
4104 IoSetTopLevelIrp(NULL);
4105
4106 FsRtlExitFileSystem();
4107
4108 return Status;
4109 }
4110
4111 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) {
4112 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION));
4113
4114 *length -= sizeof(FILE_BASIC_INFORMATION);
4115
4116 if (fcb == fcb->Vcb->dummy_fcb) {
4117 LARGE_INTEGER time;
4118
4119 KeQuerySystemTime(&time);
4120 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time;
4121 } else {
4122 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4123 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4124 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4125 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4126 }
4127
4128 if (fcb->ads) {
4129 if (!fileref || !fileref->parent) {
4130 ERR("no fileref for stream\n");
4131 return STATUS_INTERNAL_ERROR;
4132 } else
4133 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4134 } else
4135 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4136
4137 return STATUS_SUCCESS;
4138 }
4139
4140 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) {
4141 INODE_ITEM* ii;
4142
4143 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
4144 WARN("overflow\n");
4145 return STATUS_BUFFER_OVERFLOW;
4146 }
4147
4148 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
4149
4150 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
4151
4152 if (fcb->ads) {
4153 if (!fileref || !fileref->parent) {
4154 ERR("no fileref for stream\n");
4155 return STATUS_INTERNAL_ERROR;
4156 }
4157
4158 ii = &fileref->parent->fcb->inode_item;
4159 } else
4160 ii = &fcb->inode_item;
4161
4162 if (fcb == fcb->Vcb->dummy_fcb) {
4163 LARGE_INTEGER time;
4164
4165 KeQuerySystemTime(&time);
4166 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time;
4167 } else {
4168 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4169 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4170 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4171 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4172 }
4173
4174 if (fcb->ads) {
4175 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
4176 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts;
4177 } else {
4178 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4179 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4180 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4181 }
4182
4183 return STATUS_SUCCESS;
4184 }
4185
4186 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) {
4187 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION));
4188
4189 *length -= sizeof(FILE_STANDARD_INFORMATION);
4190
4191 if (fcb->ads) {
4192 if (!fileref || !fileref->parent) {
4193 ERR("no fileref for stream\n");
4194 return STATUS_INTERNAL_ERROR;
4195 }
4196
4197 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4198 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
4199 fsi->Directory = false;
4200 } else {
4201 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4202 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4203 fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4204 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
4205 }
4206
4207 TRACE("length = %I64u\n", fsi->EndOfFile.QuadPart);
4208
4209 fsi->DeletePending = fileref ? fileref->delete_on_close : false;
4210
4211 return STATUS_SUCCESS;
4212 }
4213
4214 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) {
4215 *length -= sizeof(FILE_INTERNAL_INFORMATION);
4216
4217 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4218
4219 return STATUS_SUCCESS;
4220 }
4221
4222 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) {
4223 *length -= sizeof(FILE_EA_INFORMATION);
4224
4225 /* This value appears to be the size of the structure NTFS stores on disk, and not,
4226 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store).
4227 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each
4228 * item. */
4229
4230 eai->EaSize = fcb->ealen;
4231
4232 return STATUS_SUCCESS;
4233 }
4234
4235 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
4236 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION));
4237
4238 *length -= sizeof(FILE_POSITION_INFORMATION);
4239
4240 fpi->CurrentByteOffset = FileObject->CurrentByteOffset;
4241
4242 return STATUS_SUCCESS;
4243 }
4244
4245 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
4246 file_ref* fr;
4247 NTSTATUS Status;
4248 ULONG reqlen = 0;
4249 USHORT offset;
4250 bool overflow = false;
4251
4252 // FIXME - we need a lock on filerefs' filepart
4253
4254 if (fileref == fileref->fcb->Vcb->root_fileref) {
4255 if (fn->MaximumLength >= sizeof(WCHAR)) {
4256 fn->Buffer[0] = '\\';
4257 fn->Length = sizeof(WCHAR);
4258
4259 if (name_offset)
4260 *name_offset = 0;
4261
4262 return STATUS_SUCCESS;
4263 } else {
4264 if (preqlen)
4265 *preqlen = sizeof(WCHAR);
4266 fn->Length = 0;
4267
4268 return STATUS_BUFFER_OVERFLOW;
4269 }
4270 }
4271
4272 fr = fileref;
4273 offset = 0;
4274
4275 while (fr->parent) {
4276 USHORT movelen;
4277
4278 if (!fr->dc)
4279 return STATUS_INTERNAL_ERROR;
4280
4281 if (!overflow) {
4282 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength)
4283 overflow = true;
4284 }
4285
4286 if (overflow)
4287 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
4288 else
4289 movelen = fn->Length;
4290
4291 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
4292 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
4293 offset += fr->dc->name.Length + sizeof(WCHAR);
4294 }
4295
4296 if (fn->MaximumLength >= sizeof(WCHAR)) {
4297 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
4298 fn->Length += sizeof(WCHAR);
4299
4300 if (fn->MaximumLength > sizeof(WCHAR)) {
4301 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
4302 fn->Length += fr->dc->name.Length;
4303 }
4304
4305 if (fn->Length > fn->MaximumLength) {
4306 fn->Length = fn->MaximumLength;
4307 overflow = true;
4308 }
4309 }
4310
4311 reqlen += sizeof(WCHAR) + fr->dc->name.Length;
4312
4313 fr = fr->parent;
4314 }
4315
4316 offset += sizeof(WCHAR);
4317
4318 if (overflow) {
4319 if (preqlen)
4320 *preqlen = reqlen;
4321 Status = STATUS_BUFFER_OVERFLOW;
4322 } else {
4323 if (name_offset)
4324 *name_offset = offset;
4325
4326 Status = STATUS_SUCCESS;
4327 }
4328
4329 return Status;
4330 }
4331
4332 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
4333 ULONG reqlen;
4334 UNICODE_STRING fn;
4335 NTSTATUS Status;
4336 static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
4337 uint16_t datasuflen = sizeof(datasuf) - sizeof(WCHAR);
4338
4339 if (!fileref) {
4340 ERR("called without fileref\n");
4341 return STATUS_INVALID_PARAMETER;
4342 }
4343
4344 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
4345
4346 TRACE("maximum length is %li\n", *length);
4347 fni->FileNameLength = 0;
4348
4349 fni->FileName[0] = 0;
4350
4351 fn.Buffer = fni->FileName;
4352 fn.Length = 0;
4353 fn.MaximumLength = (uint16_t)*length;
4354
4355 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
4356 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
4357 ERR("fileref_get_filename returned %08lx\n", Status);
4358 return Status;
4359 }
4360
4361 if (fcb->ads) {
4362 if (Status == STATUS_BUFFER_OVERFLOW)
4363 reqlen += datasuflen;
4364 else {
4365 if (fn.Length + datasuflen > fn.MaximumLength) {
4366 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
4367 reqlen += datasuflen;
4368 Status = STATUS_BUFFER_OVERFLOW;
4369 } else {
4370 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
4371 fn.Length += datasuflen;
4372 }
4373 }
4374 }
4375
4376 if (Status == STATUS_BUFFER_OVERFLOW) {
4377 *length = -1;
4378 fni->FileNameLength = reqlen;
4379 TRACE("%.*S (truncated)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4380 } else {
4381 *length -= fn.Length;
4382 fni->FileNameLength = fn.Length;
4383 TRACE("%.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4384 }
4385
4386 return Status;
4387 }
4388
4389 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) {
4390 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
4391
4392 if (fcb->ads) {
4393 if (!ccb->fileref || !ccb->fileref->parent) {
4394 ERR("no fileref for stream\n");
4395 return STATUS_INTERNAL_ERROR;
4396 }
4397
4398 ati->FileAttributes = ccb->fileref->parent->fcb->atts;
4399 } else
4400 ati->FileAttributes = fcb->atts;
4401
4402 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4403 ati->ReparseTag = 0;
4404 else
4405 ati->ReparseTag = get_reparse_tag_fcb(fcb);
4406
4407 return STATUS_SUCCESS;
4408 }
4409
4410 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
4411 LONG reqsize;
4412 LIST_ENTRY* le;
4413 FILE_STREAM_INFORMATION *entry, *lastentry;
4414 NTSTATUS Status;
4415
4416 static const WCHAR datasuf[] = L":$DATA";
4417 UNICODE_STRING suf;
4418
4419 if (!fileref) {
4420 ERR("fileref was NULL\n");
4421 return STATUS_INVALID_PARAMETER;
4422 }
4423
4424 suf.Buffer = (WCHAR*)datasuf;
4425 suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
4426
4427 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
4428 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
4429 else
4430 reqsize = 0;
4431
4432 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, true);
4433
4434 le = fileref->fcb->dir_children_index.Flink;
4435 while (le != &fileref->fcb->dir_children_index) {
4436 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4437
4438 if (dc->index == 0) {
4439 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG));
4440 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
4441 } else
4442 break;
4443
4444 le = le->Flink;
4445 }
4446
4447 TRACE("length = %li, reqsize = %lu\n", *length, reqsize);
4448
4449 if (reqsize > *length) {
4450 Status = STATUS_BUFFER_OVERFLOW;
4451 goto end;
4452 }
4453
4454 entry = fsi;
4455 lastentry = NULL;
4456
4457 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
4458 ULONG off;
4459
4460 entry->NextEntryOffset = 0;
4461 entry->StreamNameLength = suf.Length + sizeof(WCHAR);
4462 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
4463 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb);
4464
4465 entry->StreamName[0] = ':';
4466 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
4467
4468 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
4469
4470 lastentry = entry;
4471 entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4472 }
4473
4474 le = fileref->fcb->dir_children_index.Flink;
4475 while (le != &fileref->fcb->dir_children_index) {
4476 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
4477
4478 if (dc->index == 0) {
4479 ULONG off;
4480
4481 entry->NextEntryOffset = 0;
4482 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
4483
4484 if (dc->fileref)
4485 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
4486 else
4487 entry->StreamSize.QuadPart = dc->size;
4488
4489 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
4490
4491 entry->StreamName[0] = ':';
4492
4493 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
4494 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
4495
4496 if (lastentry)
4497 lastentry->NextEntryOffset = (uint32_t)((uint8_t*)entry - (uint8_t*)lastentry);
4498
4499 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
4500
4501 lastentry = entry;
4502 entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off);
4503 } else
4504 break;
4505
4506 le = le->Flink;
4507 }
4508
4509 *length -= reqsize;
4510
4511 Status = STATUS_SUCCESS;
4512
4513 end:
4514 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
4515
4516 return Status;
4517 }
4518
4519 #ifndef __REACTOS__
4520 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) {
4521 TRACE("FileStandardLinkInformation\n");
4522
4523 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
4524
4525 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
4526 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
4527 fsli->DeletePending = fileref ? fileref->delete_on_close : false;
4528 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? true : false;
4529
4530 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
4531
4532 return STATUS_SUCCESS;
4533 }
4534
4535 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
4536 NTSTATUS Status;
4537 LIST_ENTRY* le;
4538 LONG bytes_needed;
4539 FILE_LINK_ENTRY_INFORMATION* feli;
4540 bool overflow = false;
4541 fcb* fcb = fileref->fcb;
4542 ULONG len;
4543
4544 if (fcb->ads)
4545 return STATUS_INVALID_PARAMETER;
4546
4547 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry))
4548 return STATUS_INVALID_PARAMETER;
4549
4550 RtlZeroMemory(fli, *length);
4551
4552 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry);
4553 len = bytes_needed;
4554 feli = NULL;
4555
4556 ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4557
4558 if (fcb->inode == SUBVOL_ROOT_INODE) {
4559 ULONG namelen;
4560
4561 if (fcb == fcb->Vcb->root_fileref->fcb)
4562 namelen = sizeof(WCHAR);
4563 else
4564 namelen = fileref->dc->name.Length;
4565
4566 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
4567
4568 if (bytes_needed > *length)
4569 overflow = true;
4570
4571 if (!overflow) {
4572 feli = &fli->Entry;
4573
4574 feli->NextEntryOffset = 0;
4575 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume
4576
4577 if (fcb == fcb->Vcb->root_fileref->fcb) {
4578 feli->FileNameLength = 1;
4579 feli->FileName[0] = '.';
4580 } else {
4581 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4582 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4583 }
4584
4585 fli->EntriesReturned++;
4586
4587 len = bytes_needed;
4588 }
4589 } else {
4590 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4591
4592 if (IsListEmpty(&fcb->hardlinks)) {
4593 if (!fileref->dc) {
4594 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4595 Status = STATUS_INVALID_PARAMETER;
4596 goto end;
4597 }
4598
4599 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
4600
4601 if (bytes_needed > *length)
4602 overflow = true;
4603
4604 if (!overflow) {
4605 feli = &fli->Entry;
4606
4607 feli->NextEntryOffset = 0;
4608 feli->ParentFileId = fileref->parent->fcb->inode;
4609 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4610 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4611
4612 fli->EntriesReturned++;
4613
4614 len = bytes_needed;
4615 }
4616 } else {
4617 le = fcb->hardlinks.Flink;
4618 while (le != &fcb->hardlinks) {
4619 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4620 file_ref* parfr;
4621
4622 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer);
4623
4624 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4625
4626 if (!NT_SUCCESS(Status)) {
4627 ERR("open_fileref_by_inode returned %08lx\n", Status);
4628 } else if (!parfr->deleted) {
4629 LIST_ENTRY* le2;
4630 bool found = false, deleted = false;
4631 UNICODE_STRING* fn = NULL;
4632
4633 le2 = parfr->children.Flink;
4634 while (le2 != &parfr->children) {
4635 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4636
4637 if (fr2->dc && fr2->dc->index == hl->index) {
4638 found = true;
4639 deleted = fr2->deleted;
4640
4641 if (!deleted)
4642 fn = &fr2->dc->name;
4643
4644 break;
4645 }
4646
4647 le2 = le2->Flink;
4648 }
4649
4650 if (!found)
4651 fn = &hl->name;
4652
4653 if (!deleted) {
4654 TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found);
4655
4656 if (feli)
4657 bytes_needed = (LONG)sector_align(bytes_needed, 8);
4658
4659 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR);
4660
4661 if (bytes_needed > *length)
4662 overflow = true;
4663
4664 if (!overflow) {
4665 if (feli) {
4666 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8);
4667 feli = (FILE_LINK_ENTRY_INFORMATION*)((uint8_t*)feli + feli->NextEntryOffset);
4668 } else
4669 feli = &fli->Entry;
4670
4671 feli->NextEntryOffset = 0;
4672 feli->ParentFileId = parfr->fcb->inode;
4673 feli->FileNameLength = fn->Length / sizeof(WCHAR);
4674 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length);
4675
4676 fli->EntriesReturned++;
4677
4678 len = bytes_needed;
4679 }
4680 }
4681
4682 free_fileref(parfr);
4683 }
4684
4685 le = le->Flink;
4686 }
4687 }
4688
4689 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4690 }
4691
4692 fli->BytesNeeded = bytes_needed;
4693
4694 *length -= len;
4695
4696 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4697
4698 end:
4699 ExReleaseResourceLite(fcb->Header.Resource);
4700
4701 return Status;
4702 }
4703
4704 static NTSTATUS fill_in_hard_link_full_id_information(FILE_LINKS_FULL_ID_INFORMATION* flfii, file_ref* fileref, PIRP Irp, LONG* length) {
4705 NTSTATUS Status;
4706 LIST_ENTRY* le;
4707 LONG bytes_needed;
4708 FILE_LINK_ENTRY_FULL_ID_INFORMATION* flefii;
4709 bool overflow = false;
4710 fcb* fcb = fileref->fcb;
4711 ULONG len;
4712
4713 if (fcb->ads)
4714 return STATUS_INVALID_PARAMETER;
4715
4716 if (*length < (LONG)offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry))
4717 return STATUS_INVALID_PARAMETER;
4718
4719 RtlZeroMemory(flfii, *length);
4720
4721 bytes_needed = offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry);
4722 len = bytes_needed;
4723 flefii = NULL;
4724
4725 ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4726
4727 if (fcb->inode == SUBVOL_ROOT_INODE) {
4728 ULONG namelen;
4729
4730 if (fcb == fcb->Vcb->root_fileref->fcb)
4731 namelen = sizeof(WCHAR);
4732 else
4733 namelen = fileref->dc->name.Length;
4734
4735 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + namelen;
4736
4737 if (bytes_needed > *length)
4738 overflow = true;
4739
4740 if (!overflow) {
4741 flefii = &flfii->Entry;
4742
4743 flefii->NextEntryOffset = 0;
4744
4745 if (fcb == fcb->Vcb->root_fileref->fcb) {
4746 RtlZeroMemory(&flefii->ParentFileId.Identifier[0], sizeof(FILE_ID_128));
4747 flefii->FileNameLength = 1;
4748 flefii->FileName[0] = '.';
4749 } else {
4750 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4751 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4752
4753 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4754 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4755 }
4756
4757 flfii->EntriesReturned++;
4758
4759 len = bytes_needed;
4760 }
4761 } else {
4762 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
4763
4764 if (IsListEmpty(&fcb->hardlinks)) {
4765 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fileref->dc->name.Length;
4766
4767 if (bytes_needed > *length)
4768 overflow = true;
4769
4770 if (!overflow) {
4771 flefii = &flfii->Entry;
4772
4773 flefii->NextEntryOffset = 0;
4774
4775 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t));
4776 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t));
4777
4778 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
4779 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
4780
4781 flfii->EntriesReturned++;
4782
4783 len = bytes_needed;
4784 }
4785 } else {
4786 le = fcb->hardlinks.Flink;
4787 while (le != &fcb->hardlinks) {
4788 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
4789 file_ref* parfr;
4790
4791 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer);
4792
4793 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
4794
4795 if (!NT_SUCCESS(Status)) {
4796 ERR("open_fileref_by_inode returned %08lx\n", Status);
4797 } else if (!parfr->deleted) {
4798 LIST_ENTRY* le2;
4799 bool found = false, deleted = false;
4800 UNICODE_STRING* fn = NULL;
4801
4802 le2 = parfr->children.Flink;
4803 while (le2 != &parfr->children) {
4804 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
4805
4806 if (fr2->dc->index == hl->index) {
4807 found = true;
4808 deleted = fr2->deleted;
4809
4810 if (!deleted)
4811 fn = &fr2->dc->name;
4812
4813 break;
4814 }
4815
4816 le2 = le2->Flink;
4817 }
4818
4819 if (!found)
4820 fn = &hl->name;
4821
4822 if (!deleted) {
4823 TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found);
4824
4825 if (flefii)
4826 bytes_needed = (LONG)sector_align(bytes_needed, 8);
4827
4828 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fn->Length;
4829
4830 if (bytes_needed > *length)
4831 overflow = true;
4832
4833 if (!overflow) {
4834 if (flefii) {
4835 flefii->NextEntryOffset = (ULONG)sector_align(offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + (flefii->FileNameLength * sizeof(WCHAR)), 8);
4836 flefii = (FILE_LINK_ENTRY_FULL_ID_INFORMATION*)((uint8_t*)flefii + flefii->NextEntryOffset);
4837 } else
4838 flefii = &flfii->Entry;
4839
4840 flefii->NextEntryOffset = 0;
4841
4842 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &parfr->fcb->inode, sizeof(uint64_t));
4843 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &parfr->fcb->subvol->id, sizeof(uint64_t));
4844
4845 flefii->FileNameLength = fn->Length / sizeof(WCHAR);
4846 RtlCopyMemory(flefii->FileName, fn->Buffer, fn->Length);
4847
4848 flfii->EntriesReturned++;
4849
4850 len = bytes_needed;
4851 }
4852 }
4853
4854 free_fileref(parfr);
4855 }
4856
4857 le = le->Flink;
4858 }
4859 }
4860
4861 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
4862 }
4863
4864 flfii->BytesNeeded = bytes_needed;
4865
4866 *length -= len;
4867
4868 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
4869
4870 ExReleaseResourceLite(fcb->Header.Resource);
4871
4872 return Status;
4873 }
4874
4875 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
4876 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(uint64_t));
4877 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(uint64_t));
4878 RtlCopyMemory(&fii->FileId.Identifier[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t));
4879
4880 *length -= sizeof(FILE_ID_INFORMATION);
4881
4882 return STATUS_SUCCESS;
4883 }
4884
4885 static NTSTATUS fill_in_file_stat_information(FILE_STAT_INFORMATION* fsi, fcb* fcb, ccb* ccb, LONG* length) {
4886 INODE_ITEM* ii;
4887
4888 fsi->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4889
4890 if (fcb->ads)
4891 ii = &ccb->fileref->parent->fcb->inode_item;
4892 else
4893 ii = &fcb->inode_item;
4894
4895 if (fcb == fcb->Vcb->dummy_fcb) {
4896 LARGE_INTEGER time;
4897
4898 KeQuerySystemTime(&time);
4899 fsi->CreationTime = fsi->LastAccessTime = fsi->LastWriteTime = fsi->ChangeTime = time;
4900 } else {
4901 fsi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4902 fsi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4903 fsi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4904 fsi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4905 }
4906
4907 if (fcb->ads) {
4908 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
4909 fsi->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4910 } else {
4911 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4912 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4913 fsi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4914 }
4915
4916 if (fcb->type == BTRFS_TYPE_SOCKET)
4917 fsi->ReparseTag = IO_REPARSE_TAG_AF_UNIX;
4918 else if (fcb->type == BTRFS_TYPE_FIFO)
4919 fsi->ReparseTag = IO_REPARSE_TAG_LX_FIFO;
4920 else if (fcb->type == BTRFS_TYPE_CHARDEV)
4921 fsi->ReparseTag = IO_REPARSE_TAG_LX_CHR;
4922 else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4923 fsi->ReparseTag = IO_REPARSE_TAG_LX_BLK;
4924 else if (!(fsi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4925 fsi->ReparseTag = 0;
4926 else
4927 fsi->ReparseTag = get_reparse_tag_fcb(fcb);
4928
4929 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4930 fsi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4931
4932 if (fcb->ads)
4933 fsi->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4934 else
4935 fsi->NumberOfLinks = fcb->inode_item.st_nlink;
4936
4937 fsi->EffectiveAccess = ccb->access;
4938
4939 *length -= sizeof(FILE_STAT_INFORMATION);
4940
4941 return STATUS_SUCCESS;
4942 }
4943
4944 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
4945 INODE_ITEM* ii;
4946
4947 fsli->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode);
4948
4949 if (fcb->ads)
4950 ii = &ccb->fileref->parent->fcb->inode_item;
4951 else
4952 ii = &fcb->inode_item;
4953
4954 if (fcb == fcb->Vcb->dummy_fcb) {
4955 LARGE_INTEGER time;
4956
4957 KeQuerySystemTime(&time);
4958 fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time;
4959 } else {
4960 fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
4961 fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
4962 fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
4963 fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
4964 }
4965
4966 if (fcb->ads) {
4967 fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length;
4968 fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
4969 } else {
4970 fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
4971 fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
4972 fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
4973 }
4974
4975 if (fcb->type == BTRFS_TYPE_SOCKET)
4976 fsli->ReparseTag = IO_REPARSE_TAG_AF_UNIX;
4977 else if (fcb->type == BTRFS_TYPE_FIFO)
4978 fsli->ReparseTag = IO_REPARSE_TAG_LX_FIFO;
4979 else if (fcb->type == BTRFS_TYPE_CHARDEV)
4980 fsli->ReparseTag = IO_REPARSE_TAG_LX_CHR;
4981 else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
4982 fsli->ReparseTag = IO_REPARSE_TAG_LX_BLK;
4983 else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
4984 fsli->ReparseTag = 0;
4985 else
4986 fsli->ReparseTag = get_reparse_tag_fcb(fcb);
4987
4988 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
4989 fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
4990
4991 if (fcb->ads)
4992 fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
4993 else
4994 fsli->NumberOfLinks = fcb->inode_item.st_nlink;
4995
4996 fsli->EffectiveAccess = ccb->access;
4997 fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID;
4998
4999 if (fcb->case_sensitive)
5000 fsli->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR;
5001
5002 fsli->LxUid = ii->st_uid;
5003 fsli->LxGid = ii->st_gid;
5004 fsli->LxMode = ii->st_mode;
5005
5006 if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
5007 fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
5008 fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
5009 } else {
5010 fsli->LxDeviceIdMajor = 0;
5011 fsli->LxDeviceIdMinor = 0;
5012 }
5013
5014 *length -= sizeof(FILE_STAT_LX_INFORMATION);
5015
5016 return STATUS_SUCCESS;
5017 }
5018
5019 static NTSTATUS fill_in_file_case_sensitive_information(FILE_CASE_SENSITIVE_INFORMATION* fcsi, fcb* fcb, LONG* length) {
5020 fcsi->Flags = fcb->case_sensitive ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
5021
5022 *length -= sizeof(FILE_CASE_SENSITIVE_INFORMATION);
5023
5024 return STATUS_SUCCESS;
5025 }
5026
5027 #endif // __REACTOS__
5028
5029 static NTSTATUS fill_in_file_compression_information(FILE_COMPRESSION_INFORMATION* fci, LONG* length, fcb* fcb) {
5030 *length -= sizeof(FILE_COMPRESSION_INFORMATION);
5031
5032 memset(fci, 0, sizeof(FILE_COMPRESSION_INFORMATION));
5033
5034 if (fcb->ads)
5035 fci->CompressedFileSize.QuadPart = fcb->adsdata.Length;
5036 else if (!S_ISDIR(fcb->inode_item.st_mode))
5037 fci->CompressedFileSize.QuadPart = fcb->inode_item.st_size;
5038
5039 return STATUS_SUCCESS;
5040 }
5041
5042 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
5043 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5044 LONG length = IrpSp->Parameters.QueryFile.Length;
5045 fcb* fcb = FileObject->FsContext;
5046 ccb* ccb = FileObject->FsContext2;
5047 file_ref* fileref = ccb ? ccb->fileref : NULL;
5048 NTSTATUS Status;
5049
5050 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp);
5051 TRACE("fcb = %p\n", fcb);
5052
5053 if (fcb == Vcb->volume_fcb)
5054 return STATUS_INVALID_PARAMETER;
5055
5056 if (!ccb) {
5057 ERR("ccb is NULL\n");
5058 return STATUS_INVALID_PARAMETER;
5059 }
5060
5061 switch (IrpSp->Parameters.QueryFile.FileInformationClass) {
5062 case FileAllInformation:
5063 {
5064 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer;
5065 INODE_ITEM* ii;
5066
5067 TRACE("FileAllInformation\n");
5068
5069 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5070 WARN("insufficient privileges\n");
5071 Status = STATUS_ACCESS_DENIED;
5072 goto exit;
5073 }
5074
5075 if (fcb->ads) {
5076 if (!fileref || !fileref->parent) {
5077 ERR("no fileref for stream\n");
5078 Status = STATUS_INTERNAL_ERROR;
5079 goto exit;
5080 }
5081
5082 ii = &fileref->parent->fcb->inode_item;
5083 } else
5084 ii = &fcb->inode_item;
5085
5086 // Access, mode, and alignment are all filled in by the kernel
5087
5088 if (length > 0)
5089 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref);
5090
5091 if (length > 0)
5092 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length);
5093
5094 if (length > 0)
5095 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length);
5096
5097 if (length > 0)
5098 fill_in_file_ea_information(&fai->EaInformation, fcb, &length);
5099
5100 length -= sizeof(FILE_ACCESS_INFORMATION);
5101
5102 if (length > 0)
5103 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length);
5104
5105 length -= sizeof(FILE_MODE_INFORMATION);
5106
5107 length -= sizeof(FILE_ALIGNMENT_INFORMATION);
5108
5109 if (length > 0)
5110 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length);
5111
5112 Status = STATUS_SUCCESS;
5113
5114 break;
5115 }
5116
5117 case FileAttributeTagInformation:
5118 {
5119 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer;
5120
5121 TRACE("FileAttributeTagInformation\n");
5122
5123 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5124 WARN("insufficient privileges\n");
5125 Status = STATUS_ACCESS_DENIED;
5126 goto exit;
5127 }
5128
5129 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5130 Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
5131 ExReleaseResourceLite(&Vcb->tree_lock);
5132
5133 break;
5134 }
5135
5136 case FileBasicInformation:
5137 {
5138 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
5139 INODE_ITEM* ii;
5140
5141 TRACE("FileBasicInformation\n");
5142
5143 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5144 WARN("insufficient privileges\n");
5145 Status = STATUS_ACCESS_DENIED;
5146 goto exit;
5147 }
5148
5149 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) {
5150 WARN("overflow\n");
5151 Status = STATUS_BUFFER_OVERFLOW;
5152 goto exit;
5153 }
5154
5155 if (fcb->ads) {
5156 if (!fileref || !fileref->parent) {
5157 ERR("no fileref for stream\n");
5158 Status = STATUS_INTERNAL_ERROR;
5159 goto exit;
5160 }
5161
5162 ii = &fileref->parent->fcb->inode_item;
5163 } else
5164 ii = &fcb->inode_item;
5165
5166 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref);
5167 break;
5168 }
5169
5170 case FileCompressionInformation:
5171 {
5172 FILE_COMPRESSION_INFORMATION* fci = Irp->AssociatedIrp.SystemBuffer;
5173
5174 TRACE("FileCompressionInformation\n");
5175
5176 Status = fill_in_file_compression_information(fci, &length, fcb);
5177 break;
5178 }
5179
5180 case FileEaInformation:
5181 {
5182 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer;
5183
5184 TRACE("FileEaInformation\n");
5185
5186 Status = fill_in_file_ea_information(eai, fcb, &length);
5187
5188 break;
5189 }
5190
5191 case FileInternalInformation:
5192 {
5193 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5194
5195 TRACE("FileInternalInformation\n");
5196
5197 Status = fill_in_file_internal_information(fii, fcb, &length);
5198
5199 break;
5200 }
5201
5202 case FileNameInformation:
5203 {
5204 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5205
5206 TRACE("FileNameInformation\n");
5207
5208 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5209
5210 break;
5211 }
5212
5213 case FileNetworkOpenInformation:
5214 {
5215 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer;
5216
5217 TRACE("FileNetworkOpenInformation\n");
5218
5219 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) {
5220 WARN("insufficient privileges\n");
5221 Status = STATUS_ACCESS_DENIED;
5222 goto exit;
5223 }
5224
5225 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length);
5226
5227 break;
5228 }
5229
5230 case FilePositionInformation:
5231 {
5232 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer;
5233
5234 TRACE("FilePositionInformation\n");
5235
5236 Status = fill_in_file_position_information(fpi, FileObject, &length);
5237
5238 break;
5239 }
5240
5241 case FileStandardInformation:
5242 {
5243 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5244
5245 TRACE("FileStandardInformation\n");
5246
5247 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) {
5248 WARN("overflow\n");
5249 Status = STATUS_BUFFER_OVERFLOW;
5250 goto exit;
5251 }
5252
5253 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length);
5254
5255 break;
5256 }
5257
5258 case FileStreamInformation:
5259 {
5260 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5261
5262 TRACE("FileStreamInformation\n");
5263
5264 Status = fill_in_file_stream_information(fsi, fileref, &length);
5265
5266 break;
5267 }
5268
5269 #if (NTDDI_VERSION >= NTDDI_VISTA)
5270 case FileHardLinkInformation:
5271 {
5272 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
5273
5274 TRACE("FileHardLinkInformation\n");
5275
5276 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5277 Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
5278 ExReleaseResourceLite(&Vcb->tree_lock);
5279
5280 break;
5281 }
5282
5283 case FileNormalizedNameInformation:
5284 {
5285 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer;
5286
5287 TRACE("FileNormalizedNameInformation\n");
5288
5289 Status = fill_in_file_name_information(fni, fcb, fileref, &length);
5290
5291 break;
5292 }
5293
5294 case FileStandardLinkInformation:
5295 {
5296 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5297
5298 TRACE("FileStandardLinkInformation\n");
5299
5300 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length);
5301
5302 break;
5303 }
5304
5305 case FileRemoteProtocolInformation:
5306 TRACE("FileRemoteProtocolInformation\n");
5307 Status = STATUS_INVALID_PARAMETER;
5308 goto exit;
5309
5310 #ifndef _MSC_VER
5311 #pragma GCC diagnostic push
5312 #pragma GCC diagnostic ignored "-Wswitch"
5313 #endif
5314 case FileIdInformation:
5315 {
5316 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer;
5317
5318 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) {
5319 WARN("overflow\n");
5320 Status = STATUS_BUFFER_OVERFLOW;
5321 goto exit;
5322 }
5323
5324 TRACE("FileIdInformation\n");
5325
5326 Status = fill_in_file_id_information(fii, fcb, &length);
5327
5328 break;
5329 }
5330
5331 case FileStatInformation:
5332 {
5333 FILE_STAT_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
5334
5335 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_INFORMATION)) {
5336 WARN("overflow\n");
5337 Status = STATUS_BUFFER_OVERFLOW;
5338 goto exit;
5339 }
5340
5341 TRACE("FileStatInformation\n");
5342
5343 Status = fill_in_file_stat_information(fsi, fcb, ccb, &length);
5344
5345 break;
5346 }
5347
5348 case FileStatLxInformation:
5349 {
5350 FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
5351
5352 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
5353 WARN("overflow\n");
5354 Status = STATUS_BUFFER_OVERFLOW;
5355 goto exit;
5356 }
5357
5358 TRACE("FileStatLxInformation\n");
5359
5360 Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
5361
5362 break;
5363 }
5364
5365 case FileCaseSensitiveInformation:
5366 {
5367 FILE_CASE_SENSITIVE_INFORMATION* fcsi = Irp->AssociatedIrp.SystemBuffer;
5368
5369 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) {
5370 WARN("overflow\n");
5371 Status = STATUS_BUFFER_OVERFLOW;
5372 goto exit;
5373 }
5374
5375 TRACE("FileCaseSensitiveInformation\n");
5376
5377 Status = fill_in_file_case_sensitive_information(fcsi, fcb, &length);
5378
5379 break;
5380 }
5381
5382 case FileHardLinkFullIdInformation:
5383 {
5384 FILE_LINKS_FULL_ID_INFORMATION* flfii = Irp->AssociatedIrp.SystemBuffer;
5385
5386 TRACE("FileHardLinkFullIdInformation\n");
5387
5388 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
5389 Status = fill_in_hard_link_full_id_information(flfii, fileref, Irp, &length);
5390 ExReleaseResourceLite(&Vcb->tree_lock);
5391
5392 break;
5393 }
5394 #ifndef _MSC_VER
5395 #pragma GCC diagnostic pop
5396 #endif
5397 #endif
5398
5399 default:
5400 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass);
5401 Status = STATUS_INVALID_PARAMETER;
5402 goto exit;
5403 }
5404
5405 if (length < 0) {
5406 length = 0;
5407 Status = STATUS_BUFFER_OVERFLOW;
5408 }
5409
5410 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length;
5411
5412 exit:
5413 TRACE("query_info returning %08lx\n", Status);
5414
5415 return Status;
5416 }
5417
5418 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION)
5419 _Function_class_(DRIVER_DISPATCH)
5420 NTSTATUS __stdcall drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5421 PIO_STACK_LOCATION IrpSp;
5422 NTSTATUS Status;
5423 fcb* fcb;
5424 device_extension* Vcb = DeviceObject->DeviceExtension;
5425 bool top_level;
5426
5427 FsRtlEnterFileSystem();
5428
5429 top_level = is_top_level(Irp);
5430
5431 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5432 Status = STATUS_INVALID_DEVICE_REQUEST;
5433 goto end;
5434 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5435 Status = STATUS_INVALID_PARAMETER;
5436 goto end;
5437 }
5438
5439 Irp->IoStatus.Information = 0;
5440
5441 TRACE("query information\n");
5442
5443 IrpSp = IoGetCurrentIrpStackLocation(Irp);
5444
5445 fcb = IrpSp->FileObject->FsContext;
5446 TRACE("fcb = %p\n", fcb);
5447 TRACE("fcb->subvol = %p\n", fcb->subvol);
5448
5449 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
5450
5451 end:
5452 TRACE("returning %08lx\n", Status);
5453
5454 Irp->IoStatus.Status = Status;
5455
5456 IoCompleteRequest( Irp, IO_NO_INCREMENT );
5457
5458 if (top_level)
5459 IoSetTopLevelIrp(NULL);
5460
5461 FsRtlExitFileSystem();
5462
5463 return Status;
5464 }
5465
5466 _Dispatch_type_(IRP_MJ_QUERY_EA)
5467 _Function_class_(DRIVER_DISPATCH)
5468 NTSTATUS __stdcall drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5469 NTSTATUS Status;
5470 bool top_level;
5471 device_extension* Vcb = DeviceObject->DeviceExtension;
5472 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5473 PFILE_OBJECT FileObject = IrpSp->FileObject;
5474 fcb* fcb;
5475 ccb* ccb;
5476 FILE_FULL_EA_INFORMATION* ffei;
5477 ULONG retlen = 0;
5478
5479 FsRtlEnterFileSystem();
5480
5481 TRACE("(%p, %p)\n", DeviceObject, Irp);
5482
5483 top_level = is_top_level(Irp);
5484
5485 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5486 Status = STATUS_INVALID_DEVICE_REQUEST;
5487 goto end;
5488 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5489 Status = STATUS_INVALID_PARAMETER;
5490 goto end;
5491 }
5492
5493 ffei = map_user_buffer(Irp, NormalPagePriority);
5494 if (!ffei) {
5495 ERR("could not get output buffer\n");
5496 Status = STATUS_INVALID_PARAMETER;
5497 goto end;
5498 }
5499
5500 if (!FileObject) {
5501 ERR("no file object\n");
5502 Status = STATUS_INVALID_PARAMETER;
5503 goto end;
5504 }
5505
5506 fcb = FileObject->FsContext;
5507
5508 if (!fcb) {
5509 ERR("no fcb\n");
5510 Status = STATUS_INVALID_PARAMETER;
5511 goto end;
5512 }
5513
5514 if (fcb == fcb->Vcb->volume_fcb) {
5515 Status = STATUS_INVALID_PARAMETER;
5516 goto end;
5517 }
5518
5519 ccb = FileObject->FsContext2;
5520
5521 if (!ccb) {
5522 ERR("no ccb\n");
5523 Status = STATUS_INVALID_PARAMETER;
5524 goto end;
5525 }
5526
5527 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) {
5528 WARN("insufficient privileges\n");
5529 Status = STATUS_ACCESS_DENIED;
5530 goto end;
5531 }
5532
5533 if (fcb->ads)
5534 fcb = ccb->fileref->parent->fcb;
5535
5536 ExAcquireResourceSharedLite(fcb->Header.Resource, true);
5537
5538 if (fcb->ea_xattr.Length == 0) {
5539 Status = STATUS_NO_EAS_ON_FILE;
5540 goto end2;
5541 }
5542
5543 Status = STATUS_SUCCESS;
5544
5545 if (IrpSp->Parameters.QueryEa.EaList) {
5546 FILE_FULL_EA_INFORMATION *ea, *out;
5547 FILE_GET_EA_INFORMATION* in;
5548
5549 in = IrpSp->Parameters.QueryEa.EaList;
5550 do {
5551 STRING s;
5552
5553 s.Length = s.MaximumLength = in->EaNameLength;
5554 s.Buffer = in->EaName;
5555
5556 RtlUpperString(&s, &s);
5557
5558 if (in->NextEntryOffset == 0)
5559 break;
5560
5561 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5562 } while (true);
5563
5564 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5565 out = NULL;
5566
5567 do {
5568 bool found = false;
5569
5570 in = IrpSp->Parameters.QueryEa.EaList;
5571 do {
5572 if (in->EaNameLength == ea->EaNameLength &&
5573 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) {
5574 found = true;
5575 break;
5576 }
5577
5578 if (in->NextEntryOffset == 0)
5579 break;
5580
5581 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset);
5582 } while (true);
5583
5584 if (found) {
5585 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5586
5587 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5588 Status = STATUS_BUFFER_OVERFLOW;
5589 retlen = 0;
5590 goto end2;
5591 }
5592
5593 retlen += padding;
5594
5595 if (out) {
5596 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5597 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5598 } else
5599 out = ffei;
5600
5601 out->NextEntryOffset = 0;
5602 out->Flags = ea->Flags;
5603 out->EaNameLength = ea->EaNameLength;
5604 out->EaValueLength = ea->EaValueLength;
5605 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5606
5607 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5608
5609 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5610 break;
5611 }
5612
5613 if (ea->NextEntryOffset == 0)
5614 break;
5615
5616 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5617 } while (true);
5618 } else {
5619 FILE_FULL_EA_INFORMATION *ea, *out;
5620 ULONG index;
5621
5622 if (IrpSp->Flags & SL_INDEX_SPECIFIED) {
5623 // The index is 1-based
5624 if (IrpSp->Parameters.QueryEa.EaIndex == 0) {
5625 Status = STATUS_NONEXISTENT_EA_ENTRY;
5626 goto end2;
5627 } else
5628 index = IrpSp->Parameters.QueryEa.EaIndex - 1;
5629 } else if (IrpSp->Flags & SL_RESTART_SCAN)
5630 index = ccb->ea_index = 0;
5631 else
5632 index = ccb->ea_index;
5633
5634 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5635
5636 if (index > 0) {
5637 ULONG i;
5638
5639 for (i = 0; i < index; i++) {
5640 if (ea->NextEntryOffset == 0) { // last item
5641 Status = STATUS_NO_MORE_EAS;
5642 goto end2;
5643 }
5644
5645 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5646 }
5647 }
5648
5649 out = NULL;
5650
5651 do {
5652 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0;
5653
5654 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) {
5655 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW;
5656 goto end2;
5657 }
5658
5659 retlen += padding;
5660
5661 if (out) {
5662 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding;
5663 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset);
5664 } else
5665 out = ffei;
5666
5667 out->NextEntryOffset = 0;
5668 out->Flags = ea->Flags;
5669 out->EaNameLength = ea->EaNameLength;
5670 out->EaValueLength = ea->EaValueLength;
5671 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1);
5672
5673 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength;
5674
5675 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED))
5676 ccb->ea_index++;
5677
5678 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
5679 break;
5680
5681 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5682 } while (true);
5683 }
5684
5685 end2:
5686 ExReleaseResourceLite(fcb->Header.Resource);
5687
5688 end:
5689 TRACE("returning %08lx\n", Status);
5690
5691 Irp->IoStatus.Status = Status;
5692 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0;
5693
5694 IoCompleteRequest( Irp, IO_NO_INCREMENT );
5695
5696 if (top_level)
5697 IoSetTopLevelIrp(NULL);
5698
5699 FsRtlExitFileSystem();
5700
5701 return Status;
5702 }
5703
5704 _Dispatch_type_(IRP_MJ_SET_EA)
5705 _Function_class_(DRIVER_DISPATCH)
5706 NTSTATUS __stdcall drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
5707 device_extension* Vcb = DeviceObject->DeviceExtension;
5708 NTSTATUS Status;
5709 bool top_level;
5710 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5711 PFILE_OBJECT FileObject = IrpSp->FileObject;
5712 fcb* fcb;
5713 ccb* ccb;
5714 file_ref* fileref;
5715 FILE_FULL_EA_INFORMATION* ffei;
5716 ULONG offset;
5717 LIST_ENTRY ealist;
5718 ea_item* item;
5719 FILE_FULL_EA_INFORMATION* ea;
5720 LIST_ENTRY* le;
5721 LARGE_INTEGER time;
5722 BTRFS_TIME now;
5723
5724 FsRtlEnterFileSystem();
5725
5726 TRACE("(%p, %p)\n", DeviceObject, Irp);
5727
5728 top_level = is_top_level(Irp);
5729
5730 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5731 Status = STATUS_INVALID_DEVICE_REQUEST;
5732 goto end;
5733 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
5734 Status = STATUS_INVALID_PARAMETER;
5735 goto end;
5736 }
5737
5738 if (Vcb->readonly) {
5739 Status = STATUS_MEDIA_WRITE_PROTECTED;
5740 goto end;
5741 }
5742
5743 ffei = map_user_buffer(Irp, NormalPagePriority);
5744 if (!ffei) {
5745 ERR("could not get output buffer\n");
5746 Status = STATUS_INVALID_PARAMETER;
5747 goto end;
5748 }
5749
5750 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset);
5751 if (!NT_SUCCESS(Status)) {
5752 ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
5753 goto end;
5754 }
5755
5756 if (!FileObject) {
5757 ERR("no file object\n");
5758 Status = STATUS_INVALID_PARAMETER;
5759 goto end;
5760 }
5761
5762 fcb = FileObject->FsContext;
5763
5764 if (!fcb) {
5765 ERR("no fcb\n");
5766 Status = STATUS_INVALID_PARAMETER;
5767 goto end;
5768 }
5769
5770 if (fcb == fcb->Vcb->volume_fcb) {
5771 Status = STATUS_INVALID_PARAMETER;
5772 goto end;
5773 }
5774
5775 ccb = FileObject->FsContext2;
5776
5777 if (!ccb) {
5778 ERR("no ccb\n");
5779 Status = STATUS_INVALID_PARAMETER;
5780 goto end;
5781 }
5782
5783 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) {
5784 WARN("insufficient privileges\n");
5785 Status = STATUS_ACCESS_DENIED;
5786 goto end;
5787 }
5788
5789 if (fcb->ads) {
5790 fileref = ccb->fileref->parent;
5791 fcb = fileref->fcb;
5792 } else
5793 fileref = ccb->fileref;
5794
5795 InitializeListHead(&ealist);
5796
5797 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
5798
5799 if (fcb->ea_xattr.Length > 0) {
5800 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
5801
5802 do {
5803 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5804 if (!item) {
5805 ERR("out of memory\n");
5806 Status = STATUS_INSUFFICIENT_RESOURCES;
5807 goto end2;
5808 }
5809
5810 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5811 item->name.Buffer = ea->EaName;
5812
5813 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5814 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5815
5816 item->flags = ea->Flags;
5817
5818 InsertTailList(&ealist, &item->list_entry);
5819
5820 if (ea->NextEntryOffset == 0)
5821 break;
5822
5823 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5824 } while (true);
5825 }
5826
5827 ea = ffei;
5828
5829 do {
5830 STRING s;
5831 bool found = false;
5832
5833 s.Length = s.MaximumLength = ea->EaNameLength;
5834 s.Buffer = ea->EaName;
5835
5836 RtlUpperString(&s, &s);
5837
5838 le = ealist.Flink;
5839 while (le != &ealist) {
5840 item = CONTAINING_RECORD(le, ea_item, list_entry);
5841
5842 if (item->name.Length == s.Length &&
5843 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
5844 item->flags = ea->Flags;
5845 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5846 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5847 found = true;
5848 break;
5849 }
5850
5851 le = le->Flink;
5852 }
5853
5854 if (!found) {
5855 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
5856 if (!item) {
5857 ERR("out of memory\n");
5858 Status = STATUS_INSUFFICIENT_RESOURCES;
5859 goto end2;
5860 }
5861
5862 item->name.Length = item->name.MaximumLength = ea->EaNameLength;
5863 item->name.Buffer = ea->EaName;
5864
5865 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
5866 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
5867
5868 item->flags = ea->Flags;
5869
5870 InsertTailList(&ealist, &item->list_entry);
5871 }
5872
5873 if (ea->NextEntryOffset == 0)
5874 break;
5875
5876 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
5877 } while (true);
5878
5879 // remove entries with zero-length value
5880 le = ealist.Flink;
5881 while (le != &ealist) {
5882 LIST_ENTRY* le2 = le->Flink;
5883
5884 item = CONTAINING_RECORD(le, ea_item, list_entry);
5885
5886 if (item->value.Length == 0) {
5887 RemoveEntryList(&item->list_entry);
5888 ExFreePool(item);
5889 }
5890
5891 le = le2;
5892 }
5893
5894 // handle LXSS values
5895 le = ealist.Flink;
5896 while (le != &ealist) {
5897 LIST_ENTRY* le2 = le->Flink;
5898
5899 item = CONTAINING_RECORD(le, ea_item, list_entry);
5900
5901 if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
5902 if (item->value.Length < sizeof(uint32_t)) {
5903 ERR("uid value was shorter than expected\n");
5904 Status = STATUS_INVALID_PARAMETER;
5905 goto end2;
5906 }
5907
5908 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES) {
5909 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t));
5910 fcb->sd_dirty = true;
5911 fcb->sd_deleted = false;
5912 }
5913
5914 RemoveEntryList(&item->list_entry);
5915 ExFreePool(item);
5916 } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
5917 if (item->value.Length < sizeof(uint32_t)) {
5918 ERR("gid value was shorter than expected\n");
5919 Status = STATUS_INVALID_PARAMETER;
5920 goto end2;
5921 }
5922
5923 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES)
5924 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
5925
5926 RemoveEntryList(&item->list_entry);
5927 ExFreePool(item);
5928 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
5929 if (item->value.Length < sizeof(uint32_t)) {
5930 ERR("mode value was shorter than expected\n");
5931 Status = STATUS_INVALID_PARAMETER;
5932 goto end2;
5933 }
5934
5935 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES) {
5936 uint32_t 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;
5937 uint32_t val;
5938
5939 RtlCopyMemory(&val, item->value.Buffer, sizeof(uint32_t));
5940
5941 fcb->inode_item.st_mode &= ~allowed;
5942 fcb->inode_item.st_mode |= val & allowed;
5943 }
5944
5945 RemoveEntryList(&item->list_entry);
5946 ExFreePool(item);
5947 }
5948
5949 le = le2;
5950 }
5951
5952 if (IsListEmpty(&ealist)) {
5953 fcb->ealen = 0;
5954
5955 if (fcb->ea_xattr.Buffer)
5956 ExFreePool(fcb->ea_xattr.Buffer);
5957
5958 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0;
5959 fcb->ea_xattr.Buffer = NULL;
5960 } else {
5961 uint16_t size = 0;
5962 char *buf, *oldbuf;
5963
5964 le = ealist.Flink;
5965 while (le != &ealist) {
5966 item = CONTAINING_RECORD(le, ea_item, list_entry);
5967
5968 if (size % 4 > 0)
5969 size += 4 - (size % 4);
5970
5971 size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
5972
5973 le = le->Flink;
5974 }
5975
5976 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
5977 if (!buf) {
5978 ERR("out of memory\n");
5979 Status = STATUS_INSUFFICIENT_RESOURCES;
5980 goto end2;
5981 }
5982
5983 oldbuf = fcb->ea_xattr.Buffer;
5984
5985 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
5986 fcb->ea_xattr.Buffer = buf;
5987
5988 fcb->ealen = 4;
5989 ea = NULL;
5990
5991 le = ealist.Flink;
5992 while (le != &ealist) {
5993 item = CONTAINING_RECORD(le, ea_item, list_entry);
5994
5995 if (ea) {
5996 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
5997
5998 if (ea->NextEntryOffset % 4 > 0)
5999 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
6000
6001 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
6002 } else
6003 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
6004
6005 ea->NextEntryOffset = 0;
6006 ea->Flags = item->flags;
6007 ea->EaNameLength = (UCHAR)item->name.Length;
6008 ea->EaValueLength = item->value.Length;
6009
6010 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
6011 ea->EaName[item->name.Length] = 0;
6012 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
6013
6014 fcb->ealen += 5 + item->name.Length + item->value.Length;
6015
6016 le = le->Flink;
6017 }
6018
6019 if (oldbuf)
6020 ExFreePool(oldbuf);
6021 }
6022
6023 fcb->ea_changed = true;
6024
6025 KeQuerySystemTime(&time);
6026 win_time_to_unix(time, &now);
6027
6028 fcb->inode_item.transid = Vcb->superblock.generation;
6029 fcb->inode_item.sequence++;
6030
6031 if (!ccb->user_set_change_time)
6032 fcb->inode_item.st_ctime = now;
6033
6034 fcb->inode_item_changed = true;
6035 mark_fcb_dirty(fcb);
6036
6037 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL);
6038
6039 Status = STATUS_SUCCESS;
6040
6041 end2:
6042 ExReleaseResourceLite(fcb->Header.Resource);
6043
6044 while (!IsListEmpty(&ealist)) {
6045 le = RemoveHeadList(&ealist);
6046
6047 item = CONTAINING_RECORD(le, ea_item, list_entry);
6048
6049 ExFreePool(item);
6050 }
6051
6052 end:
6053 TRACE("returning %08lx\n", Status);
6054
6055 Irp->IoStatus.Status = Status;
6056 Irp->IoStatus.Information = 0;
6057
6058 IoCompleteRequest(Irp, IO_NO_INCREMENT);
6059
6060 if (top_level)
6061 IoSetTopLevelIrp(NULL);
6062
6063 FsRtlExitFileSystem();
6064
6065 return Status;
6066 }
6067